diff --git a/ui/src/assets/Rectangle-199.png b/ui/src/assets/Rectangle-199.png new file mode 100644 index 0000000..a8a5525 Binary files /dev/null and b/ui/src/assets/Rectangle-199.png differ diff --git a/ui/src/assets/Rectangle-200.png b/ui/src/assets/Rectangle-200.png new file mode 100644 index 0000000..fc00c0a Binary files /dev/null and b/ui/src/assets/Rectangle-200.png differ diff --git a/ui/src/assets/Rectangle-201.png b/ui/src/assets/Rectangle-201.png new file mode 100644 index 0000000..81a8e7b Binary files /dev/null and b/ui/src/assets/Rectangle-201.png differ diff --git a/ui/src/components/core/icon/custom/index.ts b/ui/src/components/core/icon/custom/index.ts index f113c6a..735c6c0 100644 --- a/ui/src/components/core/icon/custom/index.ts +++ b/ui/src/components/core/icon/custom/index.ts @@ -5,3 +5,4 @@ export * from './fleek-name-icon'; export * from './beta-tag-icon'; export * from './error-icon'; export * from './fleek-logo-icon'; +export * from './opensea-icon'; diff --git a/ui/src/components/core/icon/custom/opensea-icon.tsx b/ui/src/components/core/icon/custom/opensea-icon.tsx new file mode 100644 index 0000000..3a7e7e1 --- /dev/null +++ b/ui/src/components/core/icon/custom/opensea-icon.tsx @@ -0,0 +1,15 @@ +import { IconStyles as IS } from '../icon.styles'; + +export const OpenseaIcon: React.FC = (props) => ( + + + +); diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index b6b32ae..22de597 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -24,6 +24,7 @@ import { FleekLogo, FleekName, MetamaskIcon, + OpenseaIcon, } from './custom'; import { Share } from './custom/share-icon'; @@ -45,6 +46,7 @@ export const IconLibrary = Object.freeze({ info: IoInformationCircleSharp, menu: FaBars, metamask: MetamaskIcon, //remove if not used + opensea: OpenseaIcon, search: BiSearch, square: BsFillSquareFill, share: Share, diff --git a/ui/src/components/core/index.ts b/ui/src/components/core/index.ts index 947be14..eb6efae 100644 --- a/ui/src/components/core/index.ts +++ b/ui/src/components/core/index.ts @@ -7,3 +7,4 @@ export * from './separator.styles'; export * from './text'; export * from './switch'; export * from './color-picker'; +export * from './menu'; diff --git a/ui/src/components/core/menu/index.ts b/ui/src/components/core/menu/index.ts new file mode 100644 index 0000000..d96f145 --- /dev/null +++ b/ui/src/components/core/menu/index.ts @@ -0,0 +1 @@ +export * from './menu'; diff --git a/ui/src/components/core/menu/menu.styles.ts b/ui/src/components/core/menu/menu.styles.ts new file mode 100644 index 0000000..179e2c8 --- /dev/null +++ b/ui/src/components/core/menu/menu.styles.ts @@ -0,0 +1,45 @@ +import { Menu } from '@headlessui/react'; + +import { styled } from '@/theme'; + +export const MenuStyles = { + Wrapper: styled('div', { + position: 'relative', + }), + Items: styled(Menu.Items, { + width: '100%', + display: 'flex', + flexDirection: 'column', + position: 'absolute', + border: '1px solid $slate6', + backgroundColor: '$black', + boxSizing: 'border-box', + left: 0, + right: 0, + top: 'calc(100% + $3)', + padding: '$3', + gap: '$2', + borderRadius: '$lg', + zIndex: '$dropdown', + maxHeight: '30vh', + overflow: 'auto', + }), + Item: styled(Menu.Item, { + width: '100%', + position: 'relative', + display: 'flex', + alignItems: 'center', + gap: '$3', + cursor: 'pointer', + padding: '$2 $3', + borderRadius: '$lg', + color: '$slate11', + transition: '$all-200', + fontSize: '$sm', + + '&[data-headlessui-state*="active"]': { + backgroundColor: '$slate2', + color: '$slate12', + }, + }), +}; diff --git a/ui/src/components/core/menu/menu.tsx b/ui/src/components/core/menu/menu.tsx new file mode 100644 index 0000000..a0753fd --- /dev/null +++ b/ui/src/components/core/menu/menu.tsx @@ -0,0 +1,41 @@ +import { Menu as MenuHeadless } from '@headlessui/react'; +import React from 'react'; + +import { forwardStyledRef } from '@/theme'; + +import { MenuStyles as MS } from './menu.styles'; + +export abstract class Menu { + static readonly Root = ({ children }: Menu.MenuProps): JSX.Element => { + return {children}; + }; + + static readonly Items = forwardStyledRef( + ({ children, ...props }, ref): JSX.Element => { + return ( + + {children.map((child, index) => ( + {child} + ))} + + ); + } + ); + + static readonly Button = MenuHeadless.Button; +} + +export namespace Menu { + export type ItemsProps = { + children: React.ReactNode[]; + } & React.ComponentPropsWithRef; + + export type Elements = { + Button: React.FC>; + Items: React.FC; + }; + + export type MenuProps = { + children: React.ReactNode; + } & React.ComponentPropsWithRef; +} diff --git a/ui/src/constants/env.ts b/ui/src/constants/env.ts index 0ec5d8d..b1b1b98 100644 --- a/ui/src/constants/env.ts +++ b/ui/src/constants/env.ts @@ -1,4 +1,5 @@ export const env = Object.freeze({ + environment: import.meta.env.MODE, alchemy: { id: import.meta.env.VITE_ALCHEMY_API_KEY || '', appName: import.meta.env.VITE_ALCHEMY_APP_NAME || '', diff --git a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx index dd88a83..900d2b3 100644 --- a/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/aside.fragment.tsx @@ -7,18 +7,21 @@ import { Flex, Icon, IconName, + Menu, NFAIcon, NFAPreview, ResolvedAddress, Text, } from '@/components'; +import { env } from '@/constants'; +import { FleekERC721 } from '@/integrations/ethereum/contracts'; import { forwardStyledRef } from '@/theme'; +import { AppLog } from '@/utils'; import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; import { Tab, TabContainer } from './tabs'; -import { AppLog } from '@/utils'; const Preview: React.FC = () => { const { nfa } = IndexedNFA.useContext(); @@ -117,29 +120,85 @@ const NFAInfo: React.FC = () => { type CustomButtonProps = { icon: IconName; -} & React.ComponentPropsWithRef; +}; const CustomButon = forwardStyledRef( ({ icon, ...props }, ref) => ( ) ); +type MenuItemProps = { + label: string; + iconName: IconName; + onClick: () => void; +}; + +const MenuItem: React.FC = ({ + label, + iconName, + onClick, +}: MenuItemProps) => { + return ( + + + {label} + + ); +}; + const ButtonsFragment: React.FC = () => { - const location = window.location.href; + const { nfa } = IndexedNFA.useContext(); + const handleShareOnClick = (): void => { + const location = window.location.href; navigator.clipboard.writeText(location); AppLog.successToast('Link copied to clipboard'); }; + + const handleShareOpenSeaOnClick = (): void => { + window.open( + `https://${ + env.environment === 'development' ? 'testnets' : '' + }.opensea.io/assets/${ + env.environment === 'development' ? 'goerli' : 'ethereum' + }/${FleekERC721.address}/${nfa.tokenId}`, + '_blank' + ); + }; + + const handleShareOnTwitterOnClick = (): void => { + window.open(env.twitter.url, '_blank'); //TODO replace with twitter share + }; + return ( - + + + + + + + + + + + + {/* TODO add tooltip to copy link */} @@ -149,6 +208,7 @@ const ButtonsFragment: React.FC = () => { const PropertiesFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); + //TODO replace with real data const traitsToShow = useMemo(() => { return [ [nfa.ENS, 'ENS'], @@ -234,7 +294,7 @@ export const IndexedNFAAsideFragment: React.FC = () => { const { nfa } = IndexedNFA.useContext(); const { backgroundColor } = App.useContext(); - const background = `linear-gradient(230deg, #${backgroundColor}59 0%, #181818 80%)`; + const background = `linear-gradient(230deg, #${backgroundColor} 0%, #181818 80%)`; useEffect(() => { setTop(ref.current?.getBoundingClientRect().top); diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx index 286273e..9ac9595 100644 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx @@ -1,273 +1,104 @@ -import React, { useMemo } from 'react'; +import React, { useState } from 'react'; -import { Flex, Icon, IconName, ResolvedAddress, Text } from '@/components'; +import Rectangle1 from '@/assets/Rectangle-199.png'; +import Rectangle2 from '@/assets/Rectangle-200.png'; +import Rectangle3 from '@/assets/Rectangle-201.png'; +import { Combobox, Flex, ResolvedAddress, Text } from '@/components'; -import { IndexedNFA } from '../indexed-nfa.context'; import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; -type HeaderDataProps = { +type SortItem = { + value: string; label: string; - children: React.ReactNode; }; -const HeaderData: React.FC = ({ - label, - children, -}: HeaderDataProps) => ( - - {label} - {children} - -); +const orderResults: SortItem[] = [ + { value: 'newest', label: 'Newest' }, + { value: 'oldest', label: 'Oldest' }, +]; const Header: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); + const [selectedValue, setSelectedValue] = useState(orderResults[0]); + const handleSortChange = (item: SortItem | undefined): void => { + //TODO integrate with context and sort + if (item) { + setSelectedValue(item); + } + }; return ( <> - {nfa.name} - - {nfa.owner.id} - - - - - - {/* TODO: place correct data */} - 12/12/22 - - - - - - {nfa.accessPoints?.length ?? 0} - + Hosted NFAs + + {({ Field, Options }) => ( + <> + + {(selected) => selected?.label || 'Select'} + + + {(item) => item.label} + + + )} + - - - ); -}; - -const Description: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - <> - - Description - - - {nfa.description} - - - ); -}; - -type DataWrapperProps = React.PropsWithChildren<{ - label: string | number; -}>; - -const DataWrapper: React.FC = ({ - children, - label, -}: DataWrapperProps) => ( - - {children || '-'} - {label} - -); - -const Traits: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - // TODO: place correct data - const traitsToShow = useMemo(() => { - return [ - [nfa.ENS, 'ENS'], - [nfa.gitRepository.id, 'Repository'], - [10, 'Version'], - [nfa.externalURL, 'Domain'], - [nfa.externalURL, 'Domain 2'], - ]; - }, [nfa]); - - return ( - <> - Traits - - {traitsToShow.map(([value, label]) => ( - - {value} - - ))} - - - ); -}; - -type VerificationBannerProps = { - verified: boolean; -}; - -const VerificationBanner: React.FC = ({ - verified, -}: VerificationBannerProps) => { - const [text, icon] = useMemo<[string, IconName]>(() => { - if (verified) - return ['This Non Fungible Application is Verified.', 'verified']; - return ['This Non Fungible Application is not Verified.', 'error']; - }, [verified]); - - return ( - - {text} - - - ); -}; - -const Verification: React.FC = () => { - return ( - <> - Verification - {/* TODO: Get verified from context */} - 0.5} /> - - {/* TODO: place correct data */} - polygon.eth - polygon/fe - ); }; +const thumbnailMocks = [Rectangle1, Rectangle2, Rectangle3]; // TODO: replace mocks with fetched data -const apMocks = new Array(10).fill(0).map((_, index) => ({ - approved: Math.random() > 0.5, +const apMocks = new Array(20).fill(0).map((_, index) => ({ + thumbnail: + thumbnailMocks[ + Math.floor( + Math.random() * (Math.floor(2) - Math.ceil(0) + 1) + Math.ceil(0) + ) + ], domain: `domain${index}.com`, owner: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049', createdAt: `${Math.floor(Math.random() * 30)}m ago`, })); -const AccessPoints: React.FC = () => { - return ( - <> - Frontends - - - - - - - - - - - - - - - Domain - Owner - Created - - - - - {apMocks.map((item) => ( - - - - - {item.domain} - - {item.owner} - - {item.createdAt} - - - - - ))} - - - - - ); -}; +const AccessPointsListFragment: React.FC = () => { + //TODO add infinite scroll -// TODO: replace mocks with fetched data -const versionsMock = new Array(10).fill(0).map((_, index) => ({ - live: index === 0, - commit: (Math.random() * 0xfffffffff).toString(16), - preview: `test: subgraph matchstick tests for access points and acl refactor (#150 - ) - - * fix: errors from deprecated entities.`, - time: `${Math.floor(Math.random() * 30)}m ago`, -})); - -const Versions: React.FC = () => { return ( - <> - Versions - - - - - - - - - - - - - - - Commit - Preview - Time - - - - - {versionsMock.map((item) => ( - - - - {item.live && 'Live'} - - - {item.commit.slice(0, 6)} - - {item.preview} - - {item.time} - - - - - ))} - - - - + + {apMocks.map((item, index) => ( + + + + + + {item.domain} + + + {item.owner} + + + 220 views + + 2 months ago + + + + ))} + ); }; @@ -275,11 +106,7 @@ export const IndexedNFAMainFragment: React.FC = () => { return (
- - - - - + ); }; diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index 1217ca1..e036515 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -145,8 +145,8 @@ export const IndexedNFAStyles = { flexDirection: 'column', gap: Spacing, }), - Heading: styled('h1', { - fontSize: '2.125rem', + Heading: styled('h2', { + fontSize: '1.625rem', lineHeight: 1.35, fontWeight: 700, }), @@ -156,6 +156,36 @@ export const IndexedNFAStyles = { fontWeight: 700, marginTop: Spacing, }), + AccessPoint: { + List: styled('div', { + display: 'flex', + flexDirection: 'column', + gap: Spacing, + }), + Grid: styled('div', { + display: 'grid', + gridTemplateAreas: '"thumbnail data"', + gap: '$4h', + alignItems: 'center', + gridTemplateColumns: '10rem 1fr', + }), + Thumbnail: styled('div', { + gridArea: 'thumbnail', + }), + Data: { + Container: styled('div', { + gridArea: 'data', + + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + }, + Title: styled('h3', { + color: '$slate12', + fontSize: '$lg', + }), + }, Divider: { Line: styled('span', { width: '100%', diff --git a/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx b/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx index e3ea135..62300fb 100644 --- a/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx +++ b/ui/src/views/mint/github-step/steps/github-repo-configuration/repo-configuration-body/repo-branch-commit-fields.tsx @@ -10,6 +10,7 @@ import { import { AppLog } from '@/utils'; import { Mint } from '@/views/mint/mint.context'; import { useMintFormContext } from '@/views/mint/nfa-step/form-step'; + import { TextStyles } from './repo-branch-commit-fields.styles'; export const RepoBranchCommitFields: React.FC = () => {