diff --git a/ui/graphql/queries.graphql b/ui/graphql/queries.graphql index ff097126..06644e40 100644 --- a/ui/graphql/queries.graphql +++ b/ui/graphql/queries.graphql @@ -22,6 +22,10 @@ query lastNFAsPaginated( accessPoints { id } + owner { + id + } + verified } } @@ -31,41 +35,52 @@ query totalTokens($contractId: ID!) { } } -query getLatestNFAs { - tokens { - id - name - } -} - query getNFADetail($id: ID!) { token(id: $id) { - tokenId - owner { + accessPoints { id } - name description + color + createdAt ENS externalURL + gitRepository { + id + } logo - color - createdAt - accessPoints { - createdAt - contentVerified - owner { - id - } + name + owner { id } verified verifier { id } - gitRepository { + tokenId + } +} + +query getAccessPointsNFA( + $tokenId: String! + $orderBy: AccessPoint_orderBy + $orderDirection: OrderDirection + $pageSize: Int + $skip: Int +) { + accessPoints( + where: { token: $tokenId } + orderDirection: $orderDirection + orderBy: $orderBy + first: $pageSize + skip: $skip + ) { + contentVerified + createdAt + owner { id } + id } } diff --git a/ui/package.json b/ui/package.json index df56d51f..822fea62 100644 --- a/ui/package.json +++ b/ui/package.json @@ -49,7 +49,6 @@ "@types/react": "^18.0.25", "@types/react-dom": "^18.0.9", "@vitejs/plugin-react": "2.2.0", - "autoprefixer": "^10.4.13", "babel-loader": "^8.3.0", "buffer": "^6.0.3", "eslint": "^8.28.0", @@ -59,11 +58,9 @@ "eslint-plugin-react": "^7.31.11", "eslint-plugin-react-hooks": "^4.6.0", "ethers": "^5.7.2", - "postcss": "^8.4.21", "prettier": "^2.8.0", "process": "^0.11.10", "react-query": "^3.39.2", - "tailwindcss": "^3.2.4", "ts-loader": "^9.4.1", "typescript": "^4.9.3", "vite": "^3.2.4", diff --git a/ui/src/app.context.tsx b/ui/src/app.context.tsx new file mode 100644 index 00000000..b84f47ef --- /dev/null +++ b/ui/src/app.context.tsx @@ -0,0 +1,33 @@ +import { useState } from 'react'; + +import { createContext } from './utils'; + +export type AppContext = { + backgroundColor: string; + setBackgroundColor: (color: string) => void; +}; + +const [AppProvider, useContext] = createContext({ + name: 'App.Context', + hookName: 'App.useContext', + providerName: 'App.Provider', +}); + +export abstract class App { + static readonly useContext = useContext; + static readonly Provider: React.FC = ({ children }) => { + const [backgroundColor, setBackgroundColor] = useState(''); + + return ( + + {children} + + ); + }; +} + +export namespace App { + export type AppProps = { + children: React.ReactNode; + }; +} diff --git a/ui/src/app.tsx b/ui/src/app.tsx index 634b2b24..d5a93cd9 100644 --- a/ui/src/app.tsx +++ b/ui/src/app.tsx @@ -2,6 +2,7 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router-dom'; import { themeGlobals } from '@/theme/globals'; +import { App as AppContext } from './app.context'; import { AppPage, ToastProvider } from './components'; import { ComponentsTest, @@ -17,17 +18,19 @@ export const App: React.FC = () => { <> - - - } /> - } /> - } /> - } /> - {/** TODO remove for release */} - } /> - } /> - - + + + + } /> + } /> + } /> + } /> + {/** TODO remove for release */} + } /> + } /> + + + ); diff --git a/ui/src/assets/Rectangle-199.png b/ui/src/assets/Rectangle-199.png new file mode 100644 index 00000000..a8a55255 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 00000000..fc00c0a6 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 00000000..81a8e7b3 Binary files /dev/null and b/ui/src/assets/Rectangle-201.png differ diff --git a/ui/src/components/card/card.styles.ts b/ui/src/components/card/card.styles.ts index 9dc6fe80..13ab99ec 100644 --- a/ui/src/components/card/card.styles.ts +++ b/ui/src/components/card/card.styles.ts @@ -3,6 +3,7 @@ import { styled } from '@/theme'; export abstract class CardStyles { static readonly Container = styled('div', { width: '$full', + height: 'fit-content', backgroundColor: '$slate2', borderRadius: '$xlh', padding: '$7', diff --git a/ui/src/components/core/button/icon-button.tsx b/ui/src/components/core/button/icon-button.tsx index 32ec4206..3129627c 100644 --- a/ui/src/components/core/button/icon-button.tsx +++ b/ui/src/components/core/button/icon-button.tsx @@ -65,7 +65,6 @@ export const IconButton = forwardRef( return props[size as 'sm' | 'md' | 'lg']; }, [size]); - return ( - - + - = (props) => ( + + + +); diff --git a/ui/src/components/core/icon/custom/share-icon.tsx b/ui/src/components/core/icon/custom/share-icon.tsx new file mode 100644 index 00000000..eff3f7e4 --- /dev/null +++ b/ui/src/components/core/icon/custom/share-icon.tsx @@ -0,0 +1,18 @@ +import { IconStyles as IS } from '../icon.styles'; + +export const Share: React.FC = (props) => ( + + + +); diff --git a/ui/src/components/core/icon/icon-library.tsx b/ui/src/components/core/icon/icon-library.tsx index b350ded2..8d95e46b 100644 --- a/ui/src/components/core/icon/icon-library.tsx +++ b/ui/src/components/core/icon/icon-library.tsx @@ -1,16 +1,19 @@ import { AiFillCheckCircle } from '@react-icons/all-files/ai/AiFillCheckCircle'; import { AiOutlineCheck } from '@react-icons/all-files/ai/AiOutlineCheck'; import { AiOutlineTwitter } from '@react-icons/all-files/ai/AiOutlineTwitter'; +import { AiOutlineUnorderedList } from '@react-icons/all-files/ai/AiOutlineUnorderedList'; import { BiGitBranch } from '@react-icons/all-files/bi/BiGitBranch'; import { BiSearch } from '@react-icons/all-files/bi/BiSearch'; import { BsFillSquareFill } from '@react-icons/all-files/bs/BsFillSquareFill'; import { FaBars } from '@react-icons/all-files/fa/FaBars'; import { FaChevronRight } from '@react-icons/all-files/fa/FaChevronRight'; import { FaExternalLinkAlt } from '@react-icons/all-files/fa/FaExternalLinkAlt'; +import { HiOutlineDotsHorizontal } from '@react-icons/all-files/hi/HiOutlineDotsHorizontal'; import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp'; import { IoCheckmarkCircleSharp } from '@react-icons/all-files/io5/IoCheckmarkCircleSharp'; import { IoClose } from '@react-icons/all-files/io5/IoClose'; import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp'; +import { IoGridOutline } from '@react-icons/all-files/io5/IoGridOutline'; import { IoInformationCircleSharp } from '@react-icons/all-files/io5/IoInformationCircleSharp'; import { IoLogoGithub } from '@react-icons/all-files/io5/IoLogoGithub'; import { MdVerifiedUser } from '@react-icons/all-files/md/MdVerifiedUser'; @@ -23,7 +26,9 @@ import { FleekLogo, FleekName, MetamaskIcon, + OpenseaIcon, } from './custom'; +import { Share } from './custom/share-icon'; export const IconLibrary = Object.freeze({ back: IoArrowBackCircleSharp, @@ -40,13 +45,18 @@ export const IconLibrary = Object.freeze({ 'fleek-logo': FleekLogo, 'fleek-name': FleekName, github: IoLogoGithub, + grid: IoGridOutline, info: IoInformationCircleSharp, + list: AiOutlineUnorderedList, menu: FaBars, metamask: MetamaskIcon, //remove if not used + opensea: OpenseaIcon, search: BiSearch, square: BsFillSquareFill, + share: Share, success: AiFillCheckCircle, twitter: AiOutlineTwitter, + 'three-dots': HiOutlineDotsHorizontal, upload: IoCloudUploadSharp, verified: MdVerifiedUser, }); diff --git a/ui/src/components/core/index.ts b/ui/src/components/core/index.ts index 947be14f..eb6efae7 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/input/input-file.styles.ts b/ui/src/components/core/input/input-file.styles.ts index 74270941..68e80b70 100644 --- a/ui/src/components/core/input/input-file.styles.ts +++ b/ui/src/components/core/input/input-file.styles.ts @@ -1,14 +1,13 @@ import { Flex } from '@/components/layout'; import { styled } from '@/theme'; -export abstract class InputFileStyles { - static readonly Container = styled(Flex, { +export const InputFileStyles = { + Container: styled(Flex, { alignItems: 'center', justifyContent: 'center', cursor: 'pointer', - }); - - static readonly Border = styled('div', { + }), + Border: styled('div', { borderStyle: 'solid', borderColor: '$gray7', width: '$22', @@ -24,5 +23,14 @@ export abstract class InputFileStyles { '&[aria-invalid=true], &[data-invalid]': { borderColor: '$red9', }, - }); -} + }), + Image: styled('img', { + position: 'absolute', + width: '3.5rem', + height: '3.5rem', + }), + Input: styled('input', { + all: 'unset', + display: 'none', + }), +}; diff --git a/ui/src/components/core/input/input-file.tsx b/ui/src/components/core/input/input-file.tsx index 456e68c7..0d23589a 100644 --- a/ui/src/components/core/input/input-file.tsx +++ b/ui/src/components/core/input/input-file.tsx @@ -25,14 +25,13 @@ export const StyledInputFile = forwardRef( <> {file !== '' ? ( - logo + ) : ( )} - { + 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/components/core/switch/switch.styles.ts b/ui/src/components/core/switch/switch.styles.ts new file mode 100644 index 00000000..9d2025fd --- /dev/null +++ b/ui/src/components/core/switch/switch.styles.ts @@ -0,0 +1,84 @@ +import { Switch } from '@headlessui/react'; + +import { styled } from '@/theme'; + +export const SwitchStyles = { + Wrapper: styled(Switch, { + position: 'relative', + display: 'inline-flex', + height: '2rem', + width: '4.625rem', + flexShrink: 0, + cursor: 'pointer', + borderRadius: '$full', + borderWidth: '0.125rem', + borderColor: 'transparent', + + transitionProperty: 'all', + + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '200ms', + + '&:focus': { + outline: '0.125rem solid transparent', + outlineOffset: '0.125rem', + }, + + variants: { + isChecked: { + true: { + backgroundColor: '$green4', + }, + false: { + backgroundColor: '$red4', + }, + }, + }, + }), + Text: styled('span', { + position: 'absolute', + top: '25%', + fontSize: '$sm', + + variants: { + checked: { + true: { + right: '0.75rem', + color: '$green11', + }, + false: { + left: '1rem', + color: '$red11', + }, + }, + }, + }), + Dot: styled('span', { + position: 'absolute', + top: '5px', + left: '5px', + pointerEvents: 'none', + display: 'inline-block', + height: '1.25rem', + width: '1.25rem', + borderRadius: '$full', + + transitionProperty: 'all', + + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '200ms', + + variants: { + checked: { + true: { + backgroundColor: '$green11', + transform: 'translateX(0px)', + }, + false: { + backgroundColor: '$red11', + transform: 'translateX(2.625rem)', + }, + }, + }, + }), +}; diff --git a/ui/src/components/core/switch/switch.tsx b/ui/src/components/core/switch/switch.tsx index 41a15e4d..180d2758 100644 --- a/ui/src/components/core/switch/switch.tsx +++ b/ui/src/components/core/switch/switch.tsx @@ -1,31 +1,21 @@ -import { Switch as SwitchComponent } from '@headlessui/react'; +import { Switch as SwitchHeadless } from '@headlessui/react'; import React from 'react'; +import { SwitchStyles as S } from './switch.styles'; + type SwitchProps = { checked: boolean; onChange: (checked: boolean) => void; }; export const Switch: React.FC = ({ checked, onChange }) => ( - - - {checked ? 'Yes' : 'No'} - - + {checked ? 'Yes' : 'No'} + + ); diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index 62f959e3..351ed16c 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -6,6 +6,7 @@ export * from './spinner'; export * from './toast'; export * from './step'; export * from './nfa-card'; +export * from './nfa-icon'; export * from './nfa-preview'; export * from './card-tag'; export * from './resolved-address'; diff --git a/ui/src/components/layout/nav-bar/nav-bar.styles.ts b/ui/src/components/layout/nav-bar/nav-bar.styles.ts index aeb838b3..08266c3b 100644 --- a/ui/src/components/layout/nav-bar/nav-bar.styles.ts +++ b/ui/src/components/layout/nav-bar/nav-bar.styles.ts @@ -19,7 +19,6 @@ export const NavBarStyles = { content: '""', position: 'absolute', inset: 0, - backgroundColor: alphaColor('black', 0.8), backdropFilter: 'blur(4px)', zIndex: -1, }, diff --git a/ui/src/components/layout/nav-bar/nav-bar.tsx b/ui/src/components/layout/nav-bar/nav-bar.tsx index efbc1c19..b01ab998 100644 --- a/ui/src/components/layout/nav-bar/nav-bar.tsx +++ b/ui/src/components/layout/nav-bar/nav-bar.tsx @@ -1,8 +1,8 @@ import { useMediaQuery } from '@/hooks'; -import { ConnectWalletButton } from './connect-wallet-button'; import { Logo } from './logo'; import { NavBarStyles as Styles } from './nav-bar.styles'; +import { NavBarConnectWalletButton } from './navbar-connect-wallet-button'; import { Navigation } from './navigation'; import { Sidebar } from './sidebar'; @@ -13,7 +13,7 @@ export const NavBar: React.FC = () => { - + {enableSidebar ? : } diff --git a/ui/src/components/layout/nav-bar/connect-wallet-button.tsx b/ui/src/components/layout/nav-bar/navbar-connect-wallet-button.tsx similarity index 71% rename from ui/src/components/layout/nav-bar/connect-wallet-button.tsx rename to ui/src/components/layout/nav-bar/navbar-connect-wallet-button.tsx index 7a12e553..166b4db7 100644 --- a/ui/src/components/layout/nav-bar/connect-wallet-button.tsx +++ b/ui/src/components/layout/nav-bar/navbar-connect-wallet-button.tsx @@ -1,15 +1,24 @@ import { Avatar, ConnectKitButton } from 'connectkit'; +import { useEffect } from 'react'; +import { useAccount, useEnsName } from 'wagmi'; import { Button, Flex } from '@/components'; import { ENSActions, useAppDispatch, useENSStore } from '@/store'; -export const ConnectWalletButton: React.FC = () => { +export const NavBarConnectWalletButton: React.FC = () => { const { addressMap } = useENSStore(); + const { address } = useAccount(); + const { data: ensName } = useEnsName({ + address, + }); const dispatch = useAppDispatch(); - const setEnsNameStore = (ensName: string, address: string): void => { + useEffect(() => { + if (address === undefined) return; + const stored = addressMap[address] || {}; if (typeof stored.state !== 'undefined') return; + if (ensName === null) return; dispatch( ENSActions.setAddress({ @@ -17,13 +26,11 @@ export const ConnectWalletButton: React.FC = () => { value: { state: 'success', value: ensName }, }) ); - }; + }, [address, addressMap, dispatch, ensName]); return ( {({ isConnected, show, truncatedAddress, address, ensName }) => { - if (ensName && address) setEnsNameStore(ensName, address); - return ( - - {/* TODO: place correct href */} - Learn more - - - - - ); -}; - -export const IndexedNFAAsideFragment: React.FC = () => { - const ref = useRef(null); - const [top, setTop] = useState(); - - useEffect(() => { - setTop(ref.current?.getBoundingClientRect().top); - }, [ref]); - - return ( - - - - - ); -}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx new file mode 100644 index 00000000..28867f95 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-buttons.fragment.tsx @@ -0,0 +1,89 @@ +import { Button, Flex, Icon, IconName, Menu } from '@/components'; +import { env } from '@/constants'; +import { FleekERC721 } from '@/integrations/ethereum/contracts'; +import { forwardStyledRef } from '@/theme'; +import { AppLog } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +const openseaLink = `https://${ + env.environment === 'development' ? 'testnets' : '' +}.opensea.io/assets/${ + env.environment === 'development' ? 'goerli' : 'ethereum' +}/${FleekERC721.address}`; + +type CustomButtonProps = { + icon: IconName; +}; + +const CustomButon = forwardStyledRef( + ({ icon, ...props }, ref) => ( + + ) +); + +type MenuItemProps = { + label: string; + iconName: IconName; + href: string; +}; + +const MenuItem: React.FC = ({ + label, + iconName, + href, +}: MenuItemProps) => { + return ( + + + + {label} + + + ); +}; + +export const ButtonsFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const handleShareOnClick = (): void => { + const location = window.location.href; + navigator.clipboard.writeText(location); + AppLog.successToast('Link copied to clipboard'); + }; + + return ( + + + + + {/* TODO remove span and render as fragment */} + + + + + + + + + + {/* TODO add tooltip to copy link */} + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx new file mode 100644 index 00000000..85ece318 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-header.fragment.tsx @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; + +import { Flex, Icon, NFAIcon, ResolvedAddress } from '@/components'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type BadgeProps = { + verified: boolean; +}; + +const Badge: React.FC = ({ verified }: BadgeProps) => { + const text = useMemo( + () => (verified ? 'Verified' : 'Unverified'), + [verified] + ); + + const icon = useMemo(() => (verified ? 'verified' : 'error'), [verified]); + const color = useMemo(() => (verified ? '$green10' : '$red10'), [verified]); + return ( + + + {text} + + ); +}; + +export const Header: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + {nfa.name} + + + + + + {nfa.owner.id} + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx new file mode 100644 index 00000000..0759cc67 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-nfa-info.fragment.tsx @@ -0,0 +1,35 @@ +import { Flex, Text } from '@/components'; +import { getDate } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type HeaderDataProps = { + label: string; + children: React.ReactNode; +}; + +const HeaderData: React.FC = ({ + label, + children, +}: HeaderDataProps) => ( + + {label} + {children} + +); + +export const NFAInfo: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + return ( + + + {nfa.accessPoints?.length ?? 0} + + + + + {getDate(nfa.createdAt)} + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx new file mode 100644 index 00000000..8facad7e --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-preview.fragment.tsx @@ -0,0 +1,29 @@ +import { useMemo } from 'react'; + +import { NFAPreview } from '@/components'; +import { parseNumberToHexColor } from '@/utils/color'; + +import { IndexedNFA } from '../../indexed-nfa.context'; + +export const Preview: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const color = useMemo( + () => `#${parseNumberToHexColor(nfa.color ?? '')}`, + [nfa] + ); + + return ( + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx new file mode 100644 index 00000000..87d0b000 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside-tabs.fragment.tsx @@ -0,0 +1,92 @@ +import { useMemo, useState } from 'react'; + +import { Flex } from '@/components'; +import { getRepositoryFromURL } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { Tab, TabContainer } from '../../tabs'; + +const OverviewFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + return ( + + + Token ID + {nfa.tokenId} + + + + Network + Mainnet + + + + Standard + ERC_721 + + + + Description + + + {nfa.description} + + + ); +}; + +const PropertiesFragment: React.FC = () => { + const { nfa } = IndexedNFA.useContext(); + + const traitsToShow = useMemo(() => { + return [ + [nfa.ENS, 'ENS'], + [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], + [nfa.externalURL, 'Domain'], + ]; + }, [nfa]); + + return ( + + {traitsToShow.map(([value, label], index) => ( + + + {value || '-'} + + {label} + + ))} + + ); +}; + +export const TabFragment: React.FC = () => { + const [tabSelected, setTabSelected] = useState(0); + const handleClick = (index: number): void => { + setTabSelected(index); + }; + + return ( + <> + + {['Overview', 'Properties'].map((label, index) => ( + + ))} + + {tabSelected === 0 ? : } + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx b/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx new file mode 100644 index 00000000..ae4f7660 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/aside/aside.fragment.tsx @@ -0,0 +1,48 @@ +import { useEffect, useRef, useState } from 'react'; +import { Link } from 'react-router-dom'; + +import { App } from '@/app.context'; +import { Button } from '@/components'; +import { parseNumberToHexColor } from '@/utils/color'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { ButtonsFragment } from './aside-buttons.fragment'; +import { Header } from './aside-header.fragment'; +import { NFAInfo } from './aside-nfa-info.fragment'; +import { Preview } from './aside-preview.fragment'; +import { TabFragment } from './aside-tabs.fragment'; + +export const IndexedNFAAsideFragment: React.FC = () => { + const ref = useRef(null); + const [top, setTop] = useState(); + const { nfa } = IndexedNFA.useContext(); + + const { backgroundColor } = App.useContext(); + const background = `radial-gradient(closest-corner circle at 90% 45%, #${backgroundColor}8c 1% ,#${backgroundColor}57 20%, transparent 40%), radial-gradient(closest-corner circle at 60% 25%, #${backgroundColor} 3%, #${backgroundColor}73 30%, #181818 70%)`; + + useEffect(() => { + setTop(ref.current?.getBoundingClientRect().top); + }, [ref]); + + return ( + + +
+ + + + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/index.ts b/ui/src/views/indexed-nfa/fragments/index.ts index d90e1fb9..e31d0878 100644 --- a/ui/src/views/indexed-nfa/fragments/index.ts +++ b/ui/src/views/indexed-nfa/fragments/index.ts @@ -1,3 +1,3 @@ -export * from './aside.fragment'; -export * from './main.fragment'; +export * from './aside/aside.fragment'; +export * from './main/main.fragment'; export * from './skeleton.fragment'; diff --git a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main.fragment.tsx deleted file mode 100644 index ba08ed2a..00000000 --- a/ui/src/views/indexed-nfa/fragments/main.fragment.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import React, { useMemo } from 'react'; - -import { Flex, Icon, IconName, ResolvedAddress, Text } from '@/components'; -import { getDate, getRepositoryFromURL, getTimeSince } from '@/utils'; - -import { IndexedNFA } from '../indexed-nfa.context'; -import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; - -type HeaderDataProps = { - label: string; - children: React.ReactNode; -}; - -const HeaderData: React.FC = ({ - label, - children, -}: HeaderDataProps) => ( - - {label} - {children} - -); - -const Header: React.FC = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - <> - {nfa.name} - - - {nfa.owner.id} - - - - - {getDate(nfa.createdAt)} - - - - - {nfa.accessPoints?.length ?? 0} - - - - - ); -}; - -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(); - - const traitsToShow = useMemo(() => { - return [ - [nfa.ENS, 'ENS'], - [getRepositoryFromURL(nfa.gitRepository.id), 'Repository'], - ['', 'Version'], - [nfa.externalURL, 'Domain'], - ]; - }, [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 = () => { - const { nfa } = IndexedNFA.useContext(); - - return ( - <> - Verification - - - - {nfa.verifier ? ( - {nfa.verifier?.id} - ) : ( - '-' - )} - - - {getRepositoryFromURL(nfa.gitRepository.id)} - - - - ); -}; - -const AccessPoints: React.FC = () => { - const { - nfa: { accessPoints }, - } = IndexedNFA.useContext(); - - return ( - <> - Frontends - - - - - - - - - - - - - - - Domain - Owner - Created - - - - - {accessPoints && accessPoints.length > 0 ? ( - accessPoints.map((item) => ( - - - - - {item.id} - - {item.owner.id} - - - {getTimeSince(item.createdAt)} - - - - - - )) - ) : ( - - - No results - - - )} - - - - - ); -}; - -export const IndexedNFAMainFragment: React.FC = () => { - return ( - -
- - - - - - ); -}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx new file mode 100644 index 00000000..5228b8b5 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main-ap-list.fragment.tsx @@ -0,0 +1,118 @@ +import { useQuery } from '@apollo/client'; +import { ethers } from 'ethers'; +import { useEffect } from 'react'; + +import Rectangle1 from '@/assets/Rectangle-199.png'; +import { Flex, ResolvedAddress, Text } from '@/components'; +import { + AccessPoint as AccessPointType, + getAccessPointsNFADocument, + Owner, +} from '@/graphclient'; +import { useWindowScrollEnd } from '@/hooks'; +import { AppLog, getTimeSince } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { SkeletonAccessPointsListFragment } from './skeleton.ap-list'; + +type AccessPointProps = { + data: Pick & { + owner: Pick; + }; +}; + +const AccessPoint: React.FC = ({ + data, +}: AccessPointProps) => { + const { id: name, owner, createdAt } = data; + return ( + + + {/* TODO remove for real image */} + + + + {name} + + + {owner.id} + + + {/* TODO get from bunny CDN */} + 220 views + + {getTimeSince(createdAt)} + + + + ); +}; + +const pageSize = 10; //Set this size to test pagination + +export const AccessPointsListFragment: React.FC = () => { + const { + nfa: { tokenId }, + orderDirection, + pageNumber, + endReached, + setEndReached, + setPageNumber, + } = IndexedNFA.useContext(); + + const handleError = (error: unknown): void => { + AppLog.errorToast( + 'There was an error trying to get the access points', + error + ); + }; + + const { + loading: isLoading, + data: { accessPoints } = { accessPoints: [] }, + error: queryError, + } = useQuery(getAccessPointsNFADocument, { + skip: tokenId === undefined, + fetchPolicy: 'cache-and-network', + variables: { + tokenId: ethers.utils.hexlify(Number(tokenId)), + orderDirection: orderDirection, + orderBy: 'createdAt', + pageSize, + skip: pageNumber * pageSize, //skip is for the pagination + }, + onCompleted(data) { + if (data.accessPoints.length - accessPoints.length < pageSize) + setEndReached(true); + }, + onError(error) { + handleError(error); + }, + }); + + useEffect(() => { + // Update page number when there are cached tokens + setPageNumber(Math.ceil(accessPoints.length / pageSize)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useWindowScrollEnd(() => { + if (isLoading || endReached || queryError) return; + setPageNumber(pageNumber + 1); + }); + + return ( + + {accessPoints.map((item, index) => ( + + ))} + {isLoading && } + {!isLoading && accessPoints.length === 0 && ( + +

No hosted NFAs

+
+ )} +
+ ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx new file mode 100644 index 00000000..feb44a87 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main-header.fragment.tsx @@ -0,0 +1,66 @@ +import { useState } from 'react'; + +import { OrderDirection } from '@/../.graphclient'; +import { Combobox, Flex } from '@/components'; +import { AppLog } from '@/utils'; + +import { IndexedNFA } from '../../indexed-nfa.context'; +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +type SortItem = { + value: OrderDirection; + label: string; +}; + +const orderResults: SortItem[] = [ + { value: 'desc', label: 'Newest' }, + { value: 'asc', label: 'Oldest' }, +]; + +export const Header: React.FC = () => { + const { setPageNumber, setOrderDirection, setEndReached } = + IndexedNFA.useContext(); + const [selectedValue, setSelectedValue] = useState(orderResults[0]); + + const handleSortChange = (item: SortItem | undefined): void => { + if (item) { + setSelectedValue(item); + setPageNumber(0); + setEndReached(false); + setOrderDirection(item.value); + } else { + AppLog.errorToast('Error selecting sort option. Try again'); + } + }; + + return ( + <> + + Hosted NFAs + + {({ Field, Options }) => ( + <> + + {(selected) => selected?.label || 'Select'} + + + {(item) => item.label} + + + )} + + + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx b/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx new file mode 100644 index 00000000..2cdb4bc2 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/main.fragment.tsx @@ -0,0 +1,12 @@ +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; +import { AccessPointsListFragment } from './main-ap-list.fragment'; +import { Header } from './main-header.fragment'; + +export const IndexedNFAMainFragment: React.FC = () => { + return ( + +
+ + + ); +}; diff --git a/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx b/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx new file mode 100644 index 00000000..ce64a830 --- /dev/null +++ b/ui/src/views/indexed-nfa/fragments/main/skeleton.ap-list.tsx @@ -0,0 +1,21 @@ +import { IndexedNFAStyles as S } from '../../indexed-nfa.styles'; + +const SkeletonAccessPoint: React.FC = () => ( + + + + + + + + + +); + +export const SkeletonAccessPointsListFragment: React.FC = () => ( + + + + + +); diff --git a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx index e1e05a45..6b3ed41c 100644 --- a/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx +++ b/ui/src/views/indexed-nfa/fragments/skeleton.fragment.tsx @@ -1,16 +1,14 @@ import { IndexedNFAStyles as S } from '../indexed-nfa.styles'; +import { SkeletonAccessPointsListFragment } from './main/skeleton.ap-list'; export const IndexedNFASkeletonFragment: React.FC = () => ( - - - - + + - - - - + ); diff --git a/ui/src/views/indexed-nfa/indexed-nfa.context.tsx b/ui/src/views/indexed-nfa/indexed-nfa.context.tsx index fc99dac2..ef044b90 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.context.tsx +++ b/ui/src/views/indexed-nfa/indexed-nfa.context.tsx @@ -1,4 +1,6 @@ -import { Owner, Token } from '@/graphclient'; +import { useState } from 'react'; + +import { OrderDirection, Owner, Token } from '@/graphclient'; import { createContext } from '@/utils'; const [Provider, useContext] = createContext({ @@ -10,7 +12,21 @@ const [Provider, useContext] = createContext({ export const IndexedNFA = { useContext, Provider: ({ children, nfa }: IndexedNFA.ProviderProps): JSX.Element => { - return {children}; + const [orderDirection, setOrderDirection] = + useState('desc'); + const [pageNumber, setPageNumber] = useState(0); + const [endReached, setEndReached] = useState(false); + + const context = { + nfa, + orderDirection, + pageNumber, + endReached, + setOrderDirection, + setPageNumber, + setEndReached, + }; + return {children}; }, }; @@ -19,6 +35,12 @@ export namespace IndexedNFA { nfa: Omit & { owner: Pick; }; + orderDirection: OrderDirection; + pageNumber: number; + endReached: boolean; + setOrderDirection: (orderDirection: OrderDirection) => void; + setPageNumber: (pageNumber: number) => void; + setEndReached: (isEndReaced: boolean) => void; }; export type ProviderProps = { diff --git a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts index 1e1b2c4c..feb521f5 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.styles.ts +++ b/ui/src/views/indexed-nfa/indexed-nfa.styles.ts @@ -1,7 +1,7 @@ -import { Skeleton } from '@/components'; +import { Button, Flex, Skeleton, Text } from '@/components'; import { styled } from '@/theme'; -const Spacing = '$5'; +const Spacing = '$6'; export const IndexedNFAStyles = { Grid: styled('div', { @@ -16,9 +16,11 @@ export const IndexedNFAStyles = { gridTemplateColumns: '20rem 1fr', }, - '@media (max-width: 580px)': { + '@media (max-width: 640px)': { gridTemplateAreas: '"aside" "main"', gridTemplateColumns: '1fr', + justifyItems: 'center', + padding: '0', }, }), @@ -32,34 +34,110 @@ export const IndexedNFAStyles = { gap: Spacing, height: 'fit-content', - '@media (max-width: 580px)': { + borderRadius: '$lg', + padding: Spacing, + maxWidth: '24rem', + mixBlendMode: 'screen', + + '@media (max-width: 640px)': { position: 'static', }, }), + Header: { + Wrapper: styled(Flex, { + flexDirection: 'column', + gap: '$2h', + color: '$slate12', + }), + Container: styled(Flex, { + justifyContent: 'space-between', + alignItems: 'center', + }), + Header: styled('h1', { + fontSize: '2.125rem', + lineHeight: 1.35, + fontWeight: 700, + + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }), + Badge: styled('span', { + height: 'fit-content', + width: 'fit-content', + fontSize: '$xs', + fontWeight: '$bold', + padding: '$0h $2', + borderRadius: '$full', + backgroundColor: '#131313', + display: 'flex', + gap: '$1h', + + variants: { + verified: { + true: { + color: '$green10', + }, + false: { + color: '$red10', + }, + }, + }, + }), + }, + Divider: { + Line: styled('span', { + width: '100%', + borderBottom: '1px solid $slate6', + }), + Elipse: styled('span', { + width: '0.375rem', + height: '0.375rem', + backgroundColor: '$slate8', + borderRadius: '100%', + }), + }, + Button: { + Container: styled(Flex, { + gap: '$3', + fontSize: '16px', - CreateAccessPoint: { + [`${Button}`]: { + borderRadius: '0.375rem', + }, + }), + }, + Overview: { Container: styled('div', { display: 'flex', flexDirection: 'column', - gap: Spacing, - padding: Spacing, - backgroundColor: '$blue1', + backgroundColor: 'rgba(255, 255, 255, 0.06)', borderRadius: '$lg', + fontSize: '14px', }), - Heading: styled('h2', { - fontSize: '$md', - color: '$slate12', - }), - Text: styled('p', { - fontSize: '$sm', + Row: { + Container: styled(Flex, { + justifyContent: 'space-between', + }), + Label: styled(Text, { + color: '$slate11', + }), + Value: styled(Text, { + fontWeight: '$bold', + }), + }, + Description: styled('p', { color: '$slate11', + overflowY: 'scroll', + pr: '1rem', }), - Extra: styled('a', { + }, + Properties: { + Container: styled('div', { display: 'flex', - alignItems: 'center', - color: '$slate11', - fontSize: '$sm', - gap: '$2', + flexDirection: 'column', + gap: '$1', + padding: '$2h $4', }), }, }, @@ -70,167 +148,59 @@ export const IndexedNFAStyles = { display: 'flex', flexDirection: 'column', gap: Spacing, + width: '100%', }), - Heading: styled('h1', { - fontSize: '2.125rem', + Heading: styled('h2', { + fontSize: '1.625rem', lineHeight: 1.35, fontWeight: 700, }), - SectionHeading: styled('h2', { - fontSize: '$xl', - lineHeight: 1.2, - fontWeight: 700, - marginTop: Spacing, - }), - Divider: { - Line: styled('span', { - width: '100%', - borderBottom: '1px solid $slate6', + AccessPoint: { + List: styled('div', { + display: 'flex', + flexDirection: 'column', + gap: Spacing, }), - Elipse: styled('span', { - width: '0.375rem', - height: '0.375rem', - backgroundColor: '$slate4', - borderRadius: '100%', + Grid: styled('div', { + display: 'grid', + gridTemplateAreas: '"thumbnail data"', + gap: '$4h', + alignItems: 'center', + gridTemplateColumns: '7rem 1fr', }), - }, - Paragraph: styled('p', { - color: '$slate11', - lineHeight: 1.43, - }), - DataContainer: styled('div', { - display: 'flex', - flexDirection: 'column', - border: '1px solid $slate6', - borderRadius: '$lg', - padding: Spacing, - gap: `$1`, - }), - DataList: styled('div', { - display: 'flex', - flexWrap: 'wrap', - gap: '$5', - }), - VerificationBanner: styled('div', { - position: 'relative', - display: 'flex', - alignItems: 'center', - border: '1px solid $slate6', - borderRadius: '$lg', - padding: '$8 $5', - fontWeight: 700, - overflow: 'hidden', - - '&:after': { - content: '""', - position: 'absolute', - right: '-$5', - top: '-$10', - bottom: '-$10', - left: '84%', - borderRadius: '80% 0 0 80%', - }, - - variants: { - verified: { - true: { - borderColor: '$green11', - color: '$green11', - '&:after': { - backgroundColor: '$green11', - }, - }, - false: { - borderColor: '$red11', - color: '$red11', - '&:after': { - backgroundColor: '$red11', - }, - }, - }, - }, - }), - - Table: { - Container: styled('div', { - border: '1px solid $slate6', - borderRadius: '10px', - padding: '0 $5', - - maxHeight: '15.125rem', - overflow: 'auto', + Thumbnail: styled('div', { + gridArea: 'thumbnail', }), - Root: styled('table', { - width: 'calc(100% + 2 * $space$5)', - margin: '0 -$5', + Data: { + Container: styled('div', { + gridArea: 'data', + + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + }, + Title: styled('h3', { + color: '$slate12', + fontSize: '$lg', }), - Head: styled('thead', { - position: 'sticky', - top: 0, - backgroundColor: '$black', + NoResults: styled('div', { + display: 'flex', + justifyContent: 'center', - '&:after': { - position: 'absolute', - content: '""', - bottom: 0, - left: 0, - right: 0, - borderBottom: '1px solid $slate6', - }, - }), - Row: styled('tr'), - Data: styled('td', { - padding: '$3', - maxWidth: '10rem', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', + fontSize: '$lg', }), - Body: styled('tbody', { - tr: { - '&:hover': { - backgroundColor: '$slate6', - cursor: 'pointer', - }, - }, + }, + Divider: { + Line: styled('span', { + width: '100%', + borderBottom: '1px solid $slate6', }), - Marker: styled('span', { - display: 'block', - margin: 'auto', - width: '0.5625rem', - height: '0.5625rem', - borderRadius: '$full', - backgroundColor: '$slate6', - - variants: { - variant: { - active: { - backgroundColor: '$green11', - }, - inactive: { - backgroundColor: '$slate8', - }, - }, - text: { - true: { - fontSize: '$xs', - padding: '0 $2', - width: 'fit-content', - height: 'fit-content', - }, - }, - }, - - compoundVariants: [ - { - variant: 'active', - text: true, - css: { - color: '$green11', - backgroundColor: '$green3', - }, - }, - ], + Elipse: styled('span', { + minWidth: '0.375rem', + minHeight: '0.375rem', + backgroundColor: '$slate11', + borderRadius: '100%', }), }, }, diff --git a/ui/src/views/indexed-nfa/indexed-nfa.tsx b/ui/src/views/indexed-nfa/indexed-nfa.tsx index 232909a0..51fdd492 100644 --- a/ui/src/views/indexed-nfa/indexed-nfa.tsx +++ b/ui/src/views/indexed-nfa/indexed-nfa.tsx @@ -1,9 +1,12 @@ import { useQuery } from '@apollo/client'; import { ethers } from 'ethers'; +import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; +import { App } from '@/app.context'; import { getNFADetailDocument } from '@/graphclient'; import { AppLog } from '@/utils'; +import { parseNumberToHexColor } from '@/utils/color'; import { IndexedNFAAsideFragment, @@ -15,8 +18,15 @@ import { IndexedNFAStyles as S } from './indexed-nfa.styles'; export const IndexedNFAView: React.FC = () => { const { id } = useParams<{ id: string }>(); + const { setBackgroundColor } = App.useContext(); const navigate = useNavigate(); + useEffect(() => { + return () => { + setBackgroundColor('000000'); + }; + }, [setBackgroundColor]); + const handleError = (error: unknown): void => { AppLog.errorToast( `It was not possible to find the NFA with id "${id}"`, @@ -32,6 +42,8 @@ export const IndexedNFAView: React.FC = () => { }, onCompleted(data) { if (!data.token) handleError(new Error('Token not found')); + if (data.token?.color) + setBackgroundColor(parseNumberToHexColor(data.token.color)); }, onError(error) { handleError(error); @@ -42,11 +54,6 @@ export const IndexedNFAView: React.FC = () => { return ; } - if (!data.token) { - //TODO add 404 page - return
Token not found
; - } - return ( diff --git a/ui/src/views/indexed-nfa/tabs/index.ts b/ui/src/views/indexed-nfa/tabs/index.ts new file mode 100644 index 00000000..6dd78564 --- /dev/null +++ b/ui/src/views/indexed-nfa/tabs/index.ts @@ -0,0 +1 @@ +export * from './tabs'; diff --git a/ui/src/views/indexed-nfa/tabs/tabs.styles.ts b/ui/src/views/indexed-nfa/tabs/tabs.styles.ts new file mode 100644 index 00000000..132343ef --- /dev/null +++ b/ui/src/views/indexed-nfa/tabs/tabs.styles.ts @@ -0,0 +1,48 @@ +import { Flex } from '@/components'; +import { styled } from '@/theme'; + +export const TabsStyles = { + Container: styled(Flex, { + width: '100%', + }), + Tab: { + Container: styled(Flex, { + flexDirection: 'column', + flex: 1, + alignItems: 'center', + cursor: 'pointer', + + variants: { + active: { + true: { + color: 'white', + }, + false: { + color: '$slate8', + }, + }, + }, + }), + Label: styled('span', { + padding: '$2h', + }), + Line: styled('span', { + width: '100%', + borderRadius: '3px', + + variants: { + active: { + true: { + color: 'white', + borderBottom: '3px solid white', + }, + false: { + color: '$slate8', + borderBottom: '2px solid $slate8', + mt: '0.046875rem', + }, + }, + }, + }), + }, +}; diff --git a/ui/src/views/indexed-nfa/tabs/tabs.tsx b/ui/src/views/indexed-nfa/tabs/tabs.tsx new file mode 100644 index 00000000..b83b6526 --- /dev/null +++ b/ui/src/views/indexed-nfa/tabs/tabs.tsx @@ -0,0 +1,35 @@ +import { forwardStyledRef } from '@/theme'; + +import { TabsStyles as S } from './tabs.styles'; + +type TabProps = { + label: string; + index: number; + onTabClick: (index: number) => void; +} & React.ComponentPropsWithRef; + +export const Tab = forwardStyledRef( + ({ label, index, onTabClick, ...props }, ref) => { + const { active } = props; + const handleClick = (): void => { + onTabClick(index); + }; + + return ( + + {label} + + + ); + } +); + +type TabContainerProps = { + children: React.ReactNode; +}; + +export const TabContainer: React.FC = ({ + children, +}: TabContainerProps) => { + return {children}; +}; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx index 841aa886..eb0b6604 100644 --- a/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/github-repository-selection.tsx @@ -6,7 +6,7 @@ import { useGithubStore } from '@/store'; import { Mint } from '@/views/mint/mint.context'; import { GithubRepositorySelectionStyles as S } from './github-repository-selection.styles'; -import { RepositoriesList } from './repositories-list'; +import { RepositoriesListFragment } from './repositories-list'; import { UserOrgsCombobox } from './users-orgs-combobox'; export const Loading: React.FC = () => ( @@ -68,7 +68,7 @@ export const GithubRepositoryConnection: React.FC = () => { queryUserAndOrganizations === 'loading' ? ( ) : ( - + )} diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts new file mode 100644 index 00000000..b57446cd --- /dev/null +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/index.ts @@ -0,0 +1 @@ +export * from './repositories-list.fragment'; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx similarity index 72% rename from ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx rename to ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx index de11b53c..7b01e6da 100644 --- a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list.tsx +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.fragment.tsx @@ -1,16 +1,16 @@ import { useEffect, useMemo } from 'react'; -import { Flex } from '@/components'; import { githubActions, useAppDispatch, useGithubStore } from '@/store'; import { Mint } from '@/views/mint/mint.context'; +import { RepositoiresListStyles as S } from './repositories-list.styles'; import { Repository } from './repository'; type RepositoriesListProps = { searchValue: string; }; -export const RepositoriesList: React.FC = ({ +export const RepositoriesListFragment: React.FC = ({ searchValue, }: RepositoriesListProps) => { const { selectedUserOrg } = Mint.useContext(); @@ -37,15 +37,7 @@ export const RepositoriesList: React.FC = ({ } return ( - + {filteredRepositories.length > 0 ? ( filteredRepositories.map((repo, index, { length }) => ( = ({ )) ) : ( // TODO: update this after designs are done -
- Nothing found. -
+ Nothing found. )} -
+ ); }; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts new file mode 100644 index 00000000..e4cf54ea --- /dev/null +++ b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repositories-list.styles.ts @@ -0,0 +1,20 @@ +import { Flex } from '@/components'; +import { styled } from '@/theme'; + +export const RepositoiresListStyles = { + Container: styled(Flex, { + height: '$60', + overflowX: 'hidden', + overflowY: 'scroll', + flexDirection: 'column', + pr: '$3h', + }), + Message: styled('div', { + position: 'relative', + cursor: 'default', + userSelect: 'none', + p: '$2 $3h $4 $3h', + color: '$slate11', + textAlign: 'center', + }), +}; diff --git a/ui/src/views/mint/github-step/steps/github-repository-selection/repository.tsx b/ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repository.tsx similarity index 100% rename from ui/src/views/mint/github-step/steps/github-repository-selection/repository.tsx rename to ui/src/views/mint/github-step/steps/github-repository-selection/repositories-list/repository.tsx diff --git a/ui/src/views/mint/mint.styles.ts b/ui/src/views/mint/mint.styles.ts index f0b0796c..75b7426c 100644 --- a/ui/src/views/mint/mint.styles.ts +++ b/ui/src/views/mint/mint.styles.ts @@ -5,16 +5,11 @@ export const MintStyles = { Container: styled(Flex, { height: '100%', justifyContent: 'center', + minHeight: '85vh', + alignItems: 'flex-start', '@md': { - //to align on center - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - }, - - '@lg': { - flexDirection: 'row', + alignItems: 'center', }, }), }; diff --git a/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts b/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts index 056a3f3f..8b553106 100644 --- a/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts +++ b/ui/src/views/mint/nfa-step/verify-step/verify-nfa-step.styles.ts @@ -10,6 +10,7 @@ export const VerifyNfaStepStyles = { Text: styled(Text, { color: '$slate11', fontSize: '$sm', + lineHeight: '1.25rem', }), VerifyContainer: styled(Card.Text, { p: '$4', diff --git a/ui/src/views/mint/nft-card/nft-card.tsx b/ui/src/views/mint/nft-card/nft-card.tsx index 22800765..bf0c5c4b 100644 --- a/ui/src/views/mint/nft-card/nft-card.tsx +++ b/ui/src/views/mint/nft-card/nft-card.tsx @@ -29,7 +29,7 @@ export const NftCard: React.FC = ({ onClick, isLoading, }: NftCardProps) => { - const size = '26.5rem'; + const size = '100%'; const { form: { appName: { @@ -48,7 +48,15 @@ export const NftCard: React.FC = ({ } = useMintFormContext(); return ( - + { + const { address } = useAccount(); const { nextStep } = Stepper.useContext(); + useEffect(() => { + if (address) nextStep(); + }, [address, nextStep]); + return ( - {({ isConnected, show, address }) => { - if (isConnected && address) { - nextStep(); - } else { - return ( - - ); - } + {({ show }) => { + return ( + + ); }} ); diff --git a/ui/tailwind.config.js b/ui/tailwind.config.js deleted file mode 100644 index a9076784..00000000 --- a/ui/tailwind.config.js +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable no-undef */ -/** @type {import('tailwindcss').Config} */ - -module.exports = { - content: ['./src/**/*.tsx'], - safelist: [ - { - pattern: /(bg|border|text)-(slate)(4|11|12)/, - }, - { - pattern: /w-(0|[1-9][0-9]?|100)/, - }, - ], - theme: { - extend: { - colors: { - //TODO if we're gonna have ligth mode we should add also the light colors cause tailwind doesn't have them - slate4: 'rgba(38, 41, 43, 1)', - slate5: 'rgba(43, 47, 49, 1)', - slate6: 'rgba(49, 53, 56, 1)', - slate7: 'rgba(58, 63, 66, 1)', - slate11: 'rgba(155, 161, 166, 1)', - slate12: 'rgba(236, 237, 238, 1)', - green4: 'rgba(17, 49, 35, 1)', - green11: 'rgba(76, 195, 138, 1)', - red4: 'rgba(72, 26, 29, 1)', - red9: 'rgba(229, 72, 77, 1)', - red11: 'rgba(255, 99, 105, 1)', - }, - borderRadius: { - xhl: '1.25rem', - }, - maxWidth: { - 70: '70%', - }, - space: { - '1h': '0.375rem', - }, - }, - }, - plugins: [], -}; diff --git a/ui/yarn.lock b/ui/yarn.lock index 441a0739..f5d40c76 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4037,30 +4037,11 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-node@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.0.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.4.1, acorn@^8.8.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" @@ -4190,11 +4171,6 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -arg@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -4310,18 +4286,6 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -autoprefixer@^10.4.13: - version "10.4.13" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" - integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== - dependencies: - browserslist "^4.21.4" - caniuse-lite "^1.0.30001426" - fraction.js "^4.2.0" - normalize-range "^0.1.2" - picocolors "^1.0.0" - postcss-value-parser "^4.2.0" - available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -4655,7 +4619,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: +browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -4781,11 +4745,6 @@ camel-case@4.1.2, camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase-css@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" - integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== - camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -4801,7 +4760,7 @@ camelize@^1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== -caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449: +caniuse-lite@^1.0.30001449: version "1.0.30001462" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001462.tgz#b2e801e37536d453731286857c8520d3dcee15fe" integrity sha512-PDd20WuOBPiasZ7KbFnmQRyuLE7cFXW2PVd7dmALzbkUXEP46upAuCDm9eY9vho8fgNMGmbAX92QBZHzcnWIqw== @@ -4905,7 +4864,7 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -chokidar@3.5.3, chokidar@^3.5.3: +chokidar@3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -5003,7 +4962,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.1.4, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -5198,11 +5157,6 @@ css-to-react-native@^3.0.0: css-color-keywords "^1.0.0" postcss-value-parser "^4.0.2" -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - csstype@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" @@ -5304,11 +5258,6 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -defined@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" - integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== - delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" @@ -5357,20 +5306,6 @@ detect-node@^2.0.4, detect-node@^2.1.0: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -detective@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" - integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== - dependencies: - acorn-node "^1.8.2" - defined "^1.0.0" - minimist "^1.2.6" - -didyoumean@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" - integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== - diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -5407,11 +5342,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dlv@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" - integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== - dnscache@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/dnscache/-/dnscache-1.0.2.tgz#fd3c24d66c141625f594c77be7a8dafee2a66c8a" @@ -6128,7 +6058,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -6367,11 +6297,6 @@ formik@^2.2.9: tiny-warning "^1.0.2" tslib "^1.10.0" -fraction.js@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== - framer-motion@^6.3.11: version "6.5.1" resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" @@ -7390,11 +7315,6 @@ lie@3.1.1: dependencies: immediate "~3.0.5" -lilconfig@^2.0.5, lilconfig@^2.0.6: - version "2.1.0" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" - integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -7630,7 +7550,7 @@ meros@^1.2.1: resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.1.tgz#056f7a76e8571d0aaf3c7afcbe7eb6407ff7329e" integrity sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g== -micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@^4.0.0, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -7900,11 +7820,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== - nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -7920,11 +7835,6 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" - integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== - object-inspect@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" @@ -8261,11 +8171,6 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatc resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -8345,51 +8250,12 @@ popmotion@11.0.3: style-value-types "5.0.0" tslib "^2.1.0" -postcss-import@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" - integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== - dependencies: - postcss-value-parser "^4.0.0" - read-cache "^1.0.0" - resolve "^1.1.7" - -postcss-js@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" - integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== - dependencies: - camelcase-css "^2.0.1" - -postcss-load-config@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" - integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== - dependencies: - lilconfig "^2.0.5" - yaml "^1.10.2" - -postcss-nested@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" - integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== - dependencies: - postcss-selector-parser "^6.0.10" - -postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11: - version "6.0.11" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" - integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0: +postcss-value-parser@^4.0.2: version "4.2.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.0.9, postcss@^8.4.18, postcss@^8.4.21: +postcss@^8.4.18: version "8.4.21" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== @@ -8620,11 +8486,6 @@ quick-format-unescaped@^4.0.3: resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8736,13 +8597,6 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" -read-cache@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" - integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== - dependencies: - pify "^2.3.0" - readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.1" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.1.tgz#f9f9b5f536920253b3d26e7660e7da4ccff9bb62" @@ -8886,7 +8740,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.1.7, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.22.1: +resolve@^1.14.2, resolve@^1.17.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -9374,35 +9228,6 @@ symbol-observable@^4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -tailwindcss@^3.2.4: - version "3.2.7" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.7.tgz#5936dd08c250b05180f0944500c01dce19188c07" - integrity sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ== - dependencies: - arg "^5.0.2" - chokidar "^3.5.3" - color-name "^1.1.4" - detective "^5.2.1" - didyoumean "^1.2.2" - dlv "^1.1.3" - fast-glob "^3.2.12" - glob-parent "^6.0.2" - is-glob "^4.0.3" - lilconfig "^2.0.6" - micromatch "^4.0.5" - normalize-path "^3.0.0" - object-hash "^3.0.0" - picocolors "^1.0.0" - postcss "^8.0.9" - postcss-import "^14.1.0" - postcss-js "^4.0.0" - postcss-load-config "^3.1.4" - postcss-nested "6.0.0" - postcss-selector-parser "^6.0.11" - postcss-value-parser "^4.2.0" - quick-lru "^5.1.1" - resolve "^1.22.1" - tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -9763,7 +9588,7 @@ utf8@^3.0.0: resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== -util-deprecate@^1.0.1, util-deprecate@^1.0.2: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -10052,11 +9877,6 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"