mirror of https://github.com/sunface/rust-course
parent
8f55735e4d
commit
da00413171
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../eslintrc.json",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json"
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,4 @@
|
||||
node_modules
|
||||
yarn-error.log
|
||||
.next/
|
||||
.DS_Store
|
||||
|
@ -0,0 +1,63 @@
|
||||
const baseUrl = "https://github.com/imdotdev/im.dev"
|
||||
|
||||
const siteConfig = {
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Segun Adebayo. All Rights Reserved.`,
|
||||
algolia: {
|
||||
apiKey: "df1dcc41f7b8e5d68e73dd56d1e19701",
|
||||
indexName: "chakra-ui",
|
||||
inputSelector: "#algolia-search",
|
||||
},
|
||||
author: {
|
||||
name: "Segun Adebayo",
|
||||
github: "https://github.com/segunadebayo",
|
||||
twitter: "https://twitter.com/thesegunadebayo",
|
||||
linkedin: "https://linkedin.com/in/thesegunadebayo",
|
||||
email: "sage@adebayosegun.com",
|
||||
},
|
||||
repo: {
|
||||
url: baseUrl,
|
||||
editUrl: `${baseUrl}/edit/develop/website`,
|
||||
blobUrl: `${baseUrl}/blob/develop`,
|
||||
},
|
||||
openCollective: {
|
||||
url: "https://opencollective.com/chakra-ui",
|
||||
},
|
||||
discord: {
|
||||
url: "https://discord.gg/dQHfcWF",
|
||||
},
|
||||
seo: {
|
||||
title: "im.dev",
|
||||
titleTemplate: "%s - im.dev",
|
||||
description:
|
||||
"千挑万选,只为把高质量的内容呈现给你。我是开发,我为技术代言",
|
||||
siteUrl: "https://im.dev",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "en_US",
|
||||
url: "https://chakra-ui.com",
|
||||
title: "Chakra UI",
|
||||
description:
|
||||
"Simple, Modular and Accessible UI Components for your React Applications.",
|
||||
site_name:
|
||||
"Chakra UI: Simple, Modular and Accessible UI Components for your React Applications.",
|
||||
images: [
|
||||
{
|
||||
url: "/og-image.png",
|
||||
width: 1240,
|
||||
height: 480,
|
||||
alt:
|
||||
"Chakra UI: Simple, Modular and Accessible UI Components for your React Applications.",
|
||||
},
|
||||
{
|
||||
url: "/twitter-og-image.png",
|
||||
width: 1012,
|
||||
height: 506,
|
||||
alt:
|
||||
"Chakra UI: Simple, Modular and Accessible UI Components for your React Applications.",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default siteConfig
|
@ -0,0 +1,48 @@
|
||||
import React from "react"
|
||||
import { Box, Icon, Text, Stack, Link, chakra } from "@chakra-ui/react"
|
||||
import { IoLogoTwitter, IoLogoLinkedin } from "react-icons/io"
|
||||
import { MdEmail } from "react-icons/md"
|
||||
import { DiGithubBadge } from "react-icons/di"
|
||||
|
||||
type FooterLinkProps = {
|
||||
icon?: React.ElementType
|
||||
href?: string
|
||||
label?: string
|
||||
}
|
||||
|
||||
const FooterLink: React.FC<FooterLinkProps> = ({ icon, href, label }) => (
|
||||
<Link display="inline-block" href={href} aria-label={label} isExternal>
|
||||
<Icon as={icon} fontSize="xl" color="gray.400" />
|
||||
</Link>
|
||||
)
|
||||
|
||||
const links = [
|
||||
{
|
||||
icon: DiGithubBadge,
|
||||
label: "GitHub",
|
||||
href: "https://github.com/imdotdev/imdev",
|
||||
},
|
||||
{
|
||||
icon: MdEmail,
|
||||
label: "Email",
|
||||
href: "mailto:cto@188.com",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
export const Footer = () => (
|
||||
<Box as="footer" mt={12} textAlign="center">
|
||||
<Text fontSize="sm" display="flex" alignItems="center" justifyContent="center">
|
||||
<span>Proudly made in </span>
|
||||
<chakra.span fontSize="24px" ml="2">🇨🇳</chakra.span>
|
||||
<chakra.span ml="2">by codecc.com</chakra.span>
|
||||
</Text>
|
||||
<Stack mt={4} direction="row" spacing="12px" justify="center">
|
||||
{links.map((link) => (
|
||||
<FooterLink key={link.href} {...link} />
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
)
|
||||
|
||||
export default Footer
|
@ -0,0 +1,184 @@
|
||||
import {
|
||||
Box,
|
||||
BoxProps,
|
||||
Center,
|
||||
CloseButton,
|
||||
Flex,
|
||||
HStack,
|
||||
IconButton,
|
||||
IconButtonProps,
|
||||
useBreakpointValue,
|
||||
useColorModeValue,
|
||||
useUpdateEffect,
|
||||
} from "@chakra-ui/react"
|
||||
import { AnimatePresence, motion, useElementScroll } from "framer-motion"
|
||||
import useRouteChanged from "src/hooks/use-route-changed"
|
||||
import NextLink from "next/link"
|
||||
import { useRouter } from "next/router"
|
||||
import * as React from "react"
|
||||
import { AiOutlineMenu } from "react-icons/ai"
|
||||
import { RemoveScroll } from "react-remove-scroll"
|
||||
import Logo from "src/components/logo"
|
||||
|
||||
function NavLink({ href, children }) {
|
||||
const { pathname } = useRouter()
|
||||
|
||||
const [, group] = href.split("/")
|
||||
const isActive = pathname.includes(group)
|
||||
|
||||
return (
|
||||
<NextLink href={href}>
|
||||
<Center
|
||||
flex="1"
|
||||
minH="40px"
|
||||
as="button"
|
||||
rounded="md"
|
||||
transition="0.2s all"
|
||||
fontWeight={isActive ? "semibold" : "medium"}
|
||||
bg={isActive ? "teal.400" : undefined}
|
||||
borderWidth={isActive ? undefined : "1px"}
|
||||
color={isActive ? "white" : undefined}
|
||||
_hover={{
|
||||
bg: isActive
|
||||
? "teal.500"
|
||||
: useColorModeValue("gray.100", "whiteAlpha.100"),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Center>
|
||||
</NextLink>
|
||||
)
|
||||
}
|
||||
|
||||
interface MobileNavContentProps {
|
||||
isOpen?: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export function MobileNavContent(props: MobileNavContentProps) {
|
||||
const { isOpen, onClose } = props
|
||||
const closeBtnRef = React.useRef<HTMLButtonElement>()
|
||||
const { pathname } = useRouter()
|
||||
|
||||
useRouteChanged(onClose)
|
||||
|
||||
/**
|
||||
* Scenario: Menu is open on mobile, and user resizes to desktop/tablet viewport.
|
||||
* Result: We'll close the menu
|
||||
*/
|
||||
const showOnBreakpoint = useBreakpointValue({ base: true, lg: false })
|
||||
|
||||
React.useEffect(() => {
|
||||
if (showOnBreakpoint == false) {
|
||||
onClose()
|
||||
}
|
||||
}, [showOnBreakpoint])
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (isOpen) {
|
||||
requestAnimationFrame(() => {
|
||||
closeBtnRef.current?.focus()
|
||||
})
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const [shadow, setShadow] = React.useState<string>()
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<RemoveScroll forwardProps>
|
||||
<motion.div
|
||||
transition={{ duration: 0.08 }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<Flex
|
||||
direction="column"
|
||||
w="100%"
|
||||
bg={useColorModeValue("white", "gray.800")}
|
||||
h="100vh"
|
||||
overflow="auto"
|
||||
pos="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
zIndex={20}
|
||||
pb="8"
|
||||
>
|
||||
<Box>
|
||||
<Flex justify="space-between" px="6" pt="5" pb="4">
|
||||
<Logo />
|
||||
<HStack spacing="5">
|
||||
<CloseButton ref={closeBtnRef} onClick={onClose} />
|
||||
</HStack>
|
||||
</Flex>
|
||||
<Box px="6" pb="6" pt="2" shadow={shadow}>
|
||||
<HStack>
|
||||
<NavLink href="/docs/getting-started">Docs</NavLink>
|
||||
<NavLink href="/guides/integrations/with-cra">
|
||||
Guides
|
||||
</NavLink>
|
||||
<NavLink href="/team">Team</NavLink>
|
||||
</HStack>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<ScrollView
|
||||
onScroll={(scrolled) => {
|
||||
setShadow(scrolled ? "md" : undefined)
|
||||
}}
|
||||
>
|
||||
|
||||
</ScrollView>
|
||||
</Flex>
|
||||
</motion.div>
|
||||
</RemoveScroll>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
const ScrollView = (props: BoxProps & { onScroll?: any }) => {
|
||||
const { onScroll, ...rest } = props
|
||||
const [y, setY] = React.useState(0)
|
||||
const elRef = React.useRef<any>()
|
||||
const { scrollY } = useElementScroll(elRef)
|
||||
React.useEffect(() => {
|
||||
return scrollY.onChange(() => setY(scrollY.get()))
|
||||
}, [scrollY])
|
||||
|
||||
useUpdateEffect(() => {
|
||||
onScroll?.(y > 5 ? true : false)
|
||||
}, [y])
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={elRef}
|
||||
flex="1"
|
||||
id="routes"
|
||||
overflow="auto"
|
||||
px="6"
|
||||
pb="6"
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const MobileNavButton = React.forwardRef(
|
||||
(props: IconButtonProps, ref: React.Ref<any>) => {
|
||||
return (
|
||||
<IconButton
|
||||
ref={ref}
|
||||
display={{ base: "flex", md: "none" }}
|
||||
aria-label="Open menu"
|
||||
fontSize="20px"
|
||||
color={useColorModeValue("gray.800", "inherit")}
|
||||
variant="ghost"
|
||||
icon={<AiOutlineMenu />}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
@ -0,0 +1,186 @@
|
||||
import {
|
||||
chakra,
|
||||
Flex,
|
||||
Box,
|
||||
HStack,
|
||||
Icon,
|
||||
IconButton,
|
||||
Link,
|
||||
useColorMode,
|
||||
useColorModeValue,
|
||||
useDisclosure,
|
||||
useUpdateEffect,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Image
|
||||
} from "@chakra-ui/react"
|
||||
import siteConfig from "configs/site-config"
|
||||
import { useViewportScroll } from "framer-motion"
|
||||
import NextLink from "next/link"
|
||||
import React from "react"
|
||||
import { FaMoon, FaSun, FaUserAlt, FaRegSun, FaSignOutAlt, FaRegBookmark, FaChartBar, FaHome } from "react-icons/fa"
|
||||
import Logo, { LogoIcon } from "src/components/logo"
|
||||
import { MobileNavButton, MobileNavContent } from "./mobile-nav"
|
||||
import AlgoliaSearch from "src/components/search/algolia-search"
|
||||
|
||||
|
||||
const DiscordIcon = (props) => (
|
||||
<svg viewBox="0 0 146 146" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M107.75 125.001s-4.5-5.375-8.25-10.125c16.375-4.625 22.625-14.875 22.625-14.875-5.125 3.375-10 5.75-14.375 7.375-6.25 2.625-12.25 4.375-18.125 5.375-12 2.25-23 1.625-32.375-.125-7.125-1.375-13.25-3.375-18.375-5.375-2.875-1.125-6-2.5-9.125-4.25-.375-.25-.75-.375-1.125-.625-.25-.125-.375-.25-.5-.375-2.25-1.25-3.5-2.125-3.5-2.125s6 10 21.875 14.75c-3.75 4.75-8.375 10.375-8.375 10.375-27.625-.875-38.125-19-38.125-19 0-40.25 18-72.875 18-72.875 18-13.5 35.125-13.125 35.125-13.125l1.25 1.5c-22.5 6.5-32.875 16.375-32.875 16.375s2.75-1.5 7.375-3.625c13.375-5.875 24-7.5 28.375-7.875.75-.125 1.375-.25 2.125-.25 7.625-1 16.25-1.25 25.25-.25 11.875 1.375 24.625 4.875 37.625 12 0 0-9.875-9.375-31.125-15.875l1.75-2S110 19.626 128 33.126c0 0 18 32.625 18 72.875 0 0-10.625 18.125-38.25 19zM49.625 66.626c-7.125 0-12.75 6.25-12.75 13.875s5.75 13.875 12.75 13.875c7.125 0 12.75-6.25 12.75-13.875.125-7.625-5.625-13.875-12.75-13.875zm45.625 0c-7.125 0-12.75 6.25-12.75 13.875s5.75 13.875 12.75 13.875c7.125 0 12.75-6.25 12.75-13.875s-5.625-13.875-12.75-13.875z"
|
||||
fillRule="nonzero"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const GithubIcon = (props) => (
|
||||
<svg viewBox="0 0 20 20" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M10 0a10 10 0 0 0-3.16 19.49c.5.1.68-.22.68-.48l-.01-1.7c-2.78.6-3.37-1.34-3.37-1.34-.46-1.16-1.11-1.47-1.11-1.47-.9-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.9 1.52 2.34 1.08 2.91.83.1-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.94 0-1.1.39-1.99 1.03-2.69a3.6 3.6 0 0 1 .1-2.64s.84-.27 2.75 1.02a9.58 9.58 0 0 1 5 0c1.91-1.3 2.75-1.02 2.75-1.02.55 1.37.2 2.4.1 2.64.64.7 1.03 1.6 1.03 2.69 0 3.84-2.34 4.68-4.57 4.93.36.31.68.92.68 1.85l-.01 2.75c0 .26.18.58.69.48A10 10 0 0 0 10 0"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
function HeaderContent() {
|
||||
const mobileNav = useDisclosure()
|
||||
|
||||
const { toggleColorMode: toggleMode } = useColorMode()
|
||||
const text = useColorModeValue("dark", "light")
|
||||
const SwitchIcon = useColorModeValue(FaMoon, FaSun)
|
||||
const mobileNavBtnRef = React.useRef<HTMLButtonElement>()
|
||||
|
||||
useUpdateEffect(() => {
|
||||
mobileNavBtnRef.current?.focus()
|
||||
}, [mobileNav.isOpen])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex w="100%" h="100%" align="center" justify="space-between" px={{ base: "4", md: "6" }}>
|
||||
<Flex align="center">
|
||||
<NextLink href="/" passHref>
|
||||
<chakra.a display="block" aria-label="Chakra UI, Back to homepage">
|
||||
<Logo display={{ base: "none", md: "block" }} />
|
||||
<Box minW="3rem" display={{ base: "block", md: "none" }}>
|
||||
<LogoIcon />
|
||||
</Box>
|
||||
</chakra.a>
|
||||
</NextLink>
|
||||
|
||||
<HStack spacing="5" display={{ base: "none", md: "flex" }} ml="16" fontSize="17px">
|
||||
<Link>Home</Link>
|
||||
<Link>Tags</Link>
|
||||
</HStack>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
w="100%"
|
||||
maxW="724px"
|
||||
align="center"
|
||||
color="gray.400"
|
||||
>
|
||||
<AlgoliaSearch />
|
||||
<HStack spacing="5" display={{ base: "none", md: "flex" }}>
|
||||
<Link
|
||||
isExternal
|
||||
aria-label="Go to Chakra UI GitHub page"
|
||||
href={siteConfig.repo.url}
|
||||
>
|
||||
<Icon
|
||||
as={GithubIcon}
|
||||
display="block"
|
||||
transition="color 0.2s"
|
||||
w="5"
|
||||
h="5"
|
||||
_hover={{ color: "gray.600" }}
|
||||
/>
|
||||
</Link>
|
||||
</HStack>
|
||||
<IconButton
|
||||
size="md"
|
||||
fontSize="lg"
|
||||
aria-label={`Switch to ${text} mode`}
|
||||
variant="ghost"
|
||||
color="current"
|
||||
ml={{ base: "0", md: "3" }}
|
||||
onClick={toggleMode}
|
||||
icon={<SwitchIcon />}
|
||||
/>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={IconButton}
|
||||
bg="transparent"
|
||||
icon={<FaUserAlt />}
|
||||
aria-label="Options"
|
||||
ml={{ base: "0", md: "2" }}
|
||||
/>
|
||||
<MenuList>
|
||||
<MenuItem>
|
||||
<Image
|
||||
boxSize="2rem"
|
||||
borderRadius="full"
|
||||
src="https://placekitten.com/100/100"
|
||||
alt="Fluffybuns the destroyer"
|
||||
mr="12px"
|
||||
/>
|
||||
<span>Sunface</span>
|
||||
</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem icon={<FaChartBar fontSize="16" />}>Dashboard</MenuItem>
|
||||
<MenuItem icon={<FaRegBookmark fontSize="16" />}>Bookmarks</MenuItem>
|
||||
<MenuDivider />
|
||||
<MenuItem icon={<FaRegSun fontSize="16" />}>Account Settings</MenuItem>
|
||||
<MenuItem icon={<FaSignOutAlt fontSize="16" />}>Log out</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
<MobileNavButton
|
||||
ref={mobileNavBtnRef}
|
||||
aria-label="Open Menu"
|
||||
onClick={mobileNav.onOpen}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<MobileNavContent isOpen={mobileNav.isOpen} onClose={mobileNav.onClose} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Header(props) {
|
||||
const bg = useColorModeValue("white", "gray.800")
|
||||
const ref = React.useRef<HTMLHeadingElement>()
|
||||
const [y, setY] = React.useState(0)
|
||||
const { height = 0 } = ref.current?.getBoundingClientRect() ?? {}
|
||||
|
||||
const { scrollY } = useViewportScroll()
|
||||
React.useEffect(() => {
|
||||
return scrollY.onChange(() => setY(scrollY.get()))
|
||||
}, [scrollY])
|
||||
|
||||
return (
|
||||
<chakra.header
|
||||
ref={ref}
|
||||
shadow={y > height ? "sm" : undefined}
|
||||
transition="box-shadow 0.2s"
|
||||
pos="fixed"
|
||||
top="0"
|
||||
zIndex="3"
|
||||
bg={bg}
|
||||
left="0"
|
||||
right="0"
|
||||
borderTop="4px solid"
|
||||
borderTopColor="teal.400"
|
||||
width="full"
|
||||
{...props}
|
||||
>
|
||||
<chakra.div height="4.5rem" mx="auto" maxW="1200px">
|
||||
<HeaderContent />
|
||||
</chakra.div>
|
||||
</chakra.header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
@ -0,0 +1,66 @@
|
||||
import { Badge, Box, chakra } from "@chakra-ui/react"
|
||||
import { SkipNavContent, SkipNavLink } from "@chakra-ui/skip-nav"
|
||||
import Container from "components/container"
|
||||
import Footer from "./footer"
|
||||
import Nav from "./nav"
|
||||
import SEO from "components/seo"
|
||||
import { useRouter } from "next/router"
|
||||
import * as React from "react"
|
||||
import PageTransition from "src/components/page-transition"
|
||||
import siteConfig from "configs/site-config"
|
||||
|
||||
function useHeadingFocusOnRouteChange() {
|
||||
const router = useRouter()
|
||||
|
||||
React.useEffect(() => {
|
||||
const onRouteChange = () => {
|
||||
const [heading] = Array.from(document.getElementsByTagName("h1"))
|
||||
heading?.focus()
|
||||
}
|
||||
router.events.on("routeChangeComplete", onRouteChange)
|
||||
return () => {
|
||||
router.events.off("routeChangeComplete", onRouteChange)
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
|
||||
interface PageContainerProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function PageContainer(props: PageContainerProps) {
|
||||
const { children } = props
|
||||
useHeadingFocusOnRouteChange()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SEO
|
||||
title={siteConfig.seo.title}
|
||||
description={siteConfig.seo.description}
|
||||
/>
|
||||
<SkipNavLink zIndex={20}>Skip to Content</SkipNavLink>
|
||||
<Nav />
|
||||
<Container as="main" className="main-content">
|
||||
<Box display={{ base: "block", md: "flex" }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<SkipNavContent />
|
||||
<Box
|
||||
id="content"
|
||||
pt={3}
|
||||
px={5}
|
||||
mt="4.5rem"
|
||||
mx="auto"
|
||||
>
|
||||
<PageTransition>
|
||||
{children}
|
||||
</PageTransition>
|
||||
</Box>
|
||||
<Footer />
|
||||
</div>
|
||||
</Box>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PageContainer
|
@ -0,0 +1,2 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
@ -0,0 +1,18 @@
|
||||
async function redirect() {
|
||||
return [
|
||||
{
|
||||
source: "/discord",
|
||||
destination: "https://discord.gg/dQHfcWF",
|
||||
permanent: true,
|
||||
},
|
||||
// GENERAL
|
||||
{
|
||||
source: "/getting-started",
|
||||
destination: "/docs/getting-started",
|
||||
permanent: true,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = redirect
|
||||
|
@ -0,0 +1,23 @@
|
||||
const withPlugins = require("next-compose-plugins")
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
|
||||
const defaultConfig = {
|
||||
target: "serverless",
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
externals: [...config.externals],
|
||||
}),
|
||||
experimental: {
|
||||
optimizeFonts: true,
|
||||
modern: true,
|
||||
},
|
||||
redirects: require("./next-redirect"),
|
||||
}
|
||||
|
||||
module.exports = withPlugins(
|
||||
[withBundleAnalyzer],
|
||||
defaultConfig,
|
||||
)
|
||||
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "im.dev",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 4004",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"test": "jest",
|
||||
"clean": "rm -rf .next",
|
||||
"analyze": "ANALYZE=true next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/icons": "^1.0.4",
|
||||
"@chakra-ui/props-docs": "1.0.13",
|
||||
"@chakra-ui/react": "1.2.1",
|
||||
"@chakra-ui/skip-nav": "^1.1.0",
|
||||
"@chakra-ui/theme-tools": "1.0.3",
|
||||
"@chakra-ui/utils": "1.1.0",
|
||||
"@emotion/react": "^11.1.4",
|
||||
"@emotion/styled": "^11.0.0",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"date-fns": "^2.16.1",
|
||||
"framer-motion": "^3.1.1",
|
||||
"next": "^10.0.4",
|
||||
"next-seo": "^4.17.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-icons": "^4.1.0",
|
||||
"docsearch.js": "^2.6.3",
|
||||
"@docsearch/react": "^1.0.0-alpha.27"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^10.0.4",
|
||||
"@types/node": "^14.14.19",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"next-compose-plugins": "^2.2.1",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
import { trackPageview } from "analytics/track-event"
|
||||
import { DefaultSeo } from "next-seo"
|
||||
import Head from "next/head"
|
||||
import Router from "next/router"
|
||||
import React from "react"
|
||||
import { ChakraProvider } from "@chakra-ui/react"
|
||||
import theme from "theme"
|
||||
import FontFace from "src/components/font-face"
|
||||
import { getSeo } from "utils/seo"
|
||||
|
||||
Router.events.on("routeChangeComplete", (url) => {
|
||||
trackPageview(url)
|
||||
})
|
||||
|
||||
const App = ({ Component, pageProps }) => {
|
||||
const seo = getSeo({ omitOpenGraphImage: false })
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/favicon.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://static.cloudflareinsights.com" />
|
||||
<meta name="theme-color" content="#319795" />
|
||||
</Head>
|
||||
<DefaultSeo {...seo} />
|
||||
<ChakraProvider theme={theme}>
|
||||
<Component {...pageProps} />
|
||||
</ChakraProvider>
|
||||
<FontFace />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
@ -0,0 +1,23 @@
|
||||
import { chakra } from "@chakra-ui/react"
|
||||
import Container from "components/container"
|
||||
import SEO from "components/seo"
|
||||
import siteConfig from "configs/site-config"
|
||||
import Nav from "layouts/nav"
|
||||
import PageContainer from "layouts/page-container"
|
||||
import React from "react"
|
||||
|
||||
const HomePage = () => (
|
||||
<>
|
||||
<SEO
|
||||
title={siteConfig.seo.title}
|
||||
description={siteConfig.seo.description}
|
||||
/>
|
||||
<Nav />
|
||||
<PageContainer>
|
||||
<chakra.h1>NOT FOUND</chakra.h1>
|
||||
<p>You just hit a route that doesn't exist... the sadness.</p>
|
||||
</PageContainer>
|
||||
</>
|
||||
)
|
||||
|
||||
export default HomePage
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
import React from "react"
|
||||
|
||||
const GAScript = () => (
|
||||
<>
|
||||
<script
|
||||
async
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_TRACKING_ID}`}
|
||||
/>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${process.env.GA_TRACKING_ID}');
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
export default GAScript
|
@ -0,0 +1,33 @@
|
||||
export function trackPageview(url: string) {
|
||||
const _window = window as typeof window & { gtag: any }
|
||||
try {
|
||||
_window.gtag("config", process.env.GA_TRACKING_ID, {
|
||||
page_location: url,
|
||||
page_title: document.title,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Failed sending metrics", err)
|
||||
}
|
||||
}
|
||||
|
||||
type TrackEventOptions = {
|
||||
action: any
|
||||
category: string
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export function trackEvent(options: TrackEventOptions) {
|
||||
const { action, category, label, value } = options
|
||||
const _window = window as typeof window & { gtag: any }
|
||||
try {
|
||||
_window.gtag("event", action, {
|
||||
event_category: category,
|
||||
event_label: label,
|
||||
value,
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Failed sending metrics", err)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import { Box, BoxProps } from "@chakra-ui/react"
|
||||
|
||||
export const Container = (props: BoxProps) => (
|
||||
<Box
|
||||
w="full"
|
||||
pb="12"
|
||||
pt="3"
|
||||
maxW="1200px"
|
||||
mx="auto"
|
||||
px={{ base: "4", md: "8" }}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
export default Container
|
@ -0,0 +1,80 @@
|
||||
const FontFace = () => (
|
||||
<style jsx global>
|
||||
{`
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url(/fonts/Inter.woff2) format("woff2");
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
)
|
||||
|
||||
export default FontFace
|
||||
|
@ -0,0 +1,74 @@
|
||||
import { chakra, HTMLChakraProps, useColorModeValue } from "@chakra-ui/react"
|
||||
import React from "react"
|
||||
|
||||
export const Logo = (props: HTMLChakraProps<"svg">) => {
|
||||
const fill = useColorModeValue("#2D3748", "#fff")
|
||||
|
||||
return (
|
||||
<chakra.svg
|
||||
height="8"
|
||||
width="auto"
|
||||
viewBox="0 0 998 257"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M388.5 115.302c17.612 0 25.466 11.424 28.084 21.658l24.752-9.044c-4.76-18.564-21.896-38.08-53.074-38.08-33.32 0-59.262 25.704-59.262 61.404 0 35.224 25.942 61.642 59.976 61.642 30.464 0 47.838-19.754 53.312-37.842l-24.276-8.806c-2.618 8.806-10.948 21.42-29.036 21.42-17.374 0-32.368-13.09-32.368-36.414s14.994-35.938 31.892-35.938zM484.894 141.244c.476-14.756 8.806-26.18 24.038-26.18 17.374 0 23.8 11.424 23.8 25.704v68.544h27.608v-73.304c0-25.466-13.804-45.934-42.364-45.934-12.138 0-25.228 4.284-33.082 14.518V37h-27.608v172.312h27.608v-68.068zM577.29 177.896c0 18.326 14.994 34.986 39.27 34.986 18.802 0 30.226-9.52 35.7-18.326 0 9.282.952 14.042 1.19 14.756h25.704c-.238-1.19-1.428-8.092-1.428-18.564v-57.596c0-23.086-13.566-43.316-49.266-43.316-28.56 0-46.648 17.85-48.79 37.842l25.228 5.712c1.19-11.662 9.282-20.944 23.8-20.944 15.232 0 21.896 7.854 21.896 17.612 0 4.046-1.904 7.378-8.568 8.33l-29.75 4.522c-19.754 2.856-34.986 14.28-34.986 34.986zm44.506 13.328c-10.948 0-16.898-7.14-16.898-14.994 0-9.52 6.902-14.28 15.47-15.708L650.594 156v5.236c0 22.61-13.328 29.988-28.798 29.988zM810.108 93.406h-36.652l-44.506 46.886V37h-27.37v172.312h27.37v-32.368l14.28-14.994 34.034 47.362h33.796l-48.552-66.878 47.6-49.028zM889.349 92.692c-1.19-.238-4.046-.714-7.378-.714-15.232 0-28.084 7.378-33.558 19.992V93.406h-26.894v115.906h27.608v-55.216c0-21.658 9.758-34.034 31.178-34.034 2.856 0 5.95.238 9.044.714V92.692zM895.968 177.896c0 18.326 14.994 34.986 39.27 34.986 18.802 0 30.226-9.52 35.7-18.326 0 9.282.952 14.042 1.19 14.756h25.704c-.238-1.19-1.428-8.092-1.428-18.564v-57.596c0-23.086-13.566-43.316-49.266-43.316-28.56 0-46.648 17.85-48.79 37.842l25.228 5.712c1.19-11.662 9.282-20.944 23.8-20.944 15.232 0 21.896 7.854 21.896 17.612 0 4.046-1.904 7.378-8.568 8.33l-29.75 4.522c-19.754 2.856-34.986 14.28-34.986 34.986zm44.506 13.328c-10.948 0-16.898-7.14-16.898-14.994 0-9.52 6.902-14.28 15.47-15.708L969.272 156v5.236c0 22.61-13.328 29.988-28.798 29.988z"
|
||||
fill={fill}
|
||||
/>
|
||||
<rect width="257" height="257" rx="128.5" fill="url(#logo)" />
|
||||
<path
|
||||
d="M69.558 133.985l87.592-86.9891c1.636-1.6251 4.27.3525 3.165 2.377l-32.601 59.7521c-.728 1.332.237 2.958 1.755 2.958h56.34c1.815 0 2.691 2.223 1.364 3.462l-98.7278 92.142c-1.7702 1.652-4.4051-.676-2.9839-2.636l46.7357-64.473c.958-1.322.014-3.174-1.619-3.174H70.9673c-1.7851 0-2.6759-2.161-1.4093-3.419z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="logo"
|
||||
x1="128.5"
|
||||
x2="128.5"
|
||||
y2="257"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#7BCBD4" />
|
||||
<stop offset="1" stopColor="#29C6B7" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</chakra.svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const LogoIcon = (props: HTMLChakraProps<"svg">) => {
|
||||
const fill = useColorModeValue("#2D3748", "#fff")
|
||||
|
||||
return (
|
||||
<chakra.svg
|
||||
height="8"
|
||||
width="auto"
|
||||
viewBox="0 0 257 257"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<rect width="257" height="257" rx="128.5" fill="url(#mark)" />
|
||||
<path
|
||||
d="M69.558 133.985l87.592-86.9891c1.636-1.6251 4.27.3525 3.165 2.377l-32.601 59.7521c-.728 1.332.237 2.958 1.755 2.958h56.34c1.815 0 2.691 2.223 1.364 3.462l-98.7278 92.142c-1.7702 1.652-4.4051-.676-2.9839-2.636l46.7357-64.473c.958-1.322.014-3.174-1.619-3.174H70.9673c-1.7851 0-2.6759-2.161-1.4093-3.419z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="mark"
|
||||
x1="128.5"
|
||||
x2="128.5"
|
||||
y2="257"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#7BCBD4" />
|
||||
<stop offset="1" stopColor="#29C6B7" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</chakra.svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Logo
|
@ -0,0 +1,12 @@
|
||||
import * as React from "react"
|
||||
import { HTMLMotionProps, motion } from "framer-motion"
|
||||
|
||||
const PageTransition = (props: HTMLMotionProps<"div">) => (
|
||||
<motion.div
|
||||
initial={{ y: -16, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
export default PageTransition
|
@ -0,0 +1,168 @@
|
||||
import { SearchIcon } from "@chakra-ui/icons"
|
||||
import {
|
||||
chakra,
|
||||
HStack,
|
||||
HTMLChakraProps,
|
||||
Kbd,
|
||||
Portal,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
VisuallyHidden,
|
||||
} from "@chakra-ui/react"
|
||||
import { DocSearchModal, useDocSearchKeyboardEvents } from "@docsearch/react"
|
||||
import Head from "next/head"
|
||||
import Link from "next/link"
|
||||
import { useRouter } from "next/router"
|
||||
import * as React from "react"
|
||||
import SearchStyle from "./search.styles"
|
||||
|
||||
const ACTION_KEY_DEFAULT = ["Ctrl", "Control"]
|
||||
const ACTION_KEY_APPLE = ["⌘", "Command"]
|
||||
|
||||
function Hit(props: any) {
|
||||
const { hit, children } = props
|
||||
return (
|
||||
<Link href={hit.url} passHref>
|
||||
<a>{children}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export const SearchButton = React.forwardRef(function SearchButton(
|
||||
props: HTMLChakraProps<"button">,
|
||||
ref: React.Ref<HTMLButtonElement>,
|
||||
) {
|
||||
const [actionKey, setActionKey] = React.useState<string[]>(ACTION_KEY_APPLE)
|
||||
React.useEffect(() => {
|
||||
if (typeof navigator === "undefined") return
|
||||
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
|
||||
if (!isMac) {
|
||||
setActionKey(ACTION_KEY_DEFAULT)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<chakra.button
|
||||
flex="1"
|
||||
type="button"
|
||||
role="search"
|
||||
mr="6"
|
||||
ref={ref}
|
||||
lineHeight="1.2"
|
||||
w="100%"
|
||||
bg={useColorModeValue("white", "gray.700")}
|
||||
whiteSpace="nowrap"
|
||||
display={{ base: "none", sm: "flex" }}
|
||||
alignItems="center"
|
||||
color="gray.400"
|
||||
py="3"
|
||||
px="4"
|
||||
outline="0"
|
||||
_focus={{ shadow: "outline" }}
|
||||
shadow="base"
|
||||
rounded="md"
|
||||
{...props}
|
||||
>
|
||||
<SearchIcon />
|
||||
<HStack w="full" ml="3" spacing="4px">
|
||||
<Text textAlign="left" flex="1">
|
||||
Search the docs
|
||||
</Text>
|
||||
<HStack spacing="4px">
|
||||
<VisuallyHidden>Press </VisuallyHidden>
|
||||
<Kbd color="gray.500" rounded="2px">
|
||||
<chakra.div
|
||||
as="abbr"
|
||||
title={actionKey[1]}
|
||||
textDecoration="none !important"
|
||||
>
|
||||
{ACTION_KEY_APPLE[0]}
|
||||
</chakra.div>
|
||||
</Kbd>
|
||||
<VisuallyHidden> and </VisuallyHidden>
|
||||
<Kbd color="gray.500" rounded="2px">
|
||||
K
|
||||
</Kbd>
|
||||
<VisuallyHidden> to search</VisuallyHidden>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</chakra.button>
|
||||
)
|
||||
})
|
||||
|
||||
function AlgoliaSearch() {
|
||||
const router = useRouter()
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const searchButtonRef = React.useRef()
|
||||
const [initialQuery, setInitialQuery] = React.useState(null)
|
||||
|
||||
const onOpen = React.useCallback(() => {
|
||||
setIsOpen(true)
|
||||
}, [setIsOpen])
|
||||
|
||||
const onClose = React.useCallback(() => {
|
||||
setIsOpen(false)
|
||||
}, [setIsOpen])
|
||||
|
||||
const onInput = React.useCallback(
|
||||
(e) => {
|
||||
setIsOpen(true)
|
||||
setInitialQuery(e.key)
|
||||
},
|
||||
[setIsOpen, setInitialQuery],
|
||||
)
|
||||
|
||||
useDocSearchKeyboardEvents({
|
||||
isOpen,
|
||||
onOpen,
|
||||
onClose,
|
||||
onInput,
|
||||
searchButtonRef,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://BH4D9OD16A-dsn.algolia.net"
|
||||
crossOrigin="true"
|
||||
/>
|
||||
</Head>
|
||||
<SearchStyle />
|
||||
<SearchButton onClick={onOpen} ref={searchButtonRef} />
|
||||
{isOpen && (
|
||||
<Portal>
|
||||
<DocSearchModal
|
||||
placeholder="Search the docs"
|
||||
initialQuery={initialQuery}
|
||||
initialScrollY={window.scrollY}
|
||||
onClose={onClose}
|
||||
indexName="chakra-ui"
|
||||
apiKey="df1dcc41f7b8e5d68e73dd56d1e19701"
|
||||
appId="BH4D9OD16A"
|
||||
//@ts-expect-error
|
||||
navigator={{
|
||||
navigate({ suggestionUrl }) {
|
||||
setIsOpen(false)
|
||||
router.push(suggestionUrl)
|
||||
},
|
||||
}}
|
||||
hitComponent={Hit}
|
||||
transformItems={(items) => {
|
||||
return items.map((item) => {
|
||||
const a = document.createElement("a")
|
||||
a.href = item.url
|
||||
const hash = a.hash === "#content-wrapper" ? "" : a.hash
|
||||
item.url = `${a.pathname}${hash}`
|
||||
return item
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Portal>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AlgoliaSearch
|
@ -0,0 +1,467 @@
|
||||
import { Global, css } from "@emotion/react"
|
||||
|
||||
const SearchStyle = () => (
|
||||
<Global
|
||||
styles={(theme: any) => css`
|
||||
.DocSearch--active {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.DocSearch-Container {
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 1rem;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
padding: 10vh;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
padding: 12vh;
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-LoadingIndicator svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-LoadingIndicator {
|
||||
display: none;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Ccircle cx='12' cy='12' r='9' stroke-width='2' stroke='%23#319795' /%3E%3Cpath d='M3,12a9,9 0 1,0 18,0a9,9 0 1,0 -18,0' stroke-width='2' stroke='%2306b6d4' stroke-dasharray='56.5486677646' stroke-dashoffset='37.6991118431' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.DocSearch-Container--Stalled .DocSearch-LoadingIndicator {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.DocSearch-Modal {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 47.375rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: ${theme.shadows.lg};
|
||||
background: white;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
background: ${theme.colors.gray["700"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-SearchBar {
|
||||
flex: none;
|
||||
border-bottom: 1px solid ${theme.colors.gray["200"]};
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 1.5rem;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
border-bottom-color: ${theme.colors.gray["600"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Form {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Dropdown {
|
||||
flex: auto;
|
||||
border-bottom-left-radius: 1rem;
|
||||
border-bottom-right-radius: 1rem;
|
||||
padding: 0 1.5rem 1.5rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.DocSearch-MagnifierLabel {
|
||||
flex: none;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21 21L15 15M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z' stroke='%23319795' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
.DocSearch-MagnifierLabel svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Container--Stalled .DocSearch-MagnifierLabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Input {
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
height: 4.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: black;
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
flex: auto;
|
||||
min-width: 0;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Input:focus {
|
||||
outline: 2px dotted transparent;
|
||||
}
|
||||
|
||||
.DocSearch-Input::-webkit-search-cancel-button,
|
||||
.DocSearch-Input::-webkit-search-decoration,
|
||||
.DocSearch-Input::-webkit-search-results-button,
|
||||
.DocSearch-Input::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Reset {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Reset::before {
|
||||
content: "esc";
|
||||
}
|
||||
|
||||
.DocSearch-Cancel {
|
||||
flex: none;
|
||||
font-size: 0;
|
||||
border-radius: 0.375rem;
|
||||
background-color: ${theme.colors.gray["50"]};
|
||||
border: 1px solid ${theme.colors.gray["300"]};
|
||||
padding: 0.125rem 0.375rem;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
background-color: ${theme.colors.gray["700"]};
|
||||
border-color: ${theme.colors.gray["600"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Cancel::before {
|
||||
content: "esc";
|
||||
color: ${theme.colors.gray["400"]};
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.DocSearch-Reset svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-source {
|
||||
line-height: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: ${theme.colors.gray["600"]};
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
.chakra-ui-dark & {
|
||||
color: ${theme.colors.gray["400"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Hit-Container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-Tree {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-icon {
|
||||
flex: none;
|
||||
margin-right: 0.875rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-icon path {
|
||||
stroke-width: 2px;
|
||||
stroke: #71717a;
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-icon path {
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-content-wrapper {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-path {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
font-weight: 500;
|
||||
color: ${theme.colors.gray["500"]};
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-path {
|
||||
color: ${theme.colors.teal["200"]};
|
||||
}
|
||||
|
||||
.DocSearch-Dropdown ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-title {
|
||||
color: black;
|
||||
line-height: 1.5rem;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-title + .DocSearch-Hit-path {
|
||||
margin-bottom: 0.125rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-action {
|
||||
flex: none;
|
||||
margin-left: 0.875rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-action-button {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-action + .DocSearch-Hit-action {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-action path {
|
||||
stroke-width: 2px;
|
||||
stroke: #71717a;
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] .DocSearch-Hit-action path {
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.DocSearch-Hit > a {
|
||||
display: block;
|
||||
background: ${theme.colors.gray["50"]};
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
padding: 0 1.25rem 0 1rem;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
background: ${theme.colors.gray["600"]};
|
||||
* {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] > a {
|
||||
background: ${theme.colors.teal["500"]};
|
||||
}
|
||||
|
||||
.DocSearch-Hit + .DocSearch-Hit {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.DocSearch-Hit--Child {
|
||||
padding-left: 1.75rem;
|
||||
}
|
||||
|
||||
.DocSearch-Hit--Child::before,
|
||||
.DocSearch-Hit--Child
|
||||
+ .DocSearch-Hit:not(.DocSearch-Hit--Child)::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -0.25rem;
|
||||
bottom: -0.25rem;
|
||||
left: 0.5rem;
|
||||
width: 1.25rem;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='200' viewBox='0 0 12 200' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 1 0 V 200 M 1 100 H 12' stroke='%23a1a1aa' stroke-width='2'/%3E%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center left;
|
||||
}
|
||||
|
||||
.DocSearch-Hit--Child:last-child::before,
|
||||
.DocSearch-Hit--Child
|
||||
+ .DocSearch-Hit:not(.DocSearch-Hit--Child)::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='12' height='200' viewBox='0 0 12 200' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 1 0 V 89 Q 1 100 12 100' stroke='%23a1a1aa' stroke-width='2'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
.DocSearch-Hit:not(.DocSearch-Hit--Child) + .DocSearch-Hit--Child::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -0.25rem;
|
||||
left: 0;
|
||||
width: 1.25rem;
|
||||
height: 0.25rem;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.DocSearch-Hit--Child
|
||||
+ .DocSearch-Hit:not(.DocSearch-Hit--Child)::before {
|
||||
top: auto;
|
||||
bottom: calc(100% + 0.25rem);
|
||||
height: calc(100% + 0.25rem);
|
||||
background-color: #fff;
|
||||
.chakra-ui-dark & {
|
||||
background-color: ${theme.colors.gray["700"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Hits mark {
|
||||
background: none;
|
||||
color: ${theme.colors.teal["500"]};
|
||||
}
|
||||
|
||||
.DocSearch-Hit[aria-selected="true"] mark {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.DocSearch-Footer {
|
||||
flex: none;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 0 1.5rem;
|
||||
border-top: 1px solid ${theme.colors.gray["200"]};
|
||||
padding: 1.25rem 0;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
border-top-color: ${theme.colors.gray["600"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Commands {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #5d6494;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.DocSearch-Logo svg {
|
||||
color: ${theme.colors.teal["500"]};
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.DocSearch-Label {
|
||||
.chakra-ui-dark & {
|
||||
opacity: 0.5;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-Hit--deleting,
|
||||
.DocSearch-Hit--favoriting {
|
||||
opacity: 0;
|
||||
transition: all 250ms linear;
|
||||
}
|
||||
|
||||
.DocSearch-NoResults .DocSearch-Screen-Icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.DocSearch-Title {
|
||||
font-size: ${theme.fontSizes.lg};
|
||||
line-height: 1.2rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.DocSearch-Title strong {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.DocSearch-StartScreen,
|
||||
.DocSearch-NoResults {
|
||||
padding-top: 2.5rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.DocSearch-StartScreen .DocSearch-Help {
|
||||
font-size: ${theme.fontSizes.md};
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.DocSearch-NoResults-Prefill-List .DocSearch-Help {
|
||||
font-size: ${theme.fontSizes.xs};
|
||||
line-height: 1rem;
|
||||
letter-spacing: ${theme.letterSpacings.wide};
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid ${theme.colors.gray["200"]};
|
||||
|
||||
.chakra-ui-dark & {
|
||||
border-bottom: 1px solid ${theme.colors.gray["600"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-NoResults-Prefill-List li {
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid ${theme.colors.gray["200"]};
|
||||
.chakra-ui-dark & {
|
||||
border-bottom: 1px solid ${theme.colors.gray["600"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-NoResults-Prefill-List button {
|
||||
font-weight: 500;
|
||||
color: ${theme.colors.teal["600"]};
|
||||
.chakra-ui-dark & {
|
||||
color: ${theme.colors.teal["400"]};
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch-NoResults-Prefill-List + .DocSearch-Help {
|
||||
font-size: ${theme.fontSizes.sm};
|
||||
line-height: 1.25rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.DocSearch-NoResults-Prefill-List + .DocSearch-Help a {
|
||||
color: ${theme.colors.teal["600"]};
|
||||
font-weight: 500;
|
||||
|
||||
.chakra-ui-dark & {
|
||||
color: ${theme.colors.teal["300"]};
|
||||
}
|
||||
}
|
||||
`}
|
||||
/>
|
||||
)
|
||||
|
||||
export default SearchStyle
|
@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import { NextSeo, NextSeoProps } from "next-seo"
|
||||
import siteConfig from "configs/site-config"
|
||||
|
||||
export interface SEOProps extends Pick<NextSeoProps, "title" | "description"> {}
|
||||
|
||||
const SEO = ({ title, description }: SEOProps) => (
|
||||
<NextSeo
|
||||
title={title}
|
||||
description={description}
|
||||
openGraph={{ title, description }}
|
||||
titleTemplate={siteConfig.seo.titleTemplate}
|
||||
/>
|
||||
)
|
||||
|
||||
export default SEO
|
@ -0,0 +1,20 @@
|
||||
import { useRouter } from "next/router"
|
||||
import { useEffect } from "react"
|
||||
|
||||
const useRouteChanged = (fn: () => void) => {
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
const handleRouteChange = (url: string) => {
|
||||
fn()
|
||||
console.log("App is changing to: ", url)
|
||||
}
|
||||
|
||||
router.events.on("routeChangeComplete", handleRouteChange)
|
||||
|
||||
return () => {
|
||||
router.events.off("routeChangeComplete", handleRouteChange)
|
||||
}
|
||||
}, [router.events, fn])
|
||||
}
|
||||
|
||||
export default useRouteChanged
|
@ -0,0 +1,18 @@
|
||||
import siteConfig from "configs/site-config"
|
||||
|
||||
export function getSeo({
|
||||
omitOpenGraphImage,
|
||||
}: {
|
||||
omitOpenGraphImage: boolean
|
||||
}) {
|
||||
const { seo } = siteConfig
|
||||
const { images, ...openGraph } = seo.openGraph
|
||||
|
||||
return {
|
||||
...seo,
|
||||
openGraph: {
|
||||
...openGraph,
|
||||
images: omitOpenGraphImage ? undefined : images,
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
import { extendTheme } from "@chakra-ui/react"
|
||||
import { mode } from "@chakra-ui/theme-tools"
|
||||
|
||||
const customTheme = extendTheme({
|
||||
fonts: {
|
||||
heading: "Inter, sans-serif",
|
||||
body: "Inter, sans-serif",
|
||||
},
|
||||
shadows: {
|
||||
search:
|
||||
"0 0 0 1px rgba(16,22,26,.1), 0 4px 8px rgba(16,22,26,.2), 0 18px 46px 6px rgba(16,22,26,.2)",
|
||||
},
|
||||
styles: {
|
||||
global: (props) => ({
|
||||
body: {
|
||||
color: mode("gray.700", "whiteAlpha.900")(props),
|
||||
".deleted": {
|
||||
color: "#ff8383 !important",
|
||||
fontStyle: "normal !important",
|
||||
},
|
||||
".inserted": {
|
||||
color: "#b5f4a5 !important",
|
||||
fontStyle: "normal !important",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
textStyles: {
|
||||
heading: {
|
||||
fontFamily: "heading",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
letterSpacing: "-0.015em",
|
||||
lineHeight: "1.24",
|
||||
fontSize: { base: "2rem", md: "3.5rem" },
|
||||
},
|
||||
"heading-2": {
|
||||
fontFamily: "heading",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
letterSpacing: "-0.015em",
|
||||
lineHeight: "1.24",
|
||||
fontSize: { base: "1.75rem", md: "2.75rem" },
|
||||
},
|
||||
caps: {
|
||||
textTransform: "uppercase",
|
||||
fontSize: "sm",
|
||||
letterSpacing: "widest",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
mdx: {
|
||||
h1: {
|
||||
mt: "2rem",
|
||||
mb: ".25rem",
|
||||
lineHeight: 1.2,
|
||||
fontWeight: "bold",
|
||||
fontSize: "1.875rem",
|
||||
letterSpacing: "-.025em",
|
||||
},
|
||||
h2: {
|
||||
mt: "4rem",
|
||||
mb: "0.5rem",
|
||||
lineHeight: 1.3,
|
||||
fontWeight: "semibold",
|
||||
fontSize: "1.5rem",
|
||||
letterSpacing: "-.025em",
|
||||
"& + h3": {
|
||||
mt: "1.5rem",
|
||||
},
|
||||
},
|
||||
h3: {
|
||||
mt: "3rem",
|
||||
// mb: "0.5rem",
|
||||
lineHeight: 1.25,
|
||||
fontWeight: "semibold",
|
||||
fontSize: "1.25rem",
|
||||
letterSpacing: "-.025em",
|
||||
},
|
||||
h4: {
|
||||
mt: "3rem",
|
||||
lineHeight: 1.375,
|
||||
fontWeight: "semibold",
|
||||
fontSize: "1.125rem",
|
||||
},
|
||||
a: {
|
||||
color: "teal.500",
|
||||
fontWeight: "semibold",
|
||||
transition: "color 0.15s",
|
||||
transitionTimingFunction: "ease-out",
|
||||
_hover: {
|
||||
color: "teal.600",
|
||||
},
|
||||
},
|
||||
p: {
|
||||
mt: "1.25rem",
|
||||
lineHeight: 1.7,
|
||||
"blockquote &": {
|
||||
mt: 0,
|
||||
},
|
||||
},
|
||||
hr: {
|
||||
my: "4rem",
|
||||
},
|
||||
blockquote: {
|
||||
bg: "orange.100",
|
||||
borderWidth: "1px",
|
||||
borderColor: "orange.200",
|
||||
rounded: "lg",
|
||||
px: "1.25rem",
|
||||
py: "1rem",
|
||||
my: "1.5rem",
|
||||
},
|
||||
ul: {
|
||||
mt: "1.5rem",
|
||||
ml: "1.25rem",
|
||||
"blockquote &": { mt: 0 },
|
||||
"& > * + *": {
|
||||
mt: "0.25rem",
|
||||
},
|
||||
},
|
||||
code: {
|
||||
rounded: "sm",
|
||||
px: "1",
|
||||
fontSize: "0.875em",
|
||||
py: "2px",
|
||||
whiteSpace: "nowrap",
|
||||
lineHeight: "normal",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default customTheme
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"hooks/*": ["src/hooks/*"],
|
||||
"components/*": ["src/components/*"],
|
||||
"utils/*": ["src/utils/*"],
|
||||
"analytics/*": ["src/analytics/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
Loading…
Reference in new issue