@chakra-ui/react#VStack TypeScript Examples

The following examples show how to use @chakra-ui/react#VStack. 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: create.tsx    From next-crud with MIT License 7 votes vote down vote up
UserCreate: NextPage = () => {
  const toast = useToast()
  const { replace } = useRouter()

  const onSubmit = async (values: IFormValues) => {
    try {
      await fetch(`/api/users`, {
        method: 'POST',
        body: JSON.stringify(values),
        headers: {
          'Content-Type': 'application/json',
        },
      })
      toast({
        status: 'success',
        description: 'User successfully created',
        duration: 2000,
      })
      replace('/users')
    } catch (e) {
      toast({
        status: 'error',
        description: 'Failed to create user',
        duration: 2000,
      })
    }
  }

  return (
    <Layout title="User create" backRoute="/users">
      <VStack spacing={4} width="100%">
        <Heading>User create</Heading>
        <UserForm onSubmit={onSubmit} />
      </VStack>
    </Layout>
  )
}
Example #2
Source File: NutritionFactsFormFields.tsx    From calories-in with MIT License 6 votes vote down vote up
function NutritionFactsFormFields({ nameInputRef, canEdit, ...rest }: Props) {
  return (
    <Box {...rest}>
      <VStack spacing={2} alignItems="stretch">
        <VStack spacing={2}>
          <StatFormField
            hasDivider={false}
            isEmphasized={true}
            name="servingSizeInGrams"
            label="Serving size"
            isRequired={true}
            inputType="nutritionValue"
            isReadOnly={!canEdit}
          />

          <StatFormField
            name="energy"
            isCaption={true}
            isValueBold={true}
            isEmphasized={true}
            label="Calories"
            inputType="nutritionValue"
            nutritionValueUnit="kcal"
            isRequired={true}
            isReadOnly={!canEdit}
          />
        </VStack>

        <StatsFormFields canEdit={canEdit} />
      </VStack>
    </Box>
  )
}
Example #3
Source File: ScreenAnnounceView.tsx    From takeout-app with MIT License 6 votes vote down vote up
AnnounceTime: React.FC<{ unix: number }> = ({ unix }) => {
  const [idx, setIdx] = React.useState(0);

  React.useEffect(() => {
    let i = 0;
    const interval = setInterval(() => {
      i = (i + 1) % TIME_ZONES.length;
      setIdx(i);
    }, 5000);
    return () => clearInterval(interval);
  }, []);

  const [tzName, tz] = TIME_ZONES[idx]!;

  const t = dayjs.unix(unix);
  return (
    <VStack>
      <HStack spacing="2vw">
        <AnnounceTimeItem tz={tzName} t={t.tz(tz)} />
        <AnnounceTimeItem tz={"UTC"} t={t.utc()} />
        <AnnounceTimeItem tz={"JST"} t={t.tz("Asia/Tokyo")} />
      </HStack>
    </VStack>
  );
}
Example #4
Source File: [id].tsx    From next-crud with MIT License 6 votes vote down vote up
UserCreate: NextPage<IProps> = ({ user }) => {
  const toast = useToast()
  const { replace } = useRouter()
  const queryClient = useQueryClient()

  const onSubmit = async (values: IFormValues) => {
    try {
      const userData = await fetch(`/api/users/${user.id}`, {
        method: 'PUT',
        body: JSON.stringify(values),
        headers: {
          'Content-Type': 'application/json',
        },
      }).then((res) => res.json())
      toast({
        status: 'success',
        description: 'User successfully updated',
        duration: 2000,
      })
      replace('/users')
      queryClient.setQueryData<
        InfiniteData<TPaginationResult<User>> | undefined
      >('users', (data) => {
        const page = data?.pages.find((page) =>
          page.data.some((userElem) => userElem.id === user.id)
        )
        if (page) {
          const elemIdx = page.data.findIndex((data) => data.id === user.id)
          page.data[elemIdx] = userData
        }

        return data
      })
    } catch (e) {
      toast({
        status: 'error',
        description: 'Failed to update user',
        duration: 2000,
      })
    }
  }

  return (
    <Layout title={user.username} backRoute="/users">
      <VStack spacing={4} width="100%">
        <Heading>User edition</Heading>
        <UserForm
          initialValues={{ username: user.username }}
          onSubmit={onSubmit}
        />
      </VStack>
    </Layout>
  )
}
Example #5
Source File: ScreenHeroFiller.tsx    From takeout-app with MIT License 6 votes vote down vote up
ScreenHeroFiller: React.FC = () => {
  return (
    <Center w="100%" h="100%">
      <VStack spacing="3vw" css={{ "& svg": { height: "auto", width: "100%" } }}>
        <Image src="/assets/hero_hamburger.webp" h="30vw" />
        <Logo />
      </VStack>
    </Center>
  );
}
Example #6
Source File: index.tsx    From ksana.in with Apache License 2.0 6 votes vote down vote up
export default function BlogIndex({ data }: IBlogProps) {
  return (
    <Layout>
      <MetaHead
        title="Beranda Blog | Ksana.in"
        description="Ksana.in adalah layanan pemendek tautan / URL yang gratis dan mudah untuk digunakan, buatan asli anak Indonesia"
      />

      <VStack spacing={4} textAlign="center" as="section" mt="32">
        <VStack spacing={4} textAlign="center">
          <Heading
            as="h1"
            fontWeight={700}
            fontSize={{ base: '3xl', sm: '4xl', md: '5xl' }}
            lineHeight={'110%'}
            color="orange.400"
          >
            Blog Ksana.in
          </Heading>
        </VStack>
      </VStack>

      <Container maxW={'4xl'} mx="auto" as="section" mt="8">
        <SimpleGrid columns={{ base: 1, md: 2 }} spacing={8} py={4}>
          {data &&
            data.length > 0 &&
            data.map((post: IPost) => <BlogCardPost key={post.slug} post={post} />)}
        </SimpleGrid>
      </Container>
    </Layout>
  )
}
Example #7
Source File: TrackVideo.tsx    From takeout-app with MIT License 6 votes vote down vote up
TrackOfflineView: React.FC = () => {
  return (
    <AspectRatio ratio={16 / 9}>
      <Center w="100%" h="100%">
        <VStack>
          <Box w="95px" h="95px" css={{ filter: "grayscale(1)" }}>
            <picture>
              <source type="image/webp" srcSet="/assets/hero_hamburger.webp" />
              <Image src="/assets/hero_hamburger.svg" w="100%" h="100%" alt="" />
            </picture>
          </Box>
          <Heading as="div" color={Colors.textMuted}>
            Offline...
          </Heading>
        </VStack>
      </Center>
    </AspectRatio>
  );
}
Example #8
Source File: card-skeleton.tsx    From portfolio with MIT License 6 votes vote down vote up
CardSkeleton = () => {
  const bgColor = useColorModeValue("white", "gray.900");
  const cards:number[] = [1, 2, 3, 4, 5, 6, 7, 8]

  return (
    <>
      {cards.map(id => {
        return (
          <Box
            key={id}
            size="xl"
            py={2}
            rounded="xl"
            borderWidth="1px"
            bg={bgColor}
          >
            <Stack isInline justifyContent="space-between" py={2} px={[2, 3]}>
              <Box width="100%">
                <HStack isInline justifyContent="space-between">
                  <Skeleton height="14px" width="40%" />
                  <Skeleton height="14px" width="20%" />
                </HStack>
                <VStack align="start" marginTop={2}>
                  <Skeleton height="8px" width="30%" />
                </VStack>
                <Box marginTop={2}>
                  <Skeleton height="8px" width="100%" />
                  <Stack spacing={2} mt={1} isInline alignItems="center">
                    <Skeleton height="8px" width="80%" />
                  </Stack>
                </Box>
              </Box>
            </Stack>
          </Box>
        );
      })}
    </>
  );
}
Example #9
Source File: EmptyList.tsx    From calories-in with MIT License 6 votes vote down vote up
function EmptyList({ onAddMeal }: Props) {
  return (
    <Center
      textAlign="center"
      bg="white"
      boxShadow="base"
      borderRadius={6}
      flex={1}
      p={6}
      flexDirection="column"
    >
      <VStack spacing={6}>
        <Text fontSize="xl" fontWeight="medium" textColor="gray.500">
          You haven't added any meals to this day yet
        </Text>
        <Text maxWidth="450px" mt={3} fontSize="md" textColor="gray.500">
          Days can be specific weekdays or just types of days. For example: a
          training or a rest day.
        </Text>
        <Button
          mt={3}
          onClick={onAddMeal}
          colorScheme="teal"
          variant="outline"
          size="md"
          leftIcon={<PlusStyled size={16} pointerEvents="none" />}
        >
          Add meal
        </Button>
      </VStack>
    </Center>
  )
}
Example #10
Source File: projects.tsx    From portfolio with MIT License 6 votes vote down vote up
Projects: React.FC<ProjectsProps> = ({ projects }) => {
  return (
    <VStack align="start" spacing={8}>
      <Header underlineColor={ORANGE} mt={0} mb={0}>
        Projects
      </Header>
      <AnimateSharedLayout>
        <SimpleGrid columns={1} spacing={4} mt={5} w="100%">
          {projects.map((project, index) => (
            <MotionBox whileHover={{ y: -5 }} key={index}>
              <ProjectCard
                key={project.id}
                title={project.title}
                description={project.desc}
                blurHash={project.blurHash}
                logo={project.logo}
                link={project.link}
                technologies={project.technologies}
              />
            </MotionBox>
          ))}
        </SimpleGrid>
      </AnimateSharedLayout>
    </VStack>
  );
}
Example #11
Source File: [slug].tsx    From ksana.in with Apache License 2.0 5 votes vote down vote up
export default function BlogDetail({ post }: IBlogDetail) {
  return (
    <Layout>
      <MetaHead title={`${post.title} | Ksana.in`} description={post.excerpt} />
      <Head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(makeBreadcrumbBlogSchema({ title: post.title, slug: post.slug }))
          }}
        ></script>
      </Head>

      <VStack spacing={4} textAlign="center" as="section" mt="32">
        <Breadcrumb>
          <BreadcrumbItem>
            <BreadcrumbLink href="/">Home</BreadcrumbLink>
          </BreadcrumbItem>

          <BreadcrumbItem>
            <BreadcrumbLink href="/blog">Blog</BreadcrumbLink>
          </BreadcrumbItem>

          <BreadcrumbItem isCurrentPage>
            <BreadcrumbLink href={`/blog/${post.slug}`}>{post.title}</BreadcrumbLink>
          </BreadcrumbItem>
        </Breadcrumb>

        <Container maxW={'4xl'} mx="auto" as="section" mt="8">
          <VStack spacing={4} textAlign="center" className="blog-detail">
            <Heading
              as="h1"
              fontWeight={700}
              fontSize={{ base: '3xl', sm: '4xl', md: '5xl' }}
              lineHeight={'110%'}
              color="orange.400"
            >
              {post.title}
            </Heading>
            <Button leftIcon={<HiClock />} colorScheme="gray" variant="solid" size="xs">
              {post.date}
            </Button>
          </VStack>
        </Container>
      </VStack>

      <Container maxW={'4xl'} mx="auto" as="section" mt="8">
        <div className="markdown">
          <div
            dangerouslySetInnerHTML={{
              __html: `${post.content}`
            }}
          ></div>
        </div>
      </Container>
    </Layout>
  )
}
Example #12
Source File: about.tsx    From portfolio with MIT License 5 votes vote down vote up
About = () => {
  const { colorMode } = useColorMode();

  return (
    <PageSlideFade>
      <StaggerChildren>
        <MotionBox>
          <Heading>
            <Flex alignItems="center">
              <Header underlineColor={TURQUOISE} mt={0} mb={0}>
                Career
              </Header>
              <Stack pl={3}>
                <Box as={BsFillBriefcaseFill} size="25px" />
              </Stack>
            </Flex>
          </Heading>
        </MotionBox>
        <VStack spacing={4} marginBottom={6} align="left" mx={[0, 0, 6]} mt={12}>
          {companies.map((company, index) => (
            <MotionBox whileHover={{ y: -5 }} key={index}>
              <Card
                key={index}
                title={company.title}
                role={company.role}
                skills={company.skills}
                period={company.period}
                logo={company.logo}
                colorMode={colorMode}
              />
            </MotionBox>
          ))}
        </VStack>
        <Heading>
          <Flex alignItems="center">
            <Header underlineColor={TURQUOISE} mt={0} mb={0}>
              Education
            </Header>
            <Stack pl={3}>
              <Box as={FaGraduationCap} size="25px" />
            </Stack>
          </Flex>
        </Heading>
        <VStack spacing={4} marginBottom={6} align="left" mx={[0, 0, 6]} mt={12}>
          {institutes.map((institute, index) => (
            <MotionBox whileHover={{ y: -5 }} key={index}>
              <Card
                key={index}
                title={institute.title}
                role={institute.role}
                skills={institute.skills}
                period={institute.period}
                logo={institute.logo}
                colorMode={colorMode}
              />
            </MotionBox>
          ))}
        </VStack>
      </StaggerChildren>
    </PageSlideFade>
  );
}
Example #13
Source File: Login.tsx    From takeout-app with MIT License 5 votes vote down vote up
Login: React.FC = () => {
  return (
    <Box w="100%" h="100%" minH="100vh" bgColor={Colors.bg} py={["20px", "20px", "20px", "165px"]}>
      <Container maxW={["auto", "auto", "auto", "780px"]} w="100%">
        <Flex
          direction={["column", "column", "column", "row"]}
          justifyContent="space-around"
          alignItems="top"
          mx="15px"
        >
          <Box maxW="331px" w="100%" mb="15px" display="flex" direction="row" alignItems="center">
            <picture>
              <source type="image/webp" srcSet="/assets/hero_hamburger.webp" />
              <Image src="/assets/hero_hamburger.svg" w="100%" />
            </picture>
          </Box>
          <Box>
            <VStack>
              <Box>
                <Heading
                  as="h1"
                  w={["100%", "100%", "360px", "360px"]}
                  h="auto"
                  color={Colors.main}
                  css={{ "& svg": { maxWidth: "100%", height: "auto" } }}
                >
                  <Logo />
                  <VisuallyHidden>RubyKaigi Takeout 2021 Streaming Login</VisuallyHidden>
                </Heading>
                <Flex direction="row-reverse">
                  <Box mr="-10px">
                    <StreamingLogo />
                  </Box>
                </Flex>
              </Box>
              <Box textAlign="center" pt="30px">
                <Text>
                  A paid ticket is required to access this virtual conference.{" "}
                  <Link isExternal href="https://ti.to/rubykaigi/takeout-2021" textDecoration="underline">
                    Register now.
                  </Link>
                </Text>
              </Box>
              <LoginForm />
            </VStack>
          </Box>
        </Flex>
      </Container>
    </Box>
  );
}
Example #14
Source File: skill-card.tsx    From portfolio with MIT License 5 votes vote down vote up
SkillCard = ({ name, image, link, description }) => {
  const { data, loading } = usePalette(image);

  return (
    <MotionBox variants={item}>
      <MotionBox whileHover={{ y: -5 }}>
        <Link href={link} isExternal>
          <HStack
            p={4}
            bg={useColorModeValue("white", "gray.800")}
            rounded="xl"
            borderWidth="1px"
            borderColor={useColorModeValue("gray.100", "gray.700")}
            w="100%"
            textAlign="left"
            align="start"
            spacing={4}
            _hover={{ shadow: "md" }}
          >
            <Box
              rounded="lg"
              p={2}
              position="relative"
              overflow="hidden"
              lineHeight={0}
              boxShadow="inset 0 0 1px 1px rgba(0, 0, 0, 0.015)"
            >
              <Box
                bg={data.lightVibrant}
                position="absolute"
                top={0}
                bottom={0}
                left={0}
                right={0}
                opacity={0.25}
              ></Box>
              {loading ? (
                <Skeleton height={26} width={26} rounded="md" />
              ) : (
                <Image
                  src={image}
                  height={26}
                  width={26}
                  layout="fixed"
                  rounded="md"
                />
              )}
            </Box>
            <VStack
              align="start"
              justify="flex-start"
              spacing={1}
              maxW="lg"
              h="100%"
            >
              <VStack spacing={0} align="start" flexGrow="1">
                <Text fontWeight="bold" fontSize="md" noOfLines={2}>
                  {name}
                </Text>
                <Text
                  fontSize="sm"
                  color={useColorModeValue("gray.500", "gray.200")}
                >
                  {description}
                </Text>
              </VStack>
            </VStack>
          </HStack>
        </Link>
      </MotionBox>
    </MotionBox>
  );
}
Example #15
Source File: Hero.tsx    From ksana.in with Apache License 2.0 5 votes vote down vote up
export function Hero() {
  const textColor = useColorModeValue('gray.500', 'gray.300')

  return (
    <Box w={'full'}>
      <SimpleGrid columns={{ base: 1, md: 2 }} spacing={10}>
        <Container maxW={'6xl'} as="section" mt="32">
          <VStack spacing={{ base: 8, md: 10 }} px={{ base: 8, md: 10 }}>
            <Heading
              as="h2"
              textAlign="center"
              fontWeight={700}
              fontSize={{ base: '4xl', sm: '5xl', md: '6xl' }}
              lineHeight={'110%'}
            >
              Pemendek tautan yang{' '}
              <Text color="orange.400" as="span">
                mudah
              </Text>{' '}
              dan{' '}
              <Text color="orange.400" as="span">
                gratis
              </Text>
            </Heading>

            <Text
              as="span"
              textAlign="center"
              color={textColor}
              fontSize={{ base: 'lg', sm: 'xl', md: '2xl' }}
              lineHeight={'110%'}
            >
              Percantik tautanmu, jadikan agar mudah diingat, bagikan ke orang lain dengan percaya
              diri
            </Text>

            <Button
              size="lg"
              rounded="full"
              px={6}
              color={'white'}
              bg="orange.400"
              _hover={{
                bg: 'orange.500'
              }}
              as={'a'}
              href={login}
              leftIcon={<HiPlay />}
            >
              Coba sekarang
            </Button>
          </VStack>
        </Container>

        <Flex as="section" mt={{ base: 0, md: 20 }} justifyContent="center">
          <Image
            width={400}
            height={400}
            src={'/images/illustrations/ill_by_manypixels.svg'}
            alt="Women with Internet"
            priority={true}
          />
        </Flex>
      </SimpleGrid>
    </Box>
  )
}
Example #16
Source File: repositories-list.tsx    From portfolio with MIT License 5 votes vote down vote up
RepositoriesList = () => {
  const [activeTab, setActiveTab] = React.useState("live");

  const [play] = useSound(lightswitch, {
    volume: 0.05,
    sprite: {
      on: [0, 300],
      off: [500, 300]
    }
  });

  const handleClick = (type) => {
    activeTab === "live" ? play({ id: "on" }) : play({ id: "off" });
    setActiveTab(type)
  }

  return (
    <PageSlideFade>
      <VStack align="start" spacing={3}>
        <HStack justifyContent={"space-between"} width={"100%"}>
          <Header underlineColor={TURQUOISE} mt={0} mb={0}>
            Open Source
          </Header>
          <HStack>
          <Tooltip hasArrow label="Live github repos" placement="top">
            <IconButton
              aria-label={"live"}
              size="md"
              colorScheme={"linkedin"}
              icon={<RiSignalTowerLine />}
              isActive={activeTab === "live"}
              onClick={() => handleClick('live')}
              {...iconProps}
            />
            </Tooltip>
          <Tooltip hasArrow label="Local github repos" placement="top">
            <IconButton
              aria-label={"live"}
              size="md"
              colorScheme={"linkedin"}
              icon={<RiWifiOffLine />}
              isActive={activeTab === "offline"}
              onClick={() => handleClick('offline')}
              {...iconProps}
            />
            </Tooltip>
          </HStack>
        </HStack>
        <Text
          color={useColorModeValue("gray.500", "gray.200")}
          textAlign="left"
        >
          This page lists some of the open source repositories I have published
          or contributed to.
        </Text>
      </VStack>
      {activeTab === "live" ? <LiveData /> : <OfflineData />}
    </PageSlideFade>
  );
}
Example #17
Source File: AppSettingsMenu.tsx    From ledokku with MIT License 5 votes vote down vote up
AppSettingsMenu = ({ app }: AppSettingsMenuProps) => {
  const location = useLocation();

  const selectedRoute = location.pathname.endsWith('/settings/ports')
    ? 'ports'
    : location.pathname.endsWith('/settings/domains')
    ? 'domains'
    : location.pathname.endsWith('/settings/advanced')
    ? 'advanced'
    : 'index';

  return (
    <VStack align="stretch">
      <Button
        variant="ghost"
        justifyContent="left"
        isActive={selectedRoute === 'ports'}
        as={Link}
        to={`/app/${app.id}/settings/ports`}
      >
        Port Management
      </Button>
      <Button
        variant="ghost"
        justifyContent="left"
        isActive={selectedRoute === 'domains'}
        as={Link}
        to={`/app/${app.id}/settings/domains`}
      >
        Domains
      </Button>
      <Button
        variant="ghost"
        justifyContent="left"
        isActive={selectedRoute === 'advanced'}
        as={Link}
        to={`/app/${app.id}/settings/advanced`}
      >
        Advanced
      </Button>
    </VStack>
  );
}
Example #18
Source File: index.tsx    From calories-in with MIT License 5 votes vote down vote up
function Content({ onClose }: Props) {
  const [blob, setBlob] = useState<Blob>()
  const [url, setUrl] = useState<string>()
  const dietForm = useDietForm()

  const onUpdate = useCallback((blob: Blob, url: string) => {
    setBlob(blob)
    setUrl(url)
  }, [])

  function onViewInBrowser() {
    window.open(url, '_blank')
  }

  return (
    <ModalContent>
      <ModalHeader fontWeight="normal">
        Export{' '}
        <Text as="span" fontWeight="bold">
          {dietForm.name}
        </Text>
      </ModalHeader>
      <ModalCloseButton />
      <ModalBody px={0}>
        <Exporter onUpdate={onUpdate} />
      </ModalBody>

      <ModalFooter>
        <VStack spacing={3} width="100%">
          {blob && url && (
            <DownloadButton
              blob={blob}
              onClose={onClose}
              label="Download"
              isFullWidth={true}
              fileName={dietForm.name}
              isLoading={blob === undefined}
            />
          )}
          {blob && url && (
            <Button
              mr={3}
              variant="outline"
              colorScheme="teal"
              onClick={onViewInBrowser}
              isFullWidth={true}
            >
              View in browser
            </Button>
          )}

          <Button isFullWidth={true} variant="solid" onClick={onClose}>
            Close
          </Button>
        </VStack>
      </ModalFooter>
    </ModalContent>
  )
}
Example #19
Source File: TotalStats.tsx    From ksana.in with Apache License 2.0 5 votes vote down vote up
export function TotalStats({ data }: ITotalStatsProps) {
  const bgBox = useColorModeValue('white', 'gray.800')
  const sum = data.reduce((a: number, b: IUrl) => a + b.hit, 0)

  return (
    <>
      {data && data.length > 0 ? (
        <Stack direction="row" justifyContent="center">
          <VStack
            w="full"
            bg={bgBox}
            boxShadow={'2xl'}
            rounded={'md'}
            justifyContent="center"
            textAlign="center"
            overflow={'hidden'}
            py="2"
            position="relative"
          >
            <Text fontSize="xs">Total Tautan</Text>
            <Text fontSize="2xl" color="orange.400" fontWeight="700" zIndex="1">
              {new Intl.NumberFormat('id-ID').format(data.length)}
            </Text>

            <IconButton
              aria-label="Tautan"
              bg="orange.400"
              color="white"
              borderRadius="3xl"
              position="absolute"
              bottom="-.2em"
              right="-.2em"
              opacity="0.5"
              w="12"
              h="12"
              margin="0"
              fontSize="4xl"
              zIndex="0"
              icon={<HiLink />}
            />
          </VStack>
          <VStack
            w="full"
            bg={bgBox}
            boxShadow={'2xl'}
            rounded={'md'}
            justifyContent="center"
            textAlign="center"
            overflow={'hidden'}
            py="2"
            position="relative"
          >
            <Text fontSize="xs">Total Kunjungan</Text>
            <Text fontSize="2xl" color="orange.400" fontWeight="700" zIndex="1">
              {new Intl.NumberFormat('id-ID').format(sum)}
            </Text>

            <IconButton
              aria-label="Tautan"
              bg="orange.400"
              color="white"
              borderRadius="3xl"
              position="absolute"
              bottom="-.2em"
              right="-.2em"
              opacity="0.5"
              w="12"
              h="12"
              margin="0"
              fontSize="4xl"
              zIndex="0"
              icon={<HiChartBar />}
            />
          </VStack>
        </Stack>
      ) : null}
    </>
  )
}
Example #20
Source File: Hustlers.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
Hustlers = () => {
    const [hustlers, setHustlers] = React.useState<any>();

    useEffect(() => {
        if (!(window.ethereum as any)?.selectedAddress) return;

        fetch(`https://api.dopewars.gg/wallets/${ethers.utils.getAddress(
            (window.ethereum as any).selectedAddress,
          )}/hustlers`).then(res => res.json()).then(res => setHustlers(res));
    }, []);
    
    return (
        <div>
            {hustlers ? <SimpleGrid columns={2} spacing={5} paddingBottom="8">
                {
                    hustlers.map((hustler: any, i: number) =>
                        <VStack key={i}>
                            <Text paddingBottom="0px">
                                {hustler.id} {hustler.name ? " - " + hustler.name : ""}
                            </Text>
                            <object width="70%" type="image/svg+xml" data={hustler.svg} />
                            { localStorage.getItem(`gameSelectedHustler_${(window.ethereum as any).selectedAddress}`) !== hustler.id ? <Popover>
                                <PopoverTrigger>
                                    <Button variant="primary">
                                        Set as selected hustler
                                    </Button>
                                </PopoverTrigger>
                                <PopoverContent>
                                    <PopoverArrow />
                                    <PopoverCloseButton />
                                    <PopoverHeader>Are you sure?</PopoverHeader>
                                    <PopoverBody>The game needs to be reloaded in order to modify your selected hustler</PopoverBody>
                                    <PopoverFooter>
                                        <Button variant="primary" onClick={() => {
                                            localStorage.setItem(`gameSelectedHustler_${(window.ethereum as any).selectedAddress}`, hustler.id);
                                            window.location.reload();
                                        }}>
                                            Confirm
                                        </Button>
                                    </PopoverFooter>
                                </PopoverContent>
                            </Popover> : undefined }
                        </VStack>
                    )
                }
            </SimpleGrid> : <Center padding="4">
                    <Spinner size="lg"/>
                </Center>
            }
            <Center>
                <a href="/inventory">
                    <Button variant="primary">
                        Details
                    </Button>
                </a>
            </Center>
        </div>
    )
}
Example #21
Source File: ElementDescription.tsx    From engine with MIT License 5 votes vote down vote up
ElementDescription: view = ({
  selectedId = observe.selectedElement.id,
  path = observe.selectedElement.path,
  element = observe.structure.elements[arg.selectedId],
  code = observe.structure.code[arg.selectedId],
  showV2 = update.showV2,
  getShowV2 = get.showV2,
}) => {
  if (!element) {
    return;
  }

  try {
    code = (prettier as any).format(code, {
      parser: "typescript",
      plugins: prettierPlugins,
    });
  } catch (e) {
    code = code || "loading code..";
  }
  return (
    <Box p="4" overflowY="scroll" h="100vh">
      <Box mb="4">
        <VStack align="stretch" spacing={1} bg="gray.400" p="4">
          <Text>
            <Badge color={element.type === "view" ? "green" : "purple"}>
              {element.type}
            </Badge>
          </Text>
          <Text fontSize="xl" fontWeight="bold">
            {" "}
            {element.meta.name}
          </Text>
          <Text>{element.meta.relativeFilePath}</Text>
          <Badge
            onClick={() => showV2.set(!!!getShowV2.value())}
            position="absolute"
            colorScheme="green"
            right="3"
            top="3"
            cursor="pointer"
          >
            Toggle V2
          </Badge>
        </VStack>
      </Box>
      <Box mb="4">
        <Heading size="sm" mb="2">
          Header
        </Heading>

        {element.params.type === OperationTypes.STRUCT && (
          <Operations
            value={element.params.value}
            path={path}
            id={selectedId}
          />
        )}
      </Box>
      <Box mb="2">
        <Heading size="sm" mb="2">
          Body
        </Heading>
        <CodeEditor
          value={code}
          language="typescript"
          placeholder="loading code.."
          onChange={(evn) => {}}
          padding={15}
          style={{
            fontSize: 12,
            backgroundColor: "#f5f5f5",
            fontFamily:
              "ui-monospace,SFMono-Regular,SF Mono,Consolas,Liberation Mono,Menlo,monospace",
          }}
        />
      </Box>
    </Box>
  );
}
Example #22
Source File: IntroStepper.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
HasHustler = (props: Props) => {
    const [ page, setPage ] = useState(0);
    const [ hustlerIndex, setHustlerIndex ] = useState(0);

    useEffect(() => {
        localStorage.setItem(`gameSelectedHustler_${(window.ethereum as any).selectedAddress}`, props.hustlerData[hustlerIndex].id);
    }, [hustlerIndex, props.hustlerData]);

    const pages: Array<Page> = [
        {
            heading: "Hey, seems like it's your first time here! Let's get you up and running with the basics.",
            text: `Cool, you seem to already have a hustler, let's get you and your hustler set up.
            Which hustler do you wanna play with?`,
            children: <>
                <Select onChange={(e) => setHustlerIndex(Number.parseInt(e.target.value))}>
                    {props.hustlerData.map((hustler: any, i: number) => <option key={i} value={i}>{hustler.id} {hustler.name ? " - " + hustler.name : ""}</option>)}
                </Select><br /><object type="image/svg+xml" data={props.hustlerData[hustlerIndex].svg} />
            </>,
        },
        // {
        //     heading: "Where do you wanna start out your journey?",
        //     text: `There are many cities available in the game, you can visit 'em all whenever you want but you can
        //     choose one to start with.`
        // }
    ]

    const handleNext = () => {
        pages[page].onNext && pages[page].onNext!();
        if (page === pages.length - 1)
        {
            props.manager.events.emit('game');
            return;
        }

        setPage(page + 1);
    }

    return (
        <VStack>
            <Heading>
                {pages[page].heading}
            </Heading>
            <br />
            <Text>
                {pages[page].text}
            </Text>
            {pages[page].children}
            <HStack style={footerButtonsStyle}>
                <Button onClick={() => props.manager.events.emit('game')}>
                    {page === pages.length - 1 ? "Finish" : "DGAF"}
                </Button>
                {page > 0 ? <Button onClick={() => setPage(page - 1)}>
                    Go back
                </Button> : undefined}
                {page < pages.length - 1 ? <Button onClick={handleNext}>
                    Next
                </Button> : undefined}
            </HStack>
        </VStack>
    );
}
Example #23
Source File: index.tsx    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
export default function Home() {
  const { isError, gardens, error } = useDendronGardens();
  if (isError) return <div>failed to load: {JSON.stringify(error)}</div>;
  let extra: any;
  // if (_.isEmpty(gardens)) {
  //   extra = <Button>New Garden from Git</Button>;
  // }
  if (_.isEmpty(gardens)) {
    extra = (
      <Box maxW="32rem">
        <VStack spacing={4} align="stretch">
          <Center>
            <Heading mb={4}>Welcome to Dendron</Heading>
          </Center>
          <Text fontSize="xl">
            If you haven&apos;t already done so, you can install Dendron by following
            the instructions &nbsp;
            <Link
              href="https://dendron.so/notes/678c77d9-ef2c-4537-97b5-64556d6337f1.html"
              isExternal
            >
              here
            </Link>
          </Text>
          <Button>Publish a new site from Git (coming soon) </Button>
        </VStack>
      </Box>
    );
  }
  return (
    <>
      <Head>
        <title>Dendron</title>
        <link rel="icon" href="/favicon.ico" />
        <script type="text/javascript" src="/static/memberful.js" />
      </Head>

      <Grid justifyContent="center">{extra}</Grid>
    </>
  );
}
Example #24
Source File: EventWelcome.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
Stepper = (props: Props) => {
    const [ page, setPage ] = useState(0);
    const [ hustlerIndex, setHustlerIndex ] = useState(0);

    const pages: Array<Page> = [
        {
            heading: "Welcome to Dope Wars!",
            text: '',
            children: <>
                <Text>
                    Welcome OG Hustlers to the beginnings of our Dope City Murderverse! What we have to show is the culmination of untiring efforts from our developers, artists, musicians, and YOU, our community. 
                    It represents the love for what we stand for, the values that we live by, and our unwillingness to waver from them. We are web3. We are hip-hop culture. 
                    We are free minds. We are from all walks of life that have come together for this unified purpose. We are hustlers. What does it represent for you? 
                </Text>
                <Text>
                    Thank you so much for your support. 
                    We felt it was completely necessary to invite you all to our first stress test of Dope City, where you can interact with other hustlers by chatting, flexing your gear, and figuring out how to stack PAPER.  
                    It is a glimpse into a world that has become entrenched in a battle for glory, a struggle for reputation, and a fight for leadership. Welcome to Dope Wars. How will the story unfold?
                </Text>
        
            </>
        },
        {
            heading: "Message from the dev ?",
            text: '',
            children: <>
                <Text>
                    Thanks for joining us today everyone, it is with true joy and proudness that we're presenting you the current state of game!
                </Text>
                <Text>
                    However, please note that this is not the final state of the game and that some parts are still unfinished and *buggy*, so please don't expect
                    the best performance and accuracy. After all, this is still just **fun** stress test so don't be afraid to play around with the game and see what you can do (break)! 
                </Text>
                <Text>
                    Anyway, I'm not gonna bother you anymore so yeah... have fun!
                </Text>
            </>
        }
    ]

    const handleNext = () => {
        pages[page].onNext && pages[page].onNext!();
        if (page === pages.length - 1)
        {
            props.manager.events.emit('game');
            return;
        }

        setPage(page + 1);
    }

    return (
        <VStack>
            <Heading>
                {pages[page].heading}
            </Heading>
            <Text>
                {pages[page].text}
            </Text>
            {pages[page].children}
            <HStack style={footerButtonsStyle}>
                <Button onClick={() => props.manager.events.emit('game')}>
                    {page === pages.length - 1 ? "Finish" : "DGAF"}
                </Button>
                {page > 0 ? <Button onClick={() => setPage(page - 1)}>
                    Go back
                </Button> : undefined}
                {page < pages.length - 1 ? <Button onClick={handleNext}>
                    Next
                </Button> : undefined}
            </HStack>
        </VStack>
    );
}
Example #25
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function Content({
  onClose,
  mealName,
  mealForm,
  searchInputRef,
  onSelectedFoods,
  canSelect,
}: Props) {
  const selection = useSelection<Food>()
  const listRef = useRef<FoodsListMethods>(null)
  const foodEvents = useFoodEvents({ listRef, selection })

  const foodsListModalDisclosure = useDisclosure()
  const importFoods = useImportFoods({ foodsListModalDisclosure })
  const [foodsFilter] = useState(loadFoodsFilter)

  function onAdd() {
    onSelectedFoods && onSelectedFoods(selection.selectedItems, mealName)
  }

  return (
    <DrawerContent>
      <DrawerCloseButton />
      <Header mealForm={mealForm} mealName={mealName} canSelect={canSelect} />

      <DrawerBody overflow="hidden">
        <VStack
          width="100%"
          height="100%"
          spacing={canSelect ? 3 : 6}
          alignItems="stretch"
        >
          <Flex>
            <Text textColor="gray.500" size="lg" mr={1}>
              Need more foods?
            </Text>
            <Button
              variant="link"
              colorScheme="teal"
              onClick={foodEvents.onCreateFood}
            >
              Create a new food
            </Button>
          </Flex>

          {canSelect && <SelectedFoodsList selection={selection} />}

          <FoodsFilterStoreProvider initialFilter={foodsFilter}>
            <FoodsList
              ref={listRef}
              searchInputRef={searchInputRef}
              selection={selection}
              flex={1}
              onFoodPreview={foodEvents.onPreviewFood}
              itemUsageType={canSelect ? 'selectOrPreview' : 'previewOnly'}
            />
          </FoodsFilterStoreProvider>
        </VStack>
      </DrawerBody>

      <DrawerFooter justifyContent={canSelect ? 'flex-end' : 'space-between'}>
        {!canSelect && (
          <MenuButtons
            onImport={importFoods.onImport}
            onExport={foodsListModalDisclosure.onOpen}
          />
        )}

        <HStack spacing={3}>
          <Button variant="solid" size="md" onClick={onClose}>
            Close
          </Button>
          {canSelect && (
            <Tooltip
              isActive={!mealForm}
              delay={300}
              label="You can add more later"
            >
              <Button
                isDisabled={selection.selectedItems.length === 0}
                colorScheme="teal"
                onClick={onAdd}
              >
                {mealForm ? 'Add selected foods' : 'Select foods'}
              </Button>
            </Tooltip>
          )}
        </HStack>
      </DrawerFooter>

      <FoodModal
        isOpen={foodEvents.foodModalDisclosure.isOpen}
        onClose={foodEvents.foodModalDisclosure.onClose}
        onFoodCreatedOrUpdated={foodEvents.onFoodCreatedOrUpdated}
        onFoodDeleted={foodEvents.onFoodDeleted}
        food={foodEvents.food}
      />

      <FoodsListModal
        isOpen={foodsListModalDisclosure.isOpen}
        onClose={foodsListModalDisclosure.onClose}
        foodsToImport={importFoods.foodsToImport}
      />
    </DrawerContent>
  )
}
Example #26
Source File: home.tsx    From video-chat with MIT License 4 votes vote down vote up
Home = observer(() => {
  const navigate  = useNavigate();
  const [onlineUsers, setOnlineUsers] = useState<string[]>([])
  const [name, setName] = useState('')
  const { setUserId, userId, username, setUsername, currentCall } = useStore()
  const socketRef = useRef<Socket>()
  const [isRinging, setIsRinging] = useState<boolean>(false)
  const [isReceivingCall, setIsReceivingCall] = useState<boolean>(false)
  const [callerData, setCallerData] = useState<CallerData>()

  const callUser = (id: string) => {
    setIsRinging(true)
    const peer = new Peer({ initiator: true, trickle: false, stream: currentCall.myStream })

    peer.on('signal', (data: SignalData) => {
      socketRef.current?.emit('call-user', { from: userId, signalData: data, to: id})
    })

    peer.on("stream", stream => {
      currentCall.setStream(stream)
      peer.on('close', () => navigate('/'))
      currentCall.createConnection('', id)

      const roomId = v4();
      socketRef.current?.emit("join-room", { roomId: roomId, to: id })
      navigate(`/meet?id=${roomId}`)
    });

    socketRef.current?.on('call-answer', (data) => {
      setIsRinging(false)
      peer.signal(data.signalData)

      toast(`Call accepted from ${data.from}`, {type: 'success'})
    })

    socketRef.current?.on("call-refused", (data) => {
      setIsRinging(false)
      toast(`Call refused from ${data.from}`, {type: 'warning'})
    })
  }

  const answerCall = () => {
    if(!callerData?.signalData || !callerData?.from) return 
    const peer = new Peer({ initiator: false, trickle: false, stream: currentCall.myStream })

    peer.on("signal", data => {
      socketRef.current?.emit("answer-call", { signalData: data, to: callerData.from, from: userId })
    })

    peer.on("stream", stream => {
      currentCall.setStream(stream)
    });

    peer.signal(callerData?.signalData);

    setIsReceivingCall(false)

    socketRef.current?.on("join-room", (data) => {
      peer.on('close', () => navigate('/'))
      currentCall.createConnection('', callerData.from ?? '')
      navigate(`/meet?id=${data.roomId}`)
    })
  }

  const refuseCall = () => {
    socketRef.current?.emit("refuse-call", { to: callerData?.from, from: userId })
    setCallerData({})
    setIsReceivingCall(false)
  }

  const handleChangeName = (event: FormEvent) => {
    event.preventDefault()
    setUsername(name)
  }

  useEffect(() => {
    if (!username) return

    socketRef.current = io("http://localhost:8000");

    socketRef.current?.on('me', (id) => {
      setUserId(id);
    });

    socketRef?.current?.on('allUsers', (userList) => {
      setOnlineUsers(userList)
    })

    socketRef.current?.on("call", (data) => {
      setIsReceivingCall(true)
      setCallerData(data)
    })

    return () => { socketRef?.current?.disconnect() }
  }, [username])

  useEffect(() => {
    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then((currentStream) => {
      currentCall.setMyStream(currentStream);
    });
  }, [])

  return (
    <Page>
      <LoadingOverlay loading={isRinging}/>
      <ReceivingCallOverlay calling={isReceivingCall} refuse={refuseCall} answer={answerCall} caller={callerData?.from}/>
      <VStack spacing={8} width="100%">
        <Heading color="purple.200" mb="8">Video Chat</Heading>
        {!username ? (
          <Flex as="form" align="center" w="full" direction="column" maxW="600px" onSubmit={handleChangeName}>
            <Text color="gray.200" fontWeight="normal" mb="2">Before start, type your name: </Text>
              <InputTextControlled name="username" value={name} onChange={setName} placeholder="Jon Doe"/>
              <Button type="submit" colorScheme="purple" width="100%" mt="2">Confirm</Button>
          </Flex>
        ): (
          <Flex as="form" align="center" w="full" direction="column" maxW="600px" onSubmit={handleChangeName}>
            <InputTextControlled name="username" value={username} onChange={() => {}} disabled/>
            <OnlineBadger />
          </Flex>
        )}
        <OnlineList users={onlineUsers} callback={callUser}/>
      </VStack>
      </Page>
  )
})
Example #27
Source File: index.tsx    From calories-in with MIT License 4 votes vote down vote up
function VariantStats({ ...rest }: Props) {
  const dietForm = useDietForm()
  const { variantsForms, selectedVariantFormIndex } = dietForm
  const selectedVariantForm = variantsForms[selectedVariantFormIndex]
  const variantsDetailsModalDisclosure = useDisclosure()

  const {
    variantStats,
    proteinPercent,
    carbsPercent,
    fatPercent,
    energyDiff,
  } = useVariantStats({ variantFormFieldId: selectedVariantForm.fieldId })

  const { saturatedFat } = variantStats
  const saturatedFatEnergyEstimate = getFatEnergyEstimate(saturatedFat)
  const { energyEstimate } = getStatsEnergiesEstimates(variantStats)
  const saturatedFatPercent = Math.round(
    getMacroEnergyPercent(saturatedFatEnergyEstimate, energyEstimate)
  )

  const hasAtLeastOneMeal = selectedVariantForm.mealsForms.length > 0

  return (
    <VStack
      p={5}
      spacing={3}
      align="stretch"
      alignSelf="flex-start"
      justify="left"
      {...rest}
    >
      <Text fontSize="lg" textAlign="center" textColor="gray.500">
        Daily totals
      </Text>
      <Divider />
      <EnergyStat
        energy={variantStats.energy}
        energyDiff={energyDiff}
        hasAtLeastOneMeal={hasAtLeastOneMeal}
      />

      <VariantStat
        label="Protein"
        detail={`${proteinPercent}%`}
        value={variantStats.protein}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from protein"
      />
      <VariantStat
        label="Carbs"
        detail={`${carbsPercent}%`}
        value={variantStats.carbs}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from carbs"
      />
      <VariantStat
        label="Fat"
        detail={`${fatPercent}%`}
        value={variantStats.fat}
        type="primaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from fat"
      />

      <VariantStat
        label="Saturated fat"
        detail={`${saturatedFatPercent}%`}
        value={variantStats.saturatedFat}
        type="secondaryMacro"
        isDisabled={!hasAtLeastOneMeal}
        tooltipLabel="% calories from saturated fat"
      />
      <VariantStat
        label="Sugar"
        value={variantStats.sugar}
        type="secondaryMacro"
        isDisabled={!hasAtLeastOneMeal}
      />

      <Divider />

      <Button
        colorScheme="teal"
        variant="link"
        leftIcon={<Info size={16} />}
        isDisabled={!hasAtLeastOneMeal}
        onClick={variantsDetailsModalDisclosure.onOpen}
      >
        View details
      </Button>

      <VariantsDetailsModal
        isOpen={variantsDetailsModalDisclosure.isOpen}
        onClose={variantsDetailsModalDisclosure.onClose}
        initialVariantForm={selectedVariantForm}
      />
    </VStack>
  )
}
Example #28
Source File: Login.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
export default function Login(props: Props) {
    const toast = createStandaloneToast(theme);

    const [ loading, setLoading ] = useState(false);
    const [ loggedIn, setLoggedIn ] = useState(false);

    useEffect(() => {
        props.manager.events.on('loggedIn', () => setLoading(true));
    }, []);

    const login = () => {
        if ((window.ethereum as any).chainId !== '0x1') {
            toast({
                title: "Wrong network",
                description: "Please switch to the main Ethereum network",
                status: "error",
                ...toastStyle,
            });
            return;
        }

        props.authenticator.login()
            .then(() => {
                setLoggedIn(true)
                toast({
                    title: "Success",
                    description: "You have successfully logged in!",
                    status: "success",
                    ...toastStyle
                });
            })
            .catch((err) => {
                setLoggedIn(false);
                toast({
                    title: "Error " + (err.code ?? ""),
                    description: err.message ?? err,
                    status: "error",
                    ...toastStyle
                });
            });
    }

    return (
        <ChakraProvider theme={theme}>
            <Center style={{
                height: "100vh",
                backdropFilter: "brightness(50%)",
            }}>
                {loading ? <Spinner size="xl" color="white" /> : <Container style={{
                    padding: "1rem",
                    borderStyle: "solid",
                    boxShadow: "0px 0px 15px rgba(0,0,0,1)",
                    borderColor: "black",
                    borderWidth: "0px",
                    backgroundColor: "white",
                    borderRadius: "7px",
                }}>
                <VStack>
                    <Heading>
                        Please login before accessing the game
                    </Heading>
                    <br />
                    <Text>
                        To login, you need to sign a message using your wallet provider. Our personal favorite is Metamask but you can use any other extension or wallet provider.
                    </Text>
                    <UnorderedList spacing={4} style={{
                        paddingLeft: "1rem",
                    }}>
                        <ListItem>
                            Click on this button to trigger the sign message
                            <br />
                            <Button variant="primary" onClick={login}>
                                Sign
                            </Button>
                        </ListItem>
                        <ListItem>Your wallet provider will popup a dialog and you will need to press `Sign`</ListItem>
                        <ListItem>
                            Wait for this to show as logged in
                            <HStack style={{
                                paddingLeft: "1rem",
                                paddingRight: "1rem",
                            }}>
                                {loggedIn ? <CheckIcon /> : <Spinner color='red.500' />}
                                <Text style={{
                                    paddingTop: "1rem",
                                }}>{loggedIn ? 'Logged in' : 'Not logged in'}</Text>
                            </HStack>
                        </ListItem>
                        <ListItem>Press continue</ListItem>
                    </UnorderedList>
                    <Button 
                        disabled={!loggedIn}
                        onClick={() => props.manager.events.emit('loggedIn')}
                        style={{
                            position: "relative",
                            top: "30px"
                        }}
                        variant="primary"
                    >
                        Continue
                    </Button>
                </VStack>
                </Container>}
            </Center>
        </ChakraProvider>
    );
}
Example #29
Source File: achievements.tsx    From portfolio with MIT License 4 votes vote down vote up
Achievements = () => {
  return (
    <PageSlideFade>
      <Box align="start" mb={6}>
        <Header mt={0} mb={0}>
          Achievements
        </Header>
      </Box>
      <VStack textAlign="start" align="start" mb={5}>
        <Box>
          <Heading fontSize="2xl" fontWeight="600" my={5}>
            2021
          </Heading>
          <Box>
            <TimelineItem icon={FiUsers}>Became a dad ❤️</TimelineItem>
            <TimelineItem icon={FiPackage}>
              Published my first post on this website{" "}
              <InternalLink color={"blue.200"} url="/blog" text={"Blog"} />
            </TimelineItem>
            <TimelineItem icon={FiPackage}>
              Published or contributed to{" "}
              <InternalLink
                color={"blue.200"}
                url="/open-source"
                text={"9 open-source repositories"}
              />
            </TimelineItem>
            <TimelineItem icon={FiBarChart2}>
              Collected 6k+ post views and 350+ total reactions on{" "}
              <ExternalLink
                color={"blue.200"}
                url="https://dev.to/m_ahmad"
                text={"Dev.to"}
                target="_blank"
              />
            </TimelineItem>
            <TimelineItem icon={FiHome} skipTrail>
              Rebuilt my portfolio website with React, ChakraUI and
              Framer-motion,{" "}
              <ExternalLink
                color={"blue.200"}
                url="https://github.com/MA-Ahmad/portfolio"
                text={"source on Github"}
                target="_blank"
              />
              .
            </TimelineItem>
          </Box>
        </Box>
        <Box>
          <Heading fontSize="2xl" fontWeight="600" my={5}>
            2020
          </Heading>
          <Box>
            <TimelineItem icon={FiEdit2}>Wrote 5 blog posts</TimelineItem>
            <TimelineItem icon={FiPackage}>
              Published or contributed to{" "}
              <ExternalLink
                color={"blue.200"}
                url="https://github.com/MA-Ahmad?tab=repositories"
                text={"32 open-source repositories"}
                target="_blank"
              />
            </TimelineItem>
            <TimelineItem icon={FiBarChart2}>
              Collected 650+ post views and 15+ total reactions on{" "}
              <ExternalLink
                color={"blue.200"}
                url="https://dev.to/m_ahmad"
                text={"Dev.to"}
                target="_blank"
              />
            </TimelineItem>
            <TimelineItem icon={FiHome} skipTrail>
              Built my portfolio website with React and ChakraUI,{" "}
              <ExternalLink
                color={"blue.200"}
                url="https://github.com/MA-Ahmad/portfolio2"
                text={"source on Github"}
                target="_blank"
              />
              .
            </TimelineItem>
          </Box>
        </Box>
      </VStack>
    </PageSlideFade>
  );
}