theme-ui#Heading TypeScript Examples

The following examples show how to use theme-ui#Heading. 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: Error.tsx    From nft-market with MIT License 6 votes vote down vote up
Error = () => {
  return (
    <Flex>
      <Image sx={{ width: 100, height: 100 }} src="/icons/icon-512x512.png" />
      <Box mt={4} ml={4}>
        <Heading as="h2">Metamask not installed</Heading>
        <Text mt={2}>
          Go to{' '}
          <Link href="https://metamask.io" target="_blank">
            https://metamask.io
          </Link>{' '}
          to install it.
        </Text>
      </Box>
    </Flex>
  )
}
Example #2
Source File: Logo.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
Logo: React.FC = () => {
  const { theme } = useThemeUI();
  return (
    <Box p={2}>
      <Link href="/" passHref>
        <ThemeLink variant="links.invisible">
          <Flex sx={{ alignItems: "center" }}>
            <SliceMachineLogo
              width="32px"
              height="32px"
              fill={theme.colors?.text as string}
            />
            <Heading as="h5" sx={{ ml: 2 }}>
              Slice Machine
            </Heading>
          </Flex>
        </ThemeLink>
      </Link>
    </Box>
  );
}
Example #3
Source File: UserMenu.tsx    From nft-market with MIT License 6 votes vote down vote up
UserMenu = () => {
  const { user, isAuthenticated } = useAppState()

  const history = useHistory()

  return (
    <Flex sx={{ ml: 'auto', justifySelf: 'flex-end' }}>
      {isAuthenticated && user && (
        <>
          <Box sx={{ display: ['none', 'block'] }}>
            <Heading sx={{ p: 0, color: 'white' }} as="h4">
              {toShort(user.address)}
            </Heading>
            <Heading sx={{ p: 0, mt: 1, textAlign: 'right', color: 'white' }} as="h5">
              {EtherSymbol}
              {user.balance}
            </Heading>
          </Box>
          <Box
            onClick={() => {
              history.push('/profile')
            }}
            sx={{
              cursor: 'pointer',
              ml: [0, 3],
              height: 30,
              width: 30,
              borderRadius: '100%',
            }}
          >
            <Identicon size={30} address={user.address} />
          </Box>
        </>
      )}
    </Flex>
  )
}
Example #4
Source File: ContractDetails.tsx    From nft-market with MIT License 6 votes vote down vote up
ContractDetails = () => {
  const { contractDetails } = useAppState()
  return (
    <Box>
      <Heading as="h1">NFT Contract Details</Heading>
      <Grid my={3}>
        {contractDetails &&
          Object.keys(contractDetails).map(a => (
            <Text key={a}>
              {a}:{' '}
              <Text variant="text.bold" as="span">
                {contractDetails[a as keyof ContractPropsDetails]}
              </Text>
            </Text>
          ))}
      </Grid>
    </Box>
  )
}
Example #5
Source File: onboarding.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
Header = (props: HeadingProps) => (
  <Heading
    {...props}
    sx={{
      textAlign: "center",
      ...props.sx,
    }}
    style={{
      fontSize: "20px",
      lineHeight: "1.5",
      fontWeight: 700,
    }}
  />
)
Example #6
Source File: index.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
ZoneEmptyState = ({
  onEnterSelectMode,
  zoneName,
}: {
  // eslint-disable-next-line @typescript-eslint/ban-types
  onEnterSelectMode: Function;
  zoneName: string;
}) => (
  <Box sx={{ textAlign: "center", my: 4 }}>
    <Heading as="h5">Add a new field here</Heading>
    <Box sx={{ my: 2 }}>
      <Text sx={{ color: "textClear" }}>
        Add a field to your {zoneName} Zone
      </Text>
    </Box>
    <Button
      mt={3}
      variant="buttons.darkSmall"
      // eslint-disable-next-line @typescript-eslint/no-unsafe-return
      onClick={() => onEnterSelectMode()}
      data-testid="empty-zone-add-new-field"
    >
      <FaPlus
        style={{ marginRight: "8px", position: "relative", top: "2px" }}
      />{" "}
      Add a new field
    </Button>
  </Box>
)
Example #7
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
SharedSlice = {
  render({
    bordered,
    slice,
    displayStatus,
    Wrapper,
    CustomStatus,

    thumbnailHeightPx = "280px",
    wrapperType = WrapperType.clickable,
    sx,
  }: {
    bordered?: boolean;
    displayStatus?: boolean;

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    CustomStatus?: any;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    Wrapper?: any /* ? */;
    slice: SliceState;
    wrapperType?: WrapperType;
    thumbnailHeightPx?: string;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    sx?: any;
  }) {
    const defaultVariation = SliceState.variation(slice);
    if (!defaultVariation) {
      return null;
    }
    const variationId = defaultVariation.id;
    const link = LinkUtil.variation(slice.href, slice.model.name, variationId);

    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
    const CardWrapper = Wrapper || WrapperByType[wrapperType];

    const screenshotUrl = slice?.screenshotUrls?.[variationId]?.url;

    return (
      <CardWrapper link={link} slice={slice}>
        <Themecard
          role="button"
          aria-pressed="false"
          // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-argument
          sx={bordered ? borderedSx(sx) : defaultSx(sx)}
        >
          <SliceThumbnail
            withShadow={false}
            screenshotUrl={screenshotUrl}
            heightInPx={thumbnailHeightPx}
          />
          <Flex
            mt={3}
            sx={{ alignItems: "center", justifyContent: "space-between" }}
          >
            <Flex sx={{ alignItems: "center" }}>
              {CustomStatus ? (
                <CustomStatus slice={slice} />
              ) : (
                <Fragment>
                  {displayStatus && slice.__status ? (
                    <StatusBadge libStatus={slice.__status} />
                  ) : null}
                </Fragment>
              )}
              <Heading sx={{ flex: 1 }} as="h6">
                {slice.model.name}
              </Heading>
            </Flex>
            <SliceVariations
              variations={slice.variations}
              hideVariations={false}
            />
          </Flex>
        </Themecard>
      </CardWrapper>
    );
  },
}
Example #8
Source File: Profile.tsx    From nft-market with MIT License 5 votes vote down vote up
Profile = () => {
  const { user, tokensOnSale } = useAppState()

  if (!user) return null

  const { address, balance, ownedTokens } = user

  return (
    <Box>
      <Heading as="h1">My Profile</Heading>
      <Grid columns={['1fr', '1fr 1fr']} sx={{ overflow: 'hidden', gap: '0 20px' }}>
        <Box>
          <Heading as="h4" sx={{ color: 'green' }}>
            Address
          </Heading>
          <Text>{address}</Text>
        </Box>
        <Box>
          <Heading as="h4" sx={{ color: 'green' }}>
            Balance
          </Heading>
          <Text>Ξ {balance}</Text>
        </Box>
      </Grid>
      <Divider variant="divider.nft" sx={{ my: 7 }} />
      <Box my={5}>
        {ownedTokens && ownedTokens.length > 0 ? (
          <Box>
            <Heading as="h2">
              My items{' '}
              <Text variant="text.body" as="span">
                ({ownedTokens.length} item)
              </Text>
            </Heading>
            <Grid gap={4} columns={['1fr 1fr', '1fr 1fr 1fr']}>
              {ownedTokens.map((t, index) => (
                <Token
                  isOnSale={
                    !!tokensOnSale?.find(a => utils.formatUnits(a.id) === utils.formatUnits(t.id))
                  }
                  onSale
                  onTransfer
                  token={t}
                  key={index}
                />
              ))}
            </Grid>
          </Box>
        ) : (
          ownedTokens && (
            <Box>
              <Heading as="h2">You don't own any NFT tokens</Heading>
            </Box>
          )
        )}
      </Box>
    </Box>
  )
}
Example #9
Source File: Connect.tsx    From nft-market with MIT License 5 votes vote down vote up
Connect: FC = ({ children }) => {
  const { activatingConnector } = useAppState()
  const { library, chainId, account, error } = useWeb3React()

  const { setContract, setUser } = useAppState(
    useCallback(
      ({ setContract, setUser }) => ({
        setContract,
        setUser,
      }),
      []
    )
  )

  useSWR(ETHSCAN_API, fetcherETHUSD)

  useEffect(() => {
    if (!chainId || !account || !library) return

    const update = async () => {
      try {
        await setContract(library, chainId)
        setUser(account)
      } catch (e) {
        console.log(e)
      }
    }

    update()
  }, [chainId, account, library, setContract, setUser])

  const triedEager = useEagerConnect()
  useInactiveListener(!triedEager || !!activatingConnector)

  return (
    <>
      {error ? (
        <Container>
          <Heading as="h2">❌ Something is not right</Heading>
          <Text sx={{ mt: 3 }}>{getErrorMessage(error)}</Text>
        </Container>
      ) : (
        children
      )}
    </>
  )
}
Example #10
Source File: Header.tsx    From nft-market with MIT License 5 votes vote down vote up
Header = () => {
  const history = useHistory()
  const location = useLocation()

  const { user, isAuthenticated } = useAppState()

  return (
    <Box bg="black">
      <Flex sx={{ alignItems: 'center', p: 3 }} as="nav">
        <Image
          onClick={() => {
            history.push('/')
          }}
          sx={{ width: 50, cursor: 'pointer' }}
          src="/static/logo.png"
        />
        <Heading sx={{ ml: [1, 2], color: 'white' }} as="h4">
          ERC721 Marketplace{' '}
          <Text sx={{ display: ['none', 'block'] }}>+ OpenSea.io on Rinkeby Network</Text>
        </Heading>
        <UserMenu />
      </Flex>
      {isAuthenticated && user && (
        <Flex bg="midGray" py={3} sx={{ justifyContent: 'center' }}>
          <NavLink
            sx={{
              pointerEvents: location.pathname === '/' ? 'none' : 'visible',
              color: location.pathname === '/' ? 'green' : 'white',
            }}
            onClick={() => history.push('/')}
          >
            Marketplace
          </NavLink>
          <Box sx={{ width: 50 }} />
          <NavLink
            sx={{
              pointerEvents: location.pathname === '/profile' ? 'none' : 'visible',
              color: location.pathname === '/profile' ? 'green' : 'white',
            }}
            onClick={() => history.push('/profile')}
          >
            Profile
          </NavLink>
        </Flex>
      )}
    </Box>
  )
}
Example #11
Source File: Gallery.tsx    From nft-market with MIT License 5 votes vote down vote up
Gallery = () => {
  const { user, tokensOnSale } = useAppState()
  const updateTokensOnSale = useAppState(
    useCallback(({ updateTokensOnSale }) => updateTokensOnSale, [])
  )

  const [order, setOrder] = useState<StateOrder>('alpha')

  useEffect(() => {
    updateTokensOnSale()
  }, [updateTokensOnSale])

  return (
    <Box>
      <Heading as="h1">Marketplace</Heading>
      <Flex sx={{ alignItems: 'center' }} mb={3}>
        <Heading as="h3" sx={{ color: 'lightGray' }}>
          Order:
        </Heading>
        <Flex ml={3}>
          <Button
            mr={2}
            onClick={() => setOrder('alpha')}
            variant="filter"
            disabled={order === 'alpha'}
          >
            Alphabetically
          </Button>
          <Button onClick={() => setOrder('price')} variant="filter" disabled={order === 'price'}>
            Price
          </Button>
        </Flex>
      </Flex>
      <Grid gap={4} columns={['1fr 1fr', '1fr 1fr', '1fr 1fr 1fr']}>
        {tokensOnSale
          ?.sort((a, b) =>
            order === 'alpha'
              ? BigNumber.from(a.id)
                  .toString()
                  .localeCompare(BigNumber.from(b.id).toString(), undefined, { numeric: true })
              : Number(utils.formatEther(a.price.sub(b.price)))
          )
          .map((i, index) => (
            <Token onBuy={!user?.ownedTokens.find(t => t.id === i.id)} token={i} key={index} />
          ))}
      </Grid>
    </Box>
  )
}
Example #12
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
NonSharedSlice = {
  render({
    bordered,
    slice,
    displayStatus,
    thumbnailHeightPx = "280px",
    wrapperType = WrapperType.nonClickable,
    sx,
  }: {
    bordered: boolean;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    slice: { key: string; value: any };
    displayStatus?: boolean;
    thumbnailHeightPx?: string;
    wrapperType?: WrapperType;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    sx?: any;
  }) {
    const Wrapper = WrapperByType[wrapperType];

    return (
      <Wrapper link={undefined}>
        {/* eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-argument */}
        <Themecard sx={bordered ? borderedSx(sx) : defaultSx(sx)}>
          <SliceThumbnail withShadow={false} heightInPx={thumbnailHeightPx} />
          <Flex
            mt={3}
            sx={{ alignItems: "center", justifyContent: "space-between" }}
          >
            <Flex>
              {displayStatus ? (
                <Badge mr={2} variant="modified">
                  Non shared
                </Badge>
              ) : null}
              <Heading sx={{ flex: 1 }} as="h6">
                {/* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */}
                {slice?.value?.fieldset || slice.key}
              </Heading>
            </Flex>
          </Flex>
        </Themecard>
      </Wrapper>
    );
  },
}
Example #13
Source File: index.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
SliceZone: React.FC<SliceZoneProps> = ({
  tabId,
  sliceZone,
  onSelectSharedSlices,
  onRemoveSharedSlice,
  onCreateSliceZone,
}) => {
  const [formIsOpen, setFormIsOpen] = useState(false);
  const libraries = useContext(LibrariesContext);

  const { availableSlices, slicesInSliceZone, notFound } = sliceZone
    ? mapAvailableAndSharedSlices(sliceZone, libraries)
    : { availableSlices: [], slicesInSliceZone: [], notFound: [] };

  useEffect(() => {
    if (notFound?.length) {
      notFound.forEach(({ key }) => {
        onRemoveSharedSlice(key);
      });
    }
  }, [notFound]);

  const sharedSlicesInSliceZone = slicesInSliceZone
    .filter((e) => e.type === SlicesTypes.SharedSlice)
    .map((e) => e.payload) as ReadonlyArray<SliceState>;

  /* Preserve these keys in SliceZone */
  const nonSharedSlicesKeysInSliceZone = slicesInSliceZone
    .filter((e) => e.type === SlicesTypes.Slice)
    .map((e) => (e.payload as NonSharedSliceInSliceZone).key);

  const onAddNewSlice = () => {
    if (!sliceZone) {
      onCreateSliceZone();
    }
    setFormIsOpen(true);
  };

  return (
    <Box my={3}>
      <ZoneHeader
        Heading={<Heading as="h6">Slice Zone</Heading>}
        Actions={
          <Flex sx={{ alignItems: "center" }}>
            {sliceZone ? (
              <Text pr={3} sx={{ fontSize: "14px" }}>
                data.{sliceZone.key}
              </Text>
            ) : null}
            {!!slicesInSliceZone.length && (
              <Button variant="buttons.darkSmall" onClick={onAddNewSlice}>
                Update Slice Zone
              </Button>
            )}
          </Flex>
        }
      />
      {!slicesInSliceZone.length ? (
        <EmptyState onAddNewSlice={onAddNewSlice} />
      ) : (
        <SlicesList slices={slicesInSliceZone} />
      )}
      <UpdateSliceZoneModal
        isOpen={formIsOpen}
        formId={`tab-slicezone-form-${tabId}`}
        availableSlices={availableSlices}
        slicesInSliceZone={sharedSlicesInSliceZone}
        onSubmit={({ sliceKeys }) =>
          // eslint-disable-next-line @typescript-eslint/no-unsafe-return
          onSelectSharedSlices(sliceKeys, nonSharedSlicesKeysInSliceZone)
        }
        close={() => setFormIsOpen(false)}
      />
    </Box>
  );
}
Example #14
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 #15
Source File: Desktop.tsx    From slice-machine with Apache License 2.0 5 votes vote down vote up
UpdateInfo: React.FC<{
  onClick: () => void;
  hasSeenUpdate: boolean;
}> = ({ onClick, hasSeenUpdate }) => {
  return (
    <Flex
      sx={{
        maxWidth: "240px",
        border: "1px solid #E6E6EA",
        borderRadius: "4px",
        padding: "8px",
        flexDirection: "column",
      }}
    >
      <Heading
        as="h6"
        sx={{
          fontSize: "14px",
          margin: "4px 8px",
        }}
      >
        Updates Available{" "}
        {hasSeenUpdate || (
          <span
            data-testid="the-red-dot"
            style={{
              borderRadius: "50%",
              width: "8px",
              height: "8px",
              backgroundColor: "#FF4A4A",
              display: "inline-block",
              margin: "4px",
            }}
          />
        )}
      </Heading>
      <Paragraph
        sx={{
          fontSize: "14px",
          color: "#4E4E55",
          margin: "4px 8px 8px",
        }}
      >
        Some updates of Slice Machine are available.
      </Paragraph>
      <Button
        data-testid="update-modal-open"
        sx={{
          background: "#5B3DF5",
          border: "1px solid rgba(62, 62, 72, 0.15)",
          boxSizing: "border-box",
          borderRadius: "4px",
          margin: "8px",
          fontSize: "11.67px",
          alignSelf: "flex-start",
          padding: "4px 8px",
        }}
        onClick={onClick}
      >
        Learn more
      </Button>
    </Flex>
  );
}
Example #16
Source File: ModalMapSelect.tsx    From HappyIslandDesigner with MIT License 4 votes vote down vote up
function IslandLayoutSelector() {
  const [layoutType, setLayoutType] = useState<LayoutType>(LayoutType.none);
  const [layout, setLayout] = useState<number>(-1);
  const [help, setHelp] = useState<boolean>(false);

  useEffect(() => {
    if (layout != -1)
    {
      const layoutData = getLayouts(layoutType)[layout];
      loadMapFromJSONString(layoutData.data);
      CloseMapSelectModal();
    }
  }, [layoutType, layout]);

  function getLayouts(type: LayoutType) {
    switch (type) {
      case LayoutType.west:
        return Layouts.west;
      case LayoutType.south:
        return Layouts.south;
      case LayoutType.east:
        return Layouts.east;
      case LayoutType.blank:
        return Layouts.blank;
    }
    return [];
  }

  if (help) {
    return (
      <Flex p={[0, 3]} sx={{flexDirection: 'column', alignItems: 'center', position: 'relative'}}>
        <Box sx={{position: 'absolute', left: 0, top: [1, 30]}}>
          <Button variant='icon' onClick={() => setHelp(false)}>
            <Image sx={{width: 'auto'}} src='static/img/back.png' />
          </Button>
        </Box>
        <Image sx={{width: 100, margin: 'auto'}} src={'static/img/blathers.png'}/>
        <Heading m={3} sx={{px: layoutType ? 4 : 0, textAlign: 'center'}}>{'Please help contribute!'}</Heading>
        <Text my={2}>{'Sorry, we don\'t have all the map templates yet (there are almost 100 river layouts in the game!). Each option you see here has been hand-made by a member of the community.'}</Text>
        <Text my={2}>{'You can use the \'Upload Screenshot\' tool to trace an image of your island. When you\'re done please consider contributing your island map in either the '}<Link href={'https://github.com/eugeneration/HappyIslandDesigner/issues/59'}>Github</Link>{' or '}<Link href={'https://discord.gg/EtaqD5H'}>Discord</Link>!</Text>
        <Text my={2}>{'Please note that your island may have different shaped rock formations, beaches, and building positions than another island with the same river layout.'}</Text>
      </Flex>
    )
  }

  let content;
  if (layoutType != LayoutType.none) {
    var layouts: Array<Layout> = getLayouts(layoutType);
    content = (
      <Grid
        gap={0}
        columns={[2, 3, 4]}
        sx={{justifyItems: 'center' }}>
        {
          layouts.map((layout, index) => (
            <Card
              key={index}
              onClick={() => {
                confirmDestructiveAction(
                  'Clear your map? You will lose all unsaved changes.',
                  () => {
                    setLayout(index);
                  });
              }}>
              <Image variant='card' src={`static/img/layouts/${layoutType}-${layout.name}.png`}/>
            </Card>
          )).concat(
            <Card key={'help'} onClick={()=>{setHelp(true)}}>
              <Image sx={{width: 24}} src={'static/img/menu-help.png'} />
              <Text sx={{fontFamily: 'body'}}>{'Why isn\'t my map here?'}</Text>
            </Card>
          )
        }
      </Grid>
    );
  }
  else {
    content = (
      <Flex sx={{flexDirection: ['column', 'row'], alignItems: 'center'}}>
        <Card onClick={() => setLayoutType(LayoutType.west)}><Image variant='card' src={'static/img/island-type-west.png'}/></Card>
        <Card onClick={() => setLayoutType(LayoutType.south)}><Image variant='card' src={'static/img/island-type-south.png'}/></Card>
        <Card onClick={() => setLayoutType(LayoutType.east)}><Image variant='card' src={'static/img/island-type-east.png'}/></Card>
        <Card onClick={() => {
          setLayoutType(LayoutType.blank);
          confirmDestructiveAction(
            'Clear your map? You will lose all unsaved changes.',
            () => {
              setLayout(0);
            });
        }}><Image variant='card' src={'static/img/island-type-blank.png'}/></Card>      </Flex>
    );
  }
  return (
    <Box p={[0, 3]} sx={{position: 'relative'}}>
      {layoutType && <Box sx={{position: 'absolute', top: [1, 3]}}>
        <Button variant='icon' onClick={() => setLayoutType(LayoutType.none)}>
          <Image src='static/img/back.png' />
        </Button>
      </Box>}
      <Heading m={2} sx={{px: layoutType ? 4 : 0, textAlign: 'center'}}>{layoutType ? 'Choose your Island!' : 'Choose your Layout!'}</Heading>
      {layoutType && <Text m={2} sx={{textAlign: 'center'}}>{'You probably won\'t find an exact match, but pick one that roughly resembles your island.'}</Text>}
      {content}
    </Box>
  );
}
Example #17
Source File: update.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
CreateCustomtypeForm = ({
  title,
  isOpen,
  onSubmit,
  close,
  tabIds,
  allowDelete,
}: {
  title: string;
  isOpen: boolean;
  // eslint-disable-next-line @typescript-eslint/ban-types
  onSubmit: Function;
  close: () => void;
  tabIds: ReadonlyArray<string>;
  allowDelete: boolean;
}) => {
  return (
    <ModalFormCard
      omitFooter
      isOpen={isOpen}
      widthInPx="530px"
      formId={formId}
      close={close}
      cardProps={{ bodySx: { p: 0 } }}
      onSubmit={(values) => {
        onSubmit(values);
      }}
      initialValues={{
        id: "",
        actionType: null,
      }}
      validate={({ id }) => {
        if (!id) {
          return {
            id: "Tab ID is required",
          };
        }
        if (tabIds.includes(id.toLowerCase())) {
          return {
            id: "Tab exists already",
          };
        }
      }}
      content={{
        title,
      }}
    >
      {({ errors, values, setFieldValue }) => (
        <Box>
          <Box sx={{ px: 4, py: 4 }}>
            <InputBox
              name="id"
              label="Update Tab ID"
              placeholder="Tab"
              error={errors.id}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                setFieldValue("id", e.target.value.trim());
                setFieldValue("actionType", ActionType.UPDATE);
              }}
            />
            <Button
              type="button"
              sx={{ mt: 3, width: "100%" }}
              onClick={() => {
                if (values.id && values.id.length) {
                  onSubmit({
                    id: values.id,
                    actionType: ActionType.UPDATE,
                  });
                }
                close();
              }}
            >
              Save
            </Button>
          </Box>
          <Box
            as="hr"
            sx={{
              borderBottom: "none",
              // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
              borderTop: (theme) => `1px solid ${theme?.colors?.borders}`,
            }}
          />
          {allowDelete && (
            <Box sx={{ px: 4, py: 4 }}>
              <Heading as="h4">Delete this tab?</Heading>
              <Text as="p" color="textClear" sx={{ mt: 2 }}>
                This action cannot be undone.
              </Text>
              <Button
                type="button"
                variant="buttons.actionDelete"
                sx={{ mt: 3 }}
                onClick={() => {
                  onSubmit({
                    id: values.id,
                    actionType: ActionType.DELETE,
                  });
                  close();
                }}
              >
                Yes, delete tab
              </Button>
            </Box>
          )}
        </Box>
      )}
    </ModalFormCard>
  );
}
Example #18
Source File: index.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
ReviewModal: React.FunctionComponent = () => {
  const libraries = useContext(LibrariesContext);

  const {
    env,
    isReviewLoading,
    isLoginModalOpen,
    hasSendAReview,
    hasDoneTheOnboarding,
    customTypeCount,
  } = useSelector((store: SliceMachineStoreType) => ({
    env: getEnvironment(store),
    isReviewLoading: isLoading(store, LoadingKeysEnum.REVIEW),
    isLoginModalOpen: isModalOpen(store, ModalKeysEnum.LOGIN),
    hasSendAReview: userHasSendAReview(store),
    hasDoneTheOnboarding: userHasDoneTheOnboarding(store),
    customTypeCount: selectCustomTypeCount(store),
  }));

  const { skipReview, sendAReview, startLoadingReview, stopLoadingReview } =
    useSliceMachineActions();

  const sliceCount =
    libraries && libraries.length
      ? libraries.reduce((count, lib) => {
          if (!lib) {
            return count;
          }

          return count + lib.components.length;
        }, 0)
      : 0;

  const userHasCreateEnoughContent = sliceCount >= 1 && customTypeCount >= 1;

  const onSendAReview = (rating: number, comment: string): void => {
    startLoadingReview();
    // eslint-disable-next-line @typescript-eslint/no-floating-promises
    Tracker.get().trackReview(env.framework, rating, comment);
    sendAReview();
    stopLoadingReview();
  };

  const validateReview = ({ rating }: { rating: number; comment: string }) => {
    if (!rating) {
      return { id: "Please Choose a rating" };
    }
  };

  return (
    <SliceMachineModal
      isOpen={
        userHasCreateEnoughContent && !hasSendAReview && hasDoneTheOnboarding
      }
      shouldCloseOnOverlayClick={false}
      onRequestClose={() => skipReview()}
      closeTimeoutMS={500}
      contentLabel={"Review Modal"}
      portalClassName={"ReviewModal"}
      style={{
        content: {
          display: "flex",
          position: "initial",
          padding: "none",
          top: "initial",
          left: "initial",
          minHeight: "initial",
        },
        overlay: {
          top: "initial",
          left: "initial",
          right: 32,
          bottom: 32,
          position: "absolute",
          height: "fit-content",
          width: "fit-content",
          backgroundColor: "unset",
        },
      }}
    >
      <Formik
        validateOnMount
        validateOnChange
        initialValues={{
          rating: 0,
          comment: "",
        }}
        validate={validateReview}
        onSubmit={(values) => {
          onSendAReview(values.rating, values.comment);
        }}
      >
        {({ isValid, values }) => (
          <Form id="review-form">
            <Card>
              <Flex
                sx={{
                  p: "16px",
                  pl: 4,
                  bg: "headSection",
                  alignItems: "center",
                  justifyContent: "space-between",
                  borderRadius: "8px 8px 0px 0px",
                  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
                  borderBottom: (t) => `1px solid ${t.colors?.borders}`,
                }}
              >
                <Heading sx={{ fontSize: "20px", mr: 4 }}>
                  Give us your opinion
                </Heading>
                <Close
                  type="button"
                  onClick={() => skipReview()}
                  data-cy="close-review"
                />
              </Flex>
              <Flex
                sx={{
                  flexDirection: "column",
                  padding: "16px 24px 32px",
                  bg: "headSection",
                }}
              >
                <Text variant={"xs"} as={"p"} sx={{ maxWidth: 302, mb: 3 }}>
                  Overall, how difficult was your first experience using
                  Slicemachine?
                </Text>
                <Box
                  mb={2}
                  sx={{
                    display: "flex",
                    flex: 1,
                    justifyContent: "space-between",
                  }}
                >
                  <Text variant={"xs"} as={"p"}>
                    Very difficult
                  </Text>
                  <Text variant={"xs"} as={"p"}>
                    Very easy
                  </Text>
                </Box>
                <Field name={"rating"} component={SelectReviewComponent} />
                <Field
                  name={"comment"}
                  type="text"
                  placeholder={
                    "Sorry about that! What did you find the most difficult?"
                  }
                  as={Textarea}
                  autoComplete="off"
                  className={
                    values.rating >= 5 || values.rating === 0 ? "hidden" : ""
                  }
                  sx={{
                    height: 80,
                    opacity: 1,
                    mb: 3,
                    transition: "all 300ms",
                    "&.hidden": {
                      height: 0,
                      opacity: 0,
                      mb: 0,
                      p: 0,
                      border: "none",
                    },
                  }}
                />
                <Button
                  form={"review-form"}
                  type="submit"
                  disabled={!isValid || isReviewLoading || isLoginModalOpen}
                >
                  Submit
                </Button>
              </Flex>
            </Card>
          </Form>
        )}
      </Formik>
    </SliceMachineModal>
  );
}
Example #19
Source File: index.tsx    From slice-machine with Apache License 2.0 4 votes vote down vote up
function ModalCard<Values>({
  children,
  close,
  isOpen,
  formId,
  validate,
  onSubmit,
  widthInPx,
  initialValues,
  content: { title },
  cardProps,
  omitFooter = false,
  isLoading = false,
  buttonLabel = "Save",
  dataCy,
}: ModalCardProps<Values>): JSX.Element {
  Modal.setAppElement("#__next");
  return (
    <SliceMachineModal
      isOpen={isOpen}
      shouldCloseOnOverlayClick
      onRequestClose={close}
      contentLabel={title}
      style={{
        content: {
          width: widthInPx || "900px",
        },
      }}
    >
      <Formik
        validateOnChange
        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
        initialValues={initialValues}
        // eslint-disable-next-line @typescript-eslint/no-unsafe-return
        validate={(values) => (validate ? validate(values) : undefined)}
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        onSubmit={(values) => {
          onSubmit(values);
          close();
        }}
      >
        {({
          isValid,
          isSubmitting,
          values,
          errors,
          touched,
          setFieldValue,
        }) => (
          <Form id={formId} {...(dataCy ? { "data-cy": dataCy } : null)}>
            <Card
              borderFooter
              footerSx={{ p: 3 }}
              bodySx={{ px: 4, py: 4 }}
              sx={{ border: "none" }}
              {...cardProps}
              Header={({ radius }: { radius: string | number }) => (
                <Flex
                  sx={{
                    p: "16px",
                    pl: 4,
                    bg: "headSection",
                    alignItems: "center",
                    justifyContent: "space-between",
                    borderTopLeftRadius: radius,
                    borderTopRightRadius: radius,
                    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
                    borderBottom: (t) => `1px solid ${t.colors?.borders}`,
                  }}
                >
                  <Heading sx={{ fontSize: "20px" }}>{title}</Heading>
                  <Close type="button" onClick={close} />
                </Flex>
              )}
              Footer={
                !omitFooter ? (
                  <Flex sx={{ alignItems: "space-between" }}>
                    <Box sx={{ ml: "auto" }} />
                    <ThemeButton
                      mr={2}
                      type="button"
                      onClick={close}
                      variant="secondary"
                    >
                      Cancel
                    </ThemeButton>
                    <Button
                      form={formId}
                      type="submit"
                      disabled={!isValid || isSubmitting || isLoading}
                      isLoading={isLoading}
                    >
                      {buttonLabel}
                    </Button>
                  </Flex>
                ) : null
              }
            >
              {children({
                isValid,
                isSubmitting,
                values,
                errors,
                touched,
                setFieldValue,
              })}
            </Card>
          </Form>
        )}
      </Formik>
    </SliceMachineModal>
  );
}
Example #20
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>
  )
}
Example #21
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 #22
Source File: ChatWindow.tsx    From chat-window with MIT License 4 votes vote down vote up
render() {
    const {
      title = 'Welcome!',
      subtitle = 'How can we help you?',
      newMessagePlaceholder = 'Start typing...',
      emailInputPlaceholder = 'Enter your email',
      agentAvailableText = "We're online right now!",
      agentUnavailableText = "We're away at the moment.",
      newMessagesNotificationText = 'View new messages',
      companyName,
      isMobile = false,
      isCloseable = true,
      showAgentAvailability = false,
      accountId,
      baseUrl,
    } = this.props;
    const {
      customerId,
      messages = [],
      availableAgents = [],
      isSending,
      isOpen,
      isTransitioning,
      isGameMode,
      shouldDisplayNotifications,
      shouldDisplayBranding,
    } = this.state;

    if (isGameMode) {
      return (
        <EmbeddedGame
          isMobile={isMobile}
          onLoadGame={this.handleGameLoaded}
          onLeaveGame={this.handleLeaveGameMode}
        />
      );
    }

    if (isTransitioning) {
      return null; // TODO: need to figure out the best way to handle this
    }

    if (!isOpen && shouldDisplayNotifications) {
      return (
        <UnreadMessages
          messages={this.getUnreadMessages()}
          newMessagesNotificationText={newMessagesNotificationText}
          isMobile={isMobile}
          onOpen={this.emitOpenWindow}
        />
      );
    }

    // FIXME: only return null for versions of the chat-widget after v1.1.0
    if (!isOpen && !this.isOnDeprecatedVersion()) {
      return null;
    }

    const shouldAskForEmail = this.askForEmailUpfront();
    const hasAvailableAgents = availableAgents.length > 0;
    const replies = this.getQuickReplies(messages);

    return (
      <Flex
        className={isMobile ? 'Mobile' : ''}
        sx={{
          bg: 'background',
          flexDirection: 'column',
          height: '100%',
          width: '100%',
          flex: 1,
        }}
      >
        <Box sx={{bg: 'primary', position: 'relative'}}>
          <Box pt={3} pb={showAgentAvailability ? 12 : 16} px={20}>
            {/* TODO: wrap in a button element */}
            {isCloseable && !this.isOnDeprecatedVersion() && (
              <CloseIcon
                className="CloseIcon"
                width={24}
                height={24}
                onClick={this.emitCloseWindow}
              />
            )}
            <Heading
              as="h2"
              className="Papercups-heading"
              sx={{color: 'background', my: 1, mr: 12}}
            >
              {title}
            </Heading>
            <Text sx={{color: 'offset'}}>{subtitle}</Text>
          </Box>

          {showAgentAvailability && (
            <AgentAvailability
              hasAvailableAgents={hasAvailableAgents}
              agentAvailableText={agentAvailableText}
              agentUnavailableText={agentUnavailableText}
            />
          )}
        </Box>

        <Box
          p={3}
          sx={{
            flex: 1,
            boxShadow: 'rgba(0, 0, 0, 0.2) 0px 21px 4px -20px inset',
            overflowY: 'scroll',
          }}
        >
          {messages.map((msg, key) => {
            // Slight hack
            const next = messages[key + 1];
            const isLastInGroup = next
              ? msg.customer_id !== next.customer_id
              : true;
            const shouldDisplayTimestamp = key === messages.length - 1;
            // NB: `msg.type` doesn't come from the server, it's just a way to
            // help identify unsent messages in the frontend for now
            const isMe = isCustomerMessage(msg, customerId);

            return (
              <motion.div
                key={key}
                initial={{opacity: 0, x: isMe ? 2 : -2}}
                animate={{opacity: 1, x: 0}}
                transition={{duration: 0.2, ease: 'easeIn'}}
              >
                <ChatMessage
                  key={key}
                  message={msg}
                  isMe={isMe}
                  companyName={companyName}
                  isLastInGroup={isLastInGroup}
                  shouldDisplayTimestamp={shouldDisplayTimestamp}
                />
              </motion.div>
            );
          })}

          {replies && replies.length > 0 ? (
            <QuickReplies
              replies={replies}
              onSelect={this.handleSelectQuickReply}
            />
          ) : null}

          <div ref={(el) => (this.scrollToEl = el)} />
        </Box>

        {shouldDisplayBranding && <PapercupsBranding />}

        <Box
          px={2}
          sx={{
            borderTop: '1px solid rgb(230, 230, 230)',
            // TODO: only show shadow on focus TextArea below
            boxShadow: 'rgba(0, 0, 0, 0.1) 0px 0px 100px 0px',
          }}
        >
          {/*
            NB: we use a `key` prop here to force re-render on open so
            that the input will auto-focus appropriately
          */}
          <ChatFooter
            key={isOpen ? 1 : 0}
            accountId={accountId}
            baseUrl={baseUrl}
            placeholder={newMessagePlaceholder}
            emailInputPlaceholder={emailInputPlaceholder}
            isSending={isSending}
            shouldRequireEmail={shouldAskForEmail}
            onSendMessage={this.handleSendMessage}
          />
        </Box>

        <img
          alt="Papercups"
          src="https://papercups.s3.us-east-2.amazonaws.com/papercups-logo.svg"
          width="0"
          height="0"
        />
      </Flex>
    );
  }