theme-ui#Spinner TypeScript Examples

The following examples show how to use theme-ui#Spinner. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
Button: React.FunctionComponent<SliceMachineButtonProps> = ({
  isLoading = false,
  children,
  onClick,
  form,
  type,
  disabled,
  sx = {},
}) => {
  return (
    <ThemeUIButton
      sx={{
        alignSelf: "flex-start",
        ...sx,
      }}
      form={form}
      type={type}
      disabled={disabled}
      onClick={onClick}
    >
      {isLoading ? <Spinner color="#F7F7F7" size={14} mr={2} /> : children}
    </ThemeUIButton>
  );
}
Example #2
Source File: SaveButton.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
SaveButton: React.FC<{
  onClick: React.MouseEventHandler;
  loading: boolean;
  disabled: boolean;
  children: React.ReactNode;
}> = ({ loading, children, disabled, onClick }) => (
  <Button
    sx={{
      display: "flex",
      cursor: "pointer",
      p: 2,
      px: 3,
      alignItems: "center",
      justifyContent: "center",
      borderColor: "transparent",
    }}
    variant={disabled ? "buttons.disabled" : "buttons.primary"}
    disabled={disabled || loading}
    onClick={onClick}
  >
    {loading && (
      <>
        <Spinner color="#F7F7F7" size={24} mr={2} />{" "}
      </>
    )}
    {children}
  </Button>
)
Example #3
Source File: slices.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
CreateSliceButton = ({
  onClick,
  loading,
}: {
  onClick: () => void;
  loading: boolean;
}) => (
  <Button
    onClick={onClick}
    data-cy="create-slice"
    sx={{
      minWidth: "120px",
    }}
  >
    {loading ? <Spinner color="#FFF" size={14} /> : "Create a Slice"}
  </Button>
)
Example #4
Source File: LiveEdit.tsx    From use-comments with MIT License 5 votes vote down vote up
scope = { useComments, formatDate, AddComment, formatStatus, Spinner }
Example #5
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
EmptyState: React.FunctionComponent<Props> = ({
  title,
  onCreateNew,
  buttonText,
  isLoading,
  documentationComponent,
}) => (
  <Flex
    sx={(theme) => ({
      maxWidth: "480px",
      flexDirection: "column",
      border: `1px solid ${theme.colors?.grey02 as string}`,
    })}
  >
    <Flex
      sx={(theme) => ({
        flexDirection: "column",
        p: 4,
        borderBottom: `1px solid ${theme.colors?.grey02 as string}`,
      })}
    >
      <Heading
        as={"h3"}
        variant={"heading"}
        sx={{ fontSize: "16px", lineHeight: "24px", mb: 2 }}
      >
        {title}
      </Heading>
      <Text variant="xs" sx={{ lineHeight: "24px", fontSize: "13px" }}>
        {documentationComponent}
      </Text>
    </Flex>
    <Flex
      sx={{
        p: 4,
        alignItems: "center",
      }}
    >
      <Button
        onClick={onCreateNew}
        sx={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          mr: 4,
        }}
      >
        {isLoading ? <Spinner color="#FFF" size={14} /> : buttonText}
      </Button>
      <Text
        sx={{
          fontSize: "12px",
          color: "grey04",
          maxWidth: "280px",
        }}
      >
        It will be stored locally and you will be able to push it to your
        repository
      </Text>
    </Flex>
  </Flex>
)
Example #6
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
LoadingPage: React.FunctionComponent = () => (
  <FullPage>
    <Spinner variant="styles.spinner" />
  </FullPage>
)
Example #7
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
CustomTypes: React.FunctionComponent = () => {
  const { openCreateCustomTypeModal } = useSliceMachineActions();
  const { customTypes, isCreatingCustomType, customTypeCount } = useSelector(
    (store: SliceMachineStoreType) => ({
      customTypes: selectAllCustomTypes(store),
      customTypeCount: selectCustomTypeCount(store),
      isCreatingCustomType: isLoading(
        store,
        LoadingKeysEnum.CREATE_CUSTOM_TYPE
      ),
    })
  );

  return (
    <Container sx={{ flex: 1, display: "flex", flexDirection: "column" }}>
      <Header
        ActionButton={
          customTypeCount > 0 ? (
            <Button
              data-cy="create-ct"
              onClick={openCreateCustomTypeModal}
              sx={{
                minWidth: "171px",
              }}
            >
              {isCreatingCustomType ? (
                <Spinner color="#FFF" size={14} />
              ) : (
                "Create a Custom Type"
              )}
            </Button>
          ) : undefined
        }
        MainBreadcrumb={
          <Fragment>
            <MdSpaceDashboard /> <Text ml={2}>Custom Types</Text>
          </Fragment>
        }
        breadrumbHref="/"
      />
      {customTypeCount === 0 ? (
        <Flex
          sx={{
            flex: 1,
            justifyContent: "center",
            alignItems: "center",
          }}
        >
          <EmptyState
            title={"What are Custom Types?"}
            onCreateNew={openCreateCustomTypeModal}
            isLoading={isCreatingCustomType}
            buttonText={"Create one"}
            documentationComponent={
              <>
                Custom Types are models for your documents. They are the place
                where you define and configure Fields and Slices for your
                content. They will be stored locally, and you will be able to
                push them to your repository.{" "}
                <ThemeLink
                  target={"_blank"}
                  href={"https://prismic.io/docs/core-concepts/custom-types "}
                  sx={(theme) => ({ color: theme?.colors?.primary })}
                >
                  Learn more
                </ThemeLink>
                .
              </>
            }
          />
        </Flex>
      ) : (
        <CustomTypeTable customTypes={customTypes} />
      )}
      <CreateCustomTypeModal />
    </Container>
  );
}
Example #8
Source File: Login.tsx    From nft-market with MIT License 5 votes vote down vote up
Login = () => {
  const { activatingConnector, setActivatingConnector } = useAppState()
  const { connector, activate } = useWeb3React()
  return (
    <Flex sx={{ justifyContent: 'center' }}>
      {Object.keys(connectorsByName).map((name: string) => {
        const currentConnector = connectorsByName[name as keyof typeof connectorsByName]
        const activating = currentConnector === activatingConnector
        const connected = currentConnector === connector

        return (
          <Button
            mt={2}
            mr={2}
            variant="connect"
            sx={{
              borderColor: activating ? 'orange' : connected ? 'green' : 'unset',
              position: 'relative',
              maxWidth: 250,
            }}
            key={name}
            onClick={() => {
              setActivatingConnector(currentConnector)
              activate(connectorsByName[name as keyof typeof connectorsByName] as AbstractConnector)
            }}
          >
            {iconsMap[name as keyof typeof connectorsByName] && (
              <Image
                sx={{ width: 35, height: 35 }}
                mr={3}
                src={iconsMap[name as keyof typeof connectorsByName]}
              />
            )}

            {name}
            {activating && <Spinner size={20} color="white" sx={{ ml: 3 }} />}
          </Button>
        )
      })}
    </Flex>
  )
}
Example #9
Source File: TransactionProgress.tsx    From nft-market with MIT License 5 votes vote down vote up
TransactionProgress = () => {
  const { setTransaction, setUser, updateTokensOnSale } = useAppState(
    useCallback(
      ({ setTransaction, setUser, updateTokensOnSale }) => ({
        setTransaction,
        setUser,
        updateTokensOnSale,
      }),
      []
    )
  )

  const transactionRef = useRef(useAppState.getState().transaction)
  const [loading, setLoading] = useState<boolean>(false)

  const update = useCallback(async () => {
    await setUser()
    setTransaction(undefined)
    updateTokensOnSale()
    setLoading(false)
  }, [setTransaction, setUser, updateTokensOnSale])

  useEffect(() => {
    useAppState.subscribe(async ({ transaction }) => {
      try {
        transactionRef.current = transaction
        if (!transaction) return
        setLoading(true)
        const receipt = await transaction.wait()
        if (receipt.confirmations >= 1) {
          update()
        }
      } catch (e) {
        console.log('transaction', e)
        setLoading(false)
      }
    })

    return () => {
      useAppState.destroy()
    }
  }, [update])

  if (!loading) return null

  return (
    <Card variant="transaction">
      <Flex sx={{ alignItems: 'center' }}>
        <Spinner size={20} color="white" sx={{ mr: 2 }} /> Transaction:{' '}
        {toShort(transactionRef.current.hash)}
      </Flex>
    </Card>
  )
}
Example #10
Source File: Login.tsx    From nft-market with MIT License 5 votes vote down vote up
Login = () => {
  const { activatingConnector, setActivatingConnector } = useAppState()
  const { connector, activate } = useWeb3React()
  return (
    <Flex sx={{ justifyContent: 'center' }}>
      {Object.keys(connectorsByName).map((name: string) => {
        const currentConnector = connectorsByName[name as keyof typeof connectorsByName]
        const activating = currentConnector === activatingConnector
        const connected = currentConnector === connector

        return (
          <Button
            mt={2}
            mr={2}
            variant="connect"
            sx={{
              borderColor: activating ? 'orange' : connected ? 'green' : 'unset',
              position: 'relative',
              maxWidth: 250,
            }}
            key={name}
            onClick={() => {
              setActivatingConnector(currentConnector)
              activate(connectorsByName[name as keyof typeof connectorsByName] as AbstractConnector)
            }}
          >
            {iconsMap[name as keyof typeof connectorsByName] && (
              <Image
                sx={{ width: 35, height: 35 }}
                mr={3}
                src={iconsMap[name as keyof typeof connectorsByName]}
              />
            )}

            {name}
            {activating && <Spinner size={20} color="white" sx={{ ml: 3 }} />}
          </Button>
        )
      })}
    </Flex>
  )
}
Example #11
Source File: index.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
LoginModal: React.FunctionComponent = () => {
  Modal.setAppElement("#__next");

  const { env, isOpen, isLoginLoading } = useSelector(
    (store: SliceMachineStoreType) => ({
      isOpen: isModalOpen(store, ModalKeysEnum.LOGIN),
      isLoginLoading: isLoading(store, LoadingKeysEnum.LOGIN),
      env: getEnvironment(store),
    })
  );

  const { closeLoginModal, startLoadingLogin, stopLoadingLogin, openToaster } =
    useSliceMachineActions();

  const prismicBase = preferWroomBase(env.manifest.apiEndpoint);
  const loginRedirectUrl = `${
    buildEndpoints(prismicBase).Dashboard.cliLogin
  }&port=${new URL(env.sliceMachineAPIUrl).port}&path=/api/auth`;
  const onClick = async () => {
    if (!loginRedirectUrl) {
      return;
    }

    try {
      startLoadingLogin();
      await startAuth();
      window.open(loginRedirectUrl, "_blank");
      const { shortId, intercomHash } = await startPolling<
        CheckAuthStatusResponse,
        ValidAuthStatus
      >(
        checkAuthStatus,
        (status: CheckAuthStatusResponse): status is ValidAuthStatus =>
          status.status === "ok" &&
          Boolean(status.shortId) &&
          Boolean(status.intercomHash),
        3000,
        60
      );

      void Tracker.get().identifyUser(shortId, intercomHash);

      openToaster("Logged in", ToasterType.SUCCESS);
      stopLoadingLogin();
      closeLoginModal();
    } catch (e) {
      stopLoadingLogin();
      openToaster("Logging fail", ToasterType.ERROR);
    }
  };

  return (
    <SliceMachineModal
      isOpen={isOpen}
      shouldCloseOnOverlayClick
      onRequestClose={closeLoginModal}
      contentLabel={"login_modal"}
      style={{
        content: {
          position: "static",
          display: "flex",
          margin: "auto",
          minHeight: "initial",
        },
        overlay: {
          display: "flex",
        },
      }}
    >
      <Card>
        <Flex
          sx={{
            p: "16px",
            bg: "headSection",
            alignItems: "center",
            justifyContent: "space-between",
            borderRadius: "8px 8px 0px 0px",
            borderBottom: (t) => `1px solid ${String(t.colors?.borders)}`,
          }}
        >
          <Heading sx={{ fontSize: "16px" }}>You're not connected</Heading>
          <Close sx={{ p: 0 }} type="button" onClick={closeLoginModal} />
        </Flex>
        <Flex
          sx={{
            flexDirection: "column",
            p: 3,
            justifyContent: "center",
            alignItems: "stretch",
          }}
        >
          <Text
            variant={"xs"}
            sx={{
              mb: 3,
              maxWidth: 280,
              textAlign: "center",
            }}
          >
            {isLoginLoading ? (
              <>
                Not seeing the browser tab? <br />
                <Link target={"_blank"} href={loginRedirectUrl}>
                  Click here
                </Link>
              </>
            ) : (
              <>
                Your session has expired.
                <br />
                Please log in again.
              </>
            )}
          </Text>
          <Button
            sx={{
              display: "flex",
              justifyContent: "center",
              alignItems: "center",
              width: 240,
              mb: 3,
            }}
            onClick={() => void onClick()}
          >
            {isLoginLoading ? (
              <Spinner color="#FFF" size={16} />
            ) : (
              <>Signin to Prismic</>
            )}
          </Button>
        </Flex>
      </Card>
    </SliceMachineModal>
  );
}
Example #12
Source File: Header.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
CustomTypeHeader = () => {
  const {
    currentCustomType,
    hasPendingModifications,
    customTypeStatus,
    isPushingCustomType,
    isSavingCustomType,
  } = useSelector((store: SliceMachineStoreType) => ({
    currentCustomType: selectCurrentCustomType(store),
    hasPendingModifications:
      selectIsCurrentCustomTypeHasPendingModifications(store),
    customTypeStatus: selectCustomTypeStatus(store),
    isPushingCustomType: isLoading(store, LoadingKeysEnum.PUSH_CUSTOM_TYPE),
    isSavingCustomType: isLoading(store, LoadingKeysEnum.SAVE_CUSTOM_TYPE),
  }));
  const { saveCustomType, pushCustomType } = useSliceMachineActions();

  if (!currentCustomType) return null;

  const buttonProps = (() => {
    if (hasPendingModifications) {
      return {
        onClick: () => {
          saveCustomType();
        },
        children: (
          <span>
            {isSavingCustomType ? (
              <Spinner
                color="#F7F7F7"
                size={20}
                mr={2}
                sx={{ position: "relative", top: "5px", left: "3px" }}
              />
            ) : null}
            Save to File System
          </span>
        ),
      };
    }
    if (
      [CustomTypeStatus.New, CustomTypeStatus.Modified].includes(
        customTypeStatus
      )
    ) {
      return {
        onClick: () => {
          if (!isPushingCustomType) {
            pushCustomType();
          }
        },
        children: (
          <span>
            {isPushingCustomType ? (
              <Spinner
                color="#F7F7F7"
                size={20}
                mr={2}
                sx={{ position: "relative", top: "5px", left: "3px" }}
              />
            ) : null}
            Push to Prismic
          </span>
        ),
      };
    }
    return { variant: "disabled", children: "Synced with Prismic" };
  })();

  return (
    <Header
      MainBreadcrumb={
        <>
          <MdSpaceDashboard /> <Text ml={2}>Custom Types</Text>
        </>
      }
      SecondaryBreadcrumb={
        <Box sx={{ fontWeight: "thin" }} as="span">
          <Text ml={2}>/ {currentCustomType.label} </Text>
        </Box>
      }
      breadrumbHref="/"
      ActionButton={<Button {...buttonProps} />}
    />
  );
}
Example #13
Source File: ImagePreview.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
ImagePreview: React.FC<ImagePreviewProps> = ({
  src,
  onScreenshot,
  imageLoading,
  onHandleFile,
  preventScreenshot,
}) => {
  const inputFile = useRef<HTMLInputElement>(null);
  const { isCheckingSetup } = useSelector((state: SliceMachineStoreType) => ({
    isCheckingSetup: isLoading(state, LoadingKeysEnum.CHECK_SIMULATOR),
  }));
  const [display, setDisplay] = useState(false);

  const handleFile = (file: File | undefined) => {
    if (inputFile?.current) {
      file && onHandleFile(file);
      inputFile.current.value = "";
    }
  };

  return (
    <div>
      <input
        id="input-file"
        type="file"
        ref={inputFile}
        style={{ display: "none" }}
        accept={acceptedImagesTypes.map((type) => `image/${type}`).join(",")}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          handleFile(e.target.files?.[0])
        }
      />
      <Flex
        sx={{
          position: "relative",
          alignItems: "center",
          justifyContent: "center",
          height: "290px",
          overflow: "hidden",
          backgroundImage: "url(/pattern.png)",
          backgroundColor: "headSection",
          backgroundRepeat: "repeat",
          backgroundSize: "20px",
          border: "1px solid #C9D0D8",
          boxShadow: "0px 8px 14px rgba(0, 0, 0, 0.1)",
          borderRadius: "4px",
        }}
        onMouseEnter={() => setDisplay(true)}
        onMouseLeave={() => setDisplay(false)}
      >
        {display || imageLoading || isCheckingSetup ? (
          <Flex
            sx={{
              width: "100%",
              height: "100%",
              position: "absolute",
              background: "rgba(0,0,0,.4)",
              alignItems: "center",
              justifyContent: "center",
              zIndex: "0",
            }}
          >
            {display ? (
              <Fragment>
                <Flex sx={{ flexDirection: "column" }}>
                  <Button
                    sx={{ mb: 3 }}
                    onClick={onScreenshot}
                    disabled={preventScreenshot}
                    variant={preventScreenshot ? "disabled" : "primary"}
                  >
                    Take screenshot
                  </Button>
                  <Label
                    htmlFor="input-file"
                    variant="buttons.primary"
                    sx={{ p: 2, borderRadius: "4px" }}
                  >
                    Custom screenshot
                  </Label>
                </Flex>
              </Fragment>
            ) : (
              <Spinner />
            )}
          </Flex>
        ) : null}
        {src ? (
          <MemoedImage src={src} />
        ) : (
          <Text
            sx={{
              display: "flex",
              flexDirection: "column",
              alignItems: "center",
            }}
          >
            <MdInfoOutline />
            You have no screenshot yet.
          </Text>
        )}
      </Flex>
    </div>
  );
}
Example #14
Source File: index.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
SideBar: React.FunctionComponent<SideBarProps> = ({
  Model,
  variation,
  imageLoading,
  onScreenshot,
  onHandleFile,
}) => {
  const { screenshotUrls } = Model;

  const { checkSimulatorSetup } = useSliceMachineActions();

  const router = useRouter();

  const {
    isCheckingSimulatorSetup,
    isSimulatorAvailableForFramework,
    linkToStorybookDocs,
    framework,
    storybookUrl,
  } = useSelector((state: SliceMachineStoreType) => ({
    framework: getFramework(state),
    linkToStorybookDocs: getLinkToStorybookDocs(state),
    isCheckingSimulatorSetup: isLoading(state, LoadingKeysEnum.CHECK_SIMULATOR),
    isSimulatorAvailableForFramework:
      selectIsSimulatorAvailableForFramework(state),
    storybookUrl: getStorybookUrl(state),
  }));

  return (
    <Box
      sx={{
        pl: 3,
        flexGrow: 1,
        flexBasis: "sidebar",
      }}
    >
      <Card bg="headSection" bodySx={{ p: 0 }} footerSx={{ p: 0 }}>
        <MemoizedImagePreview
          src={
            screenshotUrls &&
            screenshotUrls[variation.id] &&
            screenshotUrls[variation.id].url
          }
          imageLoading={imageLoading}
          onScreenshot={onScreenshot}
          onHandleFile={onHandleFile}
          preventScreenshot={!isSimulatorAvailableForFramework}
        />
      </Card>
      <Button
        data-testid="open-set-up-simulator"
        disabled={!isSimulatorAvailableForFramework}
        onClick={() =>
          checkSimulatorSetup(true, () =>
            window.open(`${router.asPath}/simulator`)
          )
        }
        variant={
          isSimulatorAvailableForFramework ? "secondary" : "disabledSecondary"
        }
        sx={{ cursor: "pointer", width: "100%", mt: 3 }}
      >
        {isCheckingSimulatorSetup ? <Spinner size={12} /> : "Preview Slice"}
      </Button>
      {!isSimulatorAvailableForFramework && (
        <Text
          as="p"
          sx={{
            textAlign: "center",
            mt: 3,
            color: "grey05",
            "::first-letter": {
              "text-transform": "uppercase",
            },
          }}
        >
          {`Slice Simulator does not support ${
            framework || "your"
          } framework yet.`}
          &nbsp;
          {!storybookUrl ? (
            <>
              You can{" "}
              <a target={"_blank"} href={linkToStorybookDocs}>
                install Storybook
              </a>{" "}
              instead.
            </>
          ) : null}
        </Text>
      )}

      {storybookUrl && (
        <Link
          href={createStorybookUrl({
            storybook: storybookUrl,
            libraryName: Model.from,
            sliceName: Model.model.name,
            variationId: variation.id,
          })}
        >
          <Button variant={"secondary"} sx={{ width: "100%", mt: 3 }}>
            Open Storybook
          </Button>
        </Link>
      )}
    </Box>
  );
}
Example #15
Source File: Token.tsx    From nft-market with MIT License 4 votes vote down vote up
Token = ({ token, isOnSale, onTransfer, onBuy, onSale }: TokenCompProps) => {
  const [transfer, setTransfer] = useState<boolean>(false)
  const [onSaleActive, setOnSale] = useState<boolean>(false)
  const [address, setAddress] = useState<string>('')
  const [price, setPrice] = useState<string>('')
  const { user, ethPrice, contractDetails, transferToken, buyToken, setTokenSale } = useAppState()

  const onTransferClick = async (e: FormEvent | MouseEvent) => {
    e.preventDefault()
    if (onTransfer && utils.isAddress(address)) {
      transferToken(token.id, address)
      setTransfer(false)
    }
  }

  const onBuyClick = (e: MouseEvent) => {
    e.preventDefault()
    onBuy && buyToken(token.id, token.price)
  }

  const onSaleClick = async (e: MouseEvent) => {
    e.preventDefault()
    if (!onSale) return
    try {
      await setTokenSale(token.id, utils.parseEther(price), true)
      setOnSale(false)
    } catch (e) {
      throw e
    }
  }

  const { data: owner } = useSWR(token.id, fetchOwner)
  const { data } = useSWR(`${METADATA_API}/token/${token.id}`, fetcherMetadata)

  const tokenPriceEth = formatPriceEth(token.price, ethPrice)

  if (!data)
    return (
      <Card variant="nft">
        <Spinner />
      </Card>
    )

  if (!data.name) return null

  return (
    <Card variant="nft">
      <Image
        sx={{ width: '100%', bg: 'white', borderBottom: '1px solid black' }}
        src={data.image}
      />
      <Box p={3} pt={2}>
        <Heading as="h2">{data.name}</Heading>
        <Divider variant="divider.nft" />
        <Box>
          <Text sx={{ color: 'lightBlue', fontSize: 1, fontWeight: 'bold' }}>Price</Text>
          <Heading as="h3" sx={{ color: 'green', m: 0, fontWeight: 'bold' }}>
            {constants.EtherSymbol} {Number(utils.formatEther(token.price)).toFixed(2)}{' '}
            <Text sx={{ color: 'navy' }} as="span" variant="text.body">
              ({tokenPriceEth})
            </Text>
          </Heading>
          {owner && typeof owner === 'string' && !onTransfer && (
            <Box mt={2}>
              <Text as="p" sx={{ color: 'lightBlue', fontSize: 1, fontWeight: 'bold' }}>
                Owner
              </Text>
              <NavLink
                target="_blank"
                href={`https://rinkeby.etherscan.io/address/${owner}`}
                variant="owner"
                style={{
                  textOverflow: 'ellipsis',
                  width: '100%',
                  position: 'relative',
                  overflow: 'hidden',
                }}
              >
                {toShort(owner)}
              </NavLink>
            </Box>
          )}
          <Box mt={2}>
            <NavLink
              target="_blank"
              href={`https://testnets.opensea.io/assets/${contractDetails?.address}/${token.id}`}
              variant="openSea"
            >
              View on Opensea.io
            </NavLink>
          </Box>
        </Box>

        {onTransfer && (
          <Flex mt={3} sx={{ justifyContent: 'center' }}>
            {transfer && (
              <Box sx={{ width: '100%' }}>
                <Flex
                  onSubmit={onTransferClick}
                  sx={{ width: '100%', flexDirection: 'column' }}
                  as="form"
                >
                  <Input
                    onChange={e => setAddress(e.currentTarget.value)}
                    placeholder="ETH Address 0x0..."
                  />
                </Flex>
                <Flex mt={2}>
                  <Button sx={{ bg: 'green' }} onClick={onTransferClick} variant="quartiary">
                    Confirm
                  </Button>
                  <Button
                    sx={{ bg: 'red' }}
                    ml={2}
                    onClick={() => setTransfer(false)}
                    variant="quartiary"
                  >
                    Cancel
                  </Button>
                </Flex>
              </Box>
            )}
            {onSaleActive && (
              <Box sx={{ width: '100%' }}>
                <Flex
                  onSubmit={onTransferClick}
                  sx={{ width: '100%', flexDirection: 'column' }}
                  as="form"
                >
                  <Input
                    onChange={e => setPrice(e.currentTarget.value)}
                    placeholder="Token Price in ETH"
                  />
                </Flex>
                <Flex mt={2}>
                  <Button sx={{ bg: 'green' }} onClick={onSaleClick} variant="quartiary">
                    Confirm
                  </Button>
                  <Button
                    sx={{ bg: 'red' }}
                    ml={2}
                    onClick={() => setOnSale(false)}
                    variant="quartiary"
                  >
                    Cancel
                  </Button>
                </Flex>
              </Box>
            )}
            {!transfer && !onSaleActive && (
              <Flex sx={{ flexDirection: 'column', width: '100%', justifyContent: 'center' }}>
                <Button onClick={() => setTransfer(!transfer)} variant="tertiary">
                  Transfer
                </Button>
                {isOnSale ? (
                  <Button
                    mt={2}
                    onClick={() => onSale && setTokenSale(token.id, token.price, false)}
                    variant="tertiary"
                  >
                    Remove from Sale
                  </Button>
                ) : (
                  <Button mt={2} onClick={() => setOnSale(!onSaleActive)} variant="tertiary">
                    Put Token for Sale
                  </Button>
                )}
              </Flex>
            )}
          </Flex>
        )}
        {onBuy && (
          <Flex mt={3} sx={{ justifyContent: 'center', width: '100%' }}>
            <Button
              sx={{
                opacity: !!user?.ownedTokens.find(
                  a => utils.formatUnits(a.id) === utils.formatUnits(token.id)
                )
                  ? 0.5
                  : 1,
                pointerEvents: !!user?.ownedTokens.find(
                  a => utils.formatUnits(a.id) === utils.formatUnits(token.id)
                )
                  ? 'none'
                  : 'visible',
              }}
              onClick={onBuyClick}
              variant="quartiary"
            >
              Buy Token
            </Button>
          </Flex>
        )}
      </Box>
    </Card>
  )
}