mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-30 18:12:27 +00:00
The platform is what they were, so the menus name it once and the column under it lists what the platform actually offers. Co-authored-by: Hanzo Dev <[email protected]>
1047 lines
26 KiB
TypeScript
1047 lines
26 KiB
TypeScript
import { GuiLogo, ThemeTint, ThemeTintAlt } from '@hanzogui/logo'
|
|
import {
|
|
Asterisk,
|
|
Box,
|
|
Check,
|
|
CheckCircle,
|
|
ChevronRight,
|
|
Copy,
|
|
File,
|
|
Link as LinkIcon,
|
|
} from '@hanzogui/lucide-icons-2'
|
|
import type { Href } from 'one'
|
|
import React, { useState } from 'react'
|
|
import { ScrollView } from 'react-native'
|
|
import type { ImageProps, XStackProps } from '@hanzo/gui'
|
|
import {
|
|
Adapt,
|
|
Button,
|
|
H1,
|
|
H2,
|
|
H3,
|
|
H4,
|
|
H5,
|
|
Image,
|
|
Paragraph,
|
|
Separator,
|
|
SizableText,
|
|
Spacer,
|
|
Text,
|
|
Theme,
|
|
TooltipSimple,
|
|
View,
|
|
XGroup,
|
|
XStack,
|
|
YStack,
|
|
styled,
|
|
} from '@hanzo/gui'
|
|
import { LinearGradient } from '@hanzogui/linear-gradient'
|
|
import { Code, CodeInline } from '~/components/Code'
|
|
import { CustomTabs } from '~/components/CustomTabs'
|
|
import { DataTable } from '~/components/DataTable'
|
|
import { Features } from '~/components/Features'
|
|
import { HR } from '~/components/HR'
|
|
import { LI } from '~/components/LI'
|
|
import { Link } from '~/components/Link'
|
|
import { LogoCard } from '~/components/LogoCard'
|
|
import { Notice, NoticeFrame } from '~/components/Notice'
|
|
import { OffsetBox } from '~/components/OffsetBox'
|
|
import { Preview } from '~/components/Preview'
|
|
import { SubTitle } from '~/components/SubTitle'
|
|
import { GuiCard } from '~/components/GuiCard'
|
|
import { GuiExamplesCode } from '~/components/GuiExamples'
|
|
import { UL } from '~/components/UL'
|
|
import { SponsorButton } from '~/features/docs/SponsorButton'
|
|
import { ExternalIcon } from '~/features/icons/ExternalIcon'
|
|
import { BenchmarkChart } from '~/features/site/benchmarks/BenchmarkChart'
|
|
import { BenchmarkChartNative } from '~/features/site/benchmarks/BenchmarkChartNative'
|
|
import { BenchmarkChartWeb } from '~/features/site/benchmarks/BenchmarkChartWeb'
|
|
import { MediaPlayer } from '~/features/site/home/MediaPlayer'
|
|
import { SocialLinksRow } from '~/features/site/home/SocialLinksRow'
|
|
import { unwrapText } from '~/helpers/unwrapText'
|
|
import { PACKAGE_MANAGERS, pkgCommands, useBashCommand } from '~/hooks/useBashCommand'
|
|
import { useClipboard } from '~/hooks/useClipboard'
|
|
import { DocCodeBlock } from '../docs/DocsCodeBlock'
|
|
import { HeroContainer } from '../docs/HeroContainer'
|
|
import { Highlights } from '../docs/Highlights'
|
|
import { InlineTabs } from '../docs/InlineTabs'
|
|
import { PropsTable } from '../docs/PropsTable'
|
|
import { VersionSwitcher } from '../docs/VersionSwitcher'
|
|
import * as Demos from '../docs/demos'
|
|
import { ExampleAnimations } from '../site/home/HomeAnimations'
|
|
import { TabsTabProps } from '@hanzo/gui'
|
|
import { Tab } from '~/components/RovingTabs'
|
|
import { SimpleTable } from './SimpleTable'
|
|
|
|
if (!React.version.startsWith('19')) {
|
|
console.error(`\n\n\nNot on React 19 ❌\n\n\n\n`)
|
|
}
|
|
|
|
const IntroParagraph = ({ children, large, disableUnwrapText, ...props }: any) => {
|
|
return (
|
|
<Paragraph
|
|
render="p"
|
|
// ff="$mono"
|
|
size={large ? '$7' : '$6'}
|
|
mb="$4"
|
|
color="$accent1"
|
|
$sm={{
|
|
size: '$5',
|
|
}}
|
|
{...props}
|
|
>
|
|
{disableUnwrapText ? children : unwrapText(children)}
|
|
</Paragraph>
|
|
)
|
|
}
|
|
|
|
const TableFrame = styled(YStack, {
|
|
borderWidth: 1,
|
|
borderColor: '$borderColor',
|
|
rounded: '$4',
|
|
overflow: 'hidden',
|
|
my: '$4',
|
|
})
|
|
|
|
const Table = ({ heading, children, ...props }) => {
|
|
return (
|
|
<TableFrame className="no-scrollbar" overflow={'scroll' as any} {...props}>
|
|
{!!heading && (
|
|
<TableCell size="$4" bg="$color1" fontWeight="500" color="$color9">
|
|
{heading}
|
|
</TableCell>
|
|
)}
|
|
<XStack minW="100%" items="stretch">
|
|
{children}
|
|
</XStack>
|
|
</TableFrame>
|
|
)
|
|
}
|
|
|
|
const code = (props) => {
|
|
const {
|
|
showMore,
|
|
hero,
|
|
line,
|
|
scrollable,
|
|
className,
|
|
children,
|
|
id,
|
|
showLineNumbers,
|
|
collapsible,
|
|
...rest
|
|
} = props
|
|
if (!className) {
|
|
return <CodeInline>{unwrapText(children)}</CodeInline>
|
|
}
|
|
return (
|
|
<YStack mt="$3">
|
|
<DocCodeBlock
|
|
isHighlightingLines={line !== undefined}
|
|
className={className}
|
|
isHero={hero !== undefined}
|
|
showMore={showMore}
|
|
showLineNumbers={showLineNumbers !== undefined}
|
|
{...rest}
|
|
>
|
|
{children}
|
|
</DocCodeBlock>
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
const TableCell = styled(Paragraph, {
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '$borderColor',
|
|
flexDirection: 'row',
|
|
items: 'center',
|
|
position: 'relative',
|
|
flex: 1,
|
|
flexBasis: 'auto',
|
|
justify: 'center',
|
|
text: 'center',
|
|
height: '$4',
|
|
p: '$2',
|
|
px: '$3',
|
|
size: '$5',
|
|
ellipsis: true,
|
|
|
|
variants: {
|
|
head: {
|
|
true: {
|
|
bg: '$color1',
|
|
},
|
|
},
|
|
highlight: {
|
|
true: {
|
|
bg: '$yellow2',
|
|
},
|
|
},
|
|
} as const,
|
|
})
|
|
|
|
const TableCol = styled(YStack, {
|
|
borderRightWidth: 1,
|
|
borderRightColor: '$borderColor',
|
|
flex: 1,
|
|
flexBasis: 'auto',
|
|
mr: -1,
|
|
flexDirection: 'column',
|
|
})
|
|
|
|
const TableHighlight = styled(YStack, {
|
|
fullscreen: true,
|
|
bg: '$yellow1',
|
|
})
|
|
|
|
const componentsIn = {
|
|
Tabs: CustomTabs,
|
|
InlineTabs: InlineTabs,
|
|
|
|
SocialLinksRow: () => (
|
|
<YStack mt="$6" mx="$-4">
|
|
<SocialLinksRow />
|
|
</YStack>
|
|
),
|
|
|
|
Wide: (props) => (
|
|
<YStack mx="$-8" $sm={{ mx: '$-2' }}>
|
|
{props.children}
|
|
</YStack>
|
|
),
|
|
|
|
Adapt,
|
|
|
|
Table,
|
|
TableCell,
|
|
TableHighlight,
|
|
TableCol,
|
|
|
|
Spacer,
|
|
ExampleAnimations,
|
|
ScrollView,
|
|
Features,
|
|
Text,
|
|
Paragraph,
|
|
OffsetBox,
|
|
YStack,
|
|
XStack,
|
|
Theme,
|
|
BenchmarkChart,
|
|
Separator,
|
|
Code,
|
|
HeroContainer,
|
|
BenchmarkChartNative,
|
|
BenchmarkChartWeb,
|
|
TooltipSimple,
|
|
|
|
...Demos,
|
|
|
|
GuiDemo: () => {
|
|
return <GuiLogo />
|
|
},
|
|
|
|
Highlights,
|
|
VersionSwitcher,
|
|
ThemeTint,
|
|
PropsTable,
|
|
DataTable,
|
|
Description: SubTitle,
|
|
UL,
|
|
LI,
|
|
Link,
|
|
Strong: (props) => (
|
|
<Paragraph render="strong" fontSize="inherit" fontWeight="700" {...props} />
|
|
),
|
|
|
|
GuiExamplesCode,
|
|
|
|
InstallBanner: ({ name = '' }) => {
|
|
const {
|
|
transformedCommand,
|
|
isInstallCommand,
|
|
isExecCommand,
|
|
isCreateCommand,
|
|
selectedPackageManager,
|
|
setPackageManager,
|
|
} = useBashCommand(`yarn add ${name}`, 'language-bash')
|
|
|
|
const { transformedCommand: hanzoguiCommand } = useBashCommand(
|
|
`npm install hanzogui`,
|
|
'language-bash'
|
|
)
|
|
const { onCopy, hasCopied } = useClipboard(transformedCommand)
|
|
const hanzoguiCmdClip = useClipboard(hanzoguiCommand)
|
|
|
|
const CopyIcon = hasCopied ? Check : Copy
|
|
const CopyIcon2 = hanzoguiCmdClip.hasCopied ? Check : Copy
|
|
|
|
return (
|
|
<XStack flexWrap="wrap" items="center" gap="$4">
|
|
{name && (
|
|
<ThemeTint>
|
|
<TooltipSimple
|
|
restMs={1200}
|
|
delay={{
|
|
open: 1200,
|
|
close: 0,
|
|
}}
|
|
label={hasCopied ? 'Copied' : 'Copy to clipboard'}
|
|
>
|
|
<XStack
|
|
items="center"
|
|
gap="$2"
|
|
my="$1"
|
|
py="$1"
|
|
px="$2"
|
|
self="flex-start"
|
|
bg="$color2"
|
|
rounded="$3"
|
|
cursor="pointer"
|
|
onPress={onCopy}
|
|
>
|
|
<SizableText color="$color11">{transformedCommand}</SizableText>
|
|
|
|
<CopyIcon
|
|
p="$0.5"
|
|
size={16}
|
|
color="$color10"
|
|
hoverStyle={{
|
|
color: '$color2',
|
|
}}
|
|
/>
|
|
</XStack>
|
|
</TooltipSimple>
|
|
</ThemeTint>
|
|
)}
|
|
|
|
{(isInstallCommand || isExecCommand || isCreateCommand) && (
|
|
<>
|
|
<TooltipSimple label="« Individually or all-in-one »">
|
|
<XStack items="center">
|
|
<SizableText pointerEvents="none" size="$3">
|
|
or
|
|
</SizableText>
|
|
<Asterisk size={12} y={-8} />
|
|
</XStack>
|
|
</TooltipSimple>
|
|
|
|
<ThemeTintAlt>
|
|
<TooltipSimple
|
|
restMs={1200}
|
|
delay={{
|
|
open: 1200,
|
|
close: 0,
|
|
}}
|
|
label={hanzoguiCmdClip.hasCopied ? 'Copied' : 'Copy to clipboard'}
|
|
>
|
|
<XStack
|
|
items="center"
|
|
gap="$2"
|
|
my="$1"
|
|
py="$1"
|
|
px="$2"
|
|
self="flex-start"
|
|
bg="$color2"
|
|
rounded="$3"
|
|
cursor="pointer"
|
|
onPress={hanzoguiCmdClip.onCopy}
|
|
>
|
|
<SizableText color="$color11">{hanzoguiCommand}</SizableText>
|
|
|
|
<CopyIcon2
|
|
p="$0.5"
|
|
size={16}
|
|
color="$color10"
|
|
hoverStyle={{
|
|
color: '$color2',
|
|
}}
|
|
/>
|
|
</XStack>
|
|
</TooltipSimple>
|
|
</ThemeTintAlt>
|
|
</>
|
|
)}
|
|
|
|
<XStack gap="$2">
|
|
{Object.keys(pkgCommands).map((c) => {
|
|
const isActive = selectedPackageManager === c
|
|
return (
|
|
<SizableText
|
|
cursor="pointer"
|
|
onPress={() => {
|
|
setPackageManager(c)
|
|
}}
|
|
color="$color12"
|
|
opacity={isActive ? 0.8 : 0.5}
|
|
hoverStyle={{
|
|
opacity: 0.8,
|
|
}}
|
|
key={c}
|
|
>
|
|
{c}
|
|
</SizableText>
|
|
)
|
|
})}
|
|
</XStack>
|
|
</XStack>
|
|
)
|
|
},
|
|
|
|
TLDR: (props) => {
|
|
return (
|
|
<YStack
|
|
$gtMd={{ mx: '$-4' }}
|
|
mt="$5"
|
|
mb="$3"
|
|
px="$6"
|
|
py="$2"
|
|
rounded="$6"
|
|
borderWidth={1}
|
|
opacity={0.8}
|
|
borderColor="$borderColor"
|
|
{...props}
|
|
/>
|
|
)
|
|
},
|
|
|
|
Button,
|
|
|
|
Beta: () => (
|
|
<Button
|
|
aria-label="Beta blog post"
|
|
pointerEvents="none"
|
|
size="$2"
|
|
theme="yellow"
|
|
position="absolute"
|
|
t={-15}
|
|
r={-25}
|
|
rotate="5deg"
|
|
>
|
|
Beta
|
|
</Button>
|
|
),
|
|
|
|
IntroParagraph,
|
|
|
|
// A grid, now that gui can say so. It was a wrapping XStack with
|
|
// space-between, which leaves the last row spread across the full width
|
|
// however few cards are in it — the classic flex-gallery orphan row. Even
|
|
// tracks are what a gallery wants, and auto-fit picks the count from the
|
|
// width with no breakpoint.
|
|
Grid: (props) => (
|
|
<View
|
|
display="grid"
|
|
gridTemplateColumns="repeat(auto-fit, minmax(min(240px, 100%), 1fr))"
|
|
{...props}
|
|
/>
|
|
),
|
|
Card: GuiCard,
|
|
|
|
LogoCard: LogoCard,
|
|
NextJSRouterCard: (props) => {
|
|
return (
|
|
<LogoCard
|
|
icon={props.title.startsWith('Pages') ? <File size="$1" /> : <Box size="$1" />}
|
|
{...props}
|
|
/>
|
|
)
|
|
},
|
|
|
|
Note: (props) => <YStack render="aside" mt="$5" mb="$5" borderRadius="$3" {...props} />,
|
|
|
|
Notice,
|
|
|
|
h1: (props) => (
|
|
<H1 fontFamily="$mono" width="max-content" pos="relative" mb="$2" {...props} />
|
|
),
|
|
|
|
h2: ({ children, ...props }) => (
|
|
<H2
|
|
position="relative"
|
|
width={`fit-content` as any}
|
|
mt="$8"
|
|
mb="$4"
|
|
data-heading
|
|
size="$9"
|
|
color="$color12"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</H2>
|
|
),
|
|
|
|
h3: ({ children, id, ...props }) => (
|
|
<LinkHeading data-heading mt="$6" mb="$2" id={id}>
|
|
<H3
|
|
maxW="100%"
|
|
position="relative"
|
|
width={`fit-content` as any}
|
|
id={id}
|
|
size="$8"
|
|
color="$color11"
|
|
fontWeight="600"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</H3>
|
|
{getNonTextChildren(children)}
|
|
</LinkHeading>
|
|
),
|
|
|
|
h4: (props) => (
|
|
<H4
|
|
position="relative"
|
|
width={`fit-content` as any}
|
|
mt="$8"
|
|
mb="$2"
|
|
size="$8"
|
|
color="$color8"
|
|
data-heading
|
|
{...props}
|
|
fontWeight="400"
|
|
/>
|
|
),
|
|
|
|
h5: (props) => <H5 size="$6" fontWeight="600" mt="$4" {...props} />,
|
|
|
|
p: (props) => (
|
|
<Paragraph
|
|
className="docs-paragraph"
|
|
display="block"
|
|
size="$6"
|
|
my="$2"
|
|
color="$color11"
|
|
{...props}
|
|
/>
|
|
),
|
|
|
|
a: ({ href = '', children, ...props }) => {
|
|
return (
|
|
<Link className="link" href={href as Href} asChild>
|
|
<Paragraph
|
|
render="a"
|
|
// @ts-ignore
|
|
fontSize="inherit"
|
|
display="inline"
|
|
cursor="pointer"
|
|
focusVisibleStyle={{
|
|
outlineColor: '$outlineColor',
|
|
outlineWidth: 2,
|
|
outlineStyle: 'solid',
|
|
outlineOffset: 2,
|
|
}}
|
|
{...props}
|
|
>
|
|
{children}
|
|
{href.startsWith('http') ? (
|
|
<>
|
|
|
|
<Text
|
|
// @ts-ignore
|
|
fontSize="inherit"
|
|
display="inline-flex"
|
|
y={2}
|
|
ml={-1}
|
|
>
|
|
<ExternalIcon />
|
|
</Text>
|
|
</>
|
|
) : null}
|
|
</Paragraph>
|
|
</Link>
|
|
)
|
|
},
|
|
|
|
hr: HR,
|
|
|
|
ul: ({ children }) => {
|
|
return (
|
|
<UL render="ul" my="$4">
|
|
{React.Children.toArray(children).map((x) => (typeof x === 'string' ? null : x))}
|
|
</UL>
|
|
)
|
|
},
|
|
|
|
ol: (props) => <YStack {...props} render="ol" mb="$3" />,
|
|
|
|
li: (props) => {
|
|
return (
|
|
<LI
|
|
render="li"
|
|
size="$6"
|
|
mb="$1.5"
|
|
className="docs-paragraph"
|
|
style={{
|
|
listStyleType: 'disc',
|
|
}}
|
|
>
|
|
{props.children}
|
|
</LI>
|
|
)
|
|
},
|
|
|
|
strong: (props) => (
|
|
<Paragraph render="strong" fontSize="inherit" {...props} fontWeight="700" />
|
|
),
|
|
|
|
img: ({ ...props }) => (
|
|
<YStack render="span" my="$6">
|
|
<YStack render="img" {...props} maxW="100%" />
|
|
</YStack>
|
|
),
|
|
|
|
pre: ({ children }) => <>{children}</>,
|
|
|
|
code,
|
|
|
|
Image: ({
|
|
children,
|
|
size,
|
|
overlap,
|
|
linked,
|
|
...props
|
|
}: ImageProps & { size?: 'hero'; overlap?: boolean; linked?: boolean }) => {
|
|
const content = (
|
|
<OffsetBox
|
|
size={size}
|
|
render="figure"
|
|
flex={1}
|
|
flexBasis="auto"
|
|
mx={0}
|
|
mb="$3"
|
|
items="center"
|
|
justify="center"
|
|
overflow="hidden"
|
|
{...(overlap && {
|
|
mt: '$-6',
|
|
})}
|
|
>
|
|
<Image maxW="100%" {...props} />
|
|
{!!children && (
|
|
<Text render="figcaption" lineHeight={23} color="$colorPress" mt="$2">
|
|
{children}
|
|
</Text>
|
|
)}
|
|
</OffsetBox>
|
|
)
|
|
|
|
if (linked) {
|
|
return (
|
|
<Link target="_blank" href={props.src as Href}>
|
|
{content}
|
|
</Link>
|
|
)
|
|
}
|
|
|
|
return content
|
|
},
|
|
|
|
Video: ({
|
|
small,
|
|
large,
|
|
src,
|
|
children = '',
|
|
muted = true,
|
|
autoPlay = true,
|
|
controls,
|
|
size,
|
|
...props
|
|
}) => (
|
|
<YStack render="figure" mx={0} my="$6">
|
|
<OffsetBox size={size}>
|
|
<video
|
|
src={src}
|
|
autoPlay={autoPlay}
|
|
playsInline
|
|
muted={muted}
|
|
controls={controls}
|
|
loop
|
|
style={{ width: '100%', display: 'block' }}
|
|
></video>
|
|
</OffsetBox>
|
|
<Text render="figcaption" lineHeight={23} mt="$2" color="$colorPress">
|
|
{children}
|
|
</Text>
|
|
</YStack>
|
|
),
|
|
|
|
blockquote: ({ children, ...props }) => {
|
|
return (
|
|
<YStack
|
|
my="$4"
|
|
px="$6"
|
|
ml="$3"
|
|
borderLeftWidth={1}
|
|
borderColor="$borderColor"
|
|
justify="center"
|
|
{...props}
|
|
>
|
|
<Paragraph
|
|
fontFamily="$pixel"
|
|
whiteSpace="revert"
|
|
size="$8"
|
|
lineHeight="$9"
|
|
fontWeight="300"
|
|
color="$color"
|
|
opacity={0.65}
|
|
>
|
|
{unwrapText(children)}
|
|
</Paragraph>
|
|
</YStack>
|
|
)
|
|
},
|
|
|
|
Preview: (props) => {
|
|
return <Preview {...props} mt="$5" />
|
|
},
|
|
|
|
MediaPlayerDemo: ({ theme, ...props }) => {
|
|
return (
|
|
<Theme name={theme}>
|
|
<MediaPlayer {...props} />
|
|
</Theme>
|
|
)
|
|
},
|
|
|
|
GroupDisabledDemo: () => {
|
|
return (
|
|
<XGroup items="center" disabled>
|
|
<XGroup.Item>
|
|
<Button>First</Button>
|
|
</XGroup.Item>
|
|
<XGroup.Item>
|
|
<Button>Second</Button>
|
|
</XGroup.Item>
|
|
<XGroup.Item>
|
|
<Button>Third</Button>
|
|
</XGroup.Item>
|
|
</XGroup>
|
|
)
|
|
},
|
|
|
|
DemoButton: () => <Button>Hello world</Button>,
|
|
|
|
SponsorButton,
|
|
|
|
SponsorNotice: () => {
|
|
return (
|
|
<NoticeFrame theme="red">
|
|
<YStack maxW="100%" gap="$4">
|
|
<H4 color="$color10" fontFamily="$pixel">
|
|
👋 Hey! Listen!
|
|
</H4>
|
|
<YStack overflow="hidden" flex={1} flexBasis="auto" opacity={0.85} gap="$4">
|
|
<Paragraph>
|
|
Gui is fully OSS, self-funded and built by{' '}
|
|
<a href="https://x.com/natebirdman" target="_blank" rel="noreferrer">
|
|
me
|
|
</a>
|
|
.
|
|
</Paragraph>
|
|
<Paragraph>
|
|
My goal is to support Gui development with sponsorships that get early
|
|
access to <a href="#sponsors">some really interesting</a> new features.
|
|
</Paragraph>
|
|
<SponsorButton />
|
|
</YStack>
|
|
</YStack>
|
|
</NoticeFrame>
|
|
)
|
|
},
|
|
|
|
Blog: {
|
|
ThemeBuilder: {
|
|
ExamplePalette: React.lazy(() =>
|
|
import('../site/blog/BlogThemeBuilderExamples').then((x) => ({
|
|
default: x.ExamplePalette,
|
|
}))
|
|
),
|
|
ExampleTemplate: React.lazy(() =>
|
|
import('../site/blog/BlogThemeBuilderExamples').then((x) => ({
|
|
default: x.ExampleTemplate,
|
|
}))
|
|
),
|
|
},
|
|
},
|
|
|
|
DocsIntro: () => {
|
|
return (
|
|
<YStack gap="$1">
|
|
<ThemeTintAlt offset={2}>
|
|
<IntroParagraph large mt="$4">
|
|
Gui makes styling React on any platform a delight. All of its features work
|
|
the same on both React Native and React web.
|
|
</IntroParagraph>
|
|
|
|
<UL mt="$4" pl="$4" gap="$2">
|
|
<Theme name="gray">
|
|
<LI mb="$4" size="$6" color="$color11">
|
|
{/* @ts-ignore */}
|
|
<Link fontSize="inherit" href="/docs/core/configuration">
|
|
<CodeInline>
|
|
<span style={{ color: 'var(--color12)' }}>@hanzogui/core</span>
|
|
</CodeInline>
|
|
</Link>
|
|
is the base style library, it expands on the React Native style API
|
|
with many features from CSS, all without a single external dependency. It
|
|
can entirely replace React Native Web in a much lighter package, with full
|
|
API compatibility, much improved SSR, more features, and much better
|
|
performance.
|
|
</LI>
|
|
</Theme>
|
|
|
|
<Theme name="gray">
|
|
<LI mb="$4" size="$6" color="$color11">
|
|
{/* @ts-ignore */}
|
|
<Link fontSize="inherit" href="/docs/intro/compiler-install">
|
|
<CodeInline>
|
|
<span style={{ color: 'var(--color12)' }}>@hanzogui/static</span>
|
|
</CodeInline>
|
|
</Link>{' '}
|
|
is an optimizing compiler that{' '}
|
|
<Link
|
|
// @ts-ignore
|
|
fontSize="inherit"
|
|
href="/docs/intro/benchmarks"
|
|
>
|
|
significantly improves performance
|
|
</Link>{' '}
|
|
through partial analysis, hoisting, and flattening. It makes sharing code
|
|
between web and native actually feel great.
|
|
</LI>
|
|
</Theme>
|
|
|
|
<Theme name="gray">
|
|
<LI mb="$4" size="$6" color="$color11">
|
|
{/* @ts-ignore */}
|
|
<Link fontSize="inherit" href="/ui/intro">
|
|
<CodeInline>
|
|
<span style={{ color: 'var(--color12)' }}>hanzogui UI</span>
|
|
</CodeInline>
|
|
</Link>{' '}
|
|
is a bunch of unstyled and styled components for building common UI
|
|
elements. It's similar to Radix, but works on native and web, and has a
|
|
powerful Adapt primitive to shapeshift UI based on the platform or media
|
|
query.
|
|
</LI>
|
|
</Theme>
|
|
</UL>
|
|
</ThemeTintAlt>
|
|
</YStack>
|
|
)
|
|
},
|
|
|
|
GetStarted: () => {
|
|
const clipBoard = useClipboard(`npm create hanzogui@latest`)
|
|
|
|
return (
|
|
<XStack gap="$4" flex={1} flexBasis="auto" flexWrap="wrap" pt="$3" my="$5">
|
|
<>
|
|
<ThemeTint>
|
|
<Link asChild href="/docs/intro/installation">
|
|
<YStack
|
|
bg="$background"
|
|
position="relative"
|
|
rounded="$true"
|
|
render="a"
|
|
transition="quickest"
|
|
animateOnly={['transform']}
|
|
flex={1}
|
|
flexBasis="auto"
|
|
y={0}
|
|
hoverStyle={{ y: -2, bg: '$backgroundHover' }}
|
|
pressStyle={{ y: 2, bg: '$color2' }}
|
|
>
|
|
<YStack z={10} mb="auto" p="$true" gap="$2">
|
|
<H4 size="$4" color="$color8">
|
|
Install
|
|
</H4>
|
|
<Paragraph size="$6" color="$color9">
|
|
Set up an app.
|
|
</Paragraph>
|
|
</YStack>
|
|
|
|
<XStack z={5} mt="auto" mb={0} p="$true">
|
|
<ChevronRight position="absolute" b="$4" r="$4" color="$color11" />
|
|
</XStack>
|
|
</YStack>
|
|
</Link>
|
|
|
|
<YStack
|
|
bg="$background"
|
|
position="relative"
|
|
rounded="$true"
|
|
flex={1}
|
|
flexBasis="auto"
|
|
>
|
|
<YStack z={10} mb="auto" p="$true" gap="$2">
|
|
<H4 size="$4" color="$color9">
|
|
Quick start
|
|
</H4>
|
|
<Paragraph size="$4" color="$color11">
|
|
Choose from a few starters:
|
|
</Paragraph>
|
|
</YStack>
|
|
|
|
<XStack z={5} mt="auto" mb={0} p="$6" pt={0}>
|
|
<XStack position="relative" items="center" gap="$4" flex={1}>
|
|
<Code flex={1} bg="$color4" p="$3" rounded="$4" size="$5">
|
|
npm create hanzogui@latest
|
|
</Code>
|
|
<Button
|
|
position="absolute"
|
|
aria-label="Copy code to clipboard"
|
|
size="$2"
|
|
r="$3"
|
|
display="inline-flex"
|
|
icon={clipBoard.hasCopied ? CheckCircle : Copy}
|
|
onPress={() => {
|
|
clipBoard.onCopy()
|
|
}}
|
|
$xs={{
|
|
display: 'none',
|
|
}}
|
|
>
|
|
Copy
|
|
</Button>
|
|
</XStack>
|
|
</XStack>
|
|
</YStack>
|
|
</ThemeTint>
|
|
</>
|
|
</XStack>
|
|
)
|
|
},
|
|
|
|
Aside: ({ children, ...props }) => {
|
|
const areChildrenString = typeof children === 'string'
|
|
const shouldCutoff = !areChildrenString
|
|
const [cutoff, setCutoff] = useState(shouldCutoff)
|
|
|
|
return (
|
|
<YStack
|
|
render="aside"
|
|
gap="$2"
|
|
rounded="$4"
|
|
p="$5"
|
|
mx="$-2"
|
|
mt="$2"
|
|
position="relative"
|
|
{...(cutoff && {
|
|
my: '$4',
|
|
px: '$5',
|
|
borderWidth: 1,
|
|
pb: '$10',
|
|
bg: '$color1',
|
|
borderColor: '$borderColor',
|
|
maxH: 300,
|
|
overflow: 'hidden',
|
|
})}
|
|
{...props}
|
|
>
|
|
{areChildrenString ? (
|
|
<Paragraph color="$color10" my="$-5">
|
|
{children}
|
|
</Paragraph>
|
|
) : (
|
|
children
|
|
)}
|
|
|
|
{shouldCutoff && cutoff && (
|
|
<LinearGradient
|
|
position="absolute"
|
|
b={0}
|
|
l={0}
|
|
r={0}
|
|
height={200}
|
|
colors={['$background0', '$background']}
|
|
z={1000}
|
|
>
|
|
<Spacer flex={1} />
|
|
<Button onPress={() => setCutoff(!cutoff)} self="center">
|
|
Show more
|
|
</Button>
|
|
<Spacer size="$4" />
|
|
</LinearGradient>
|
|
)}
|
|
</YStack>
|
|
)
|
|
},
|
|
SimpleTable,
|
|
}
|
|
|
|
export class ErrorBoundary extends React.Component<{ children: any; name: string }> {
|
|
state = { hasError: false }
|
|
|
|
static getDerivedStateFromError(error) {
|
|
console.error('MDXComponent.error', error)
|
|
// Update state so the next render will show the fallback UI.
|
|
return { hasError: true }
|
|
}
|
|
|
|
componentDidCatch(error, info) {
|
|
// Example "componentStack":
|
|
// in ComponentThatThrows (created by App)
|
|
// in ErrorBoundary (created by App)
|
|
// in div (created by App)
|
|
// in App
|
|
console.error('MDXComponent.error', this.props.name, error, info)
|
|
}
|
|
|
|
render() {
|
|
if (this.state.hasError) {
|
|
return null
|
|
}
|
|
return this.props.children
|
|
}
|
|
}
|
|
|
|
export const components = Object.fromEntries(
|
|
Object.entries(componentsIn).map(([key, Component]) => {
|
|
if (typeof Component !== 'function') {
|
|
return [key, Component]
|
|
}
|
|
const Comp = Component as React.ElementType
|
|
// An error boundary around every MDX component, so a failing one names itself.
|
|
const wrapped: React.FC<Record<string, unknown>> = (props) => (
|
|
<ErrorBoundary name={key}>
|
|
<Comp {...props} />
|
|
</ErrorBoundary>
|
|
)
|
|
|
|
// A component's static members (a styled config, a displayName) travel with it.
|
|
const out = Object.assign(wrapped, Object.fromEntries(Object.entries(Component)))
|
|
|
|
return [key, out]
|
|
})
|
|
)
|
|
|
|
const LinkHeading = ({ id, children, ...props }: { id: string } & XStackProps) => (
|
|
<XStack
|
|
render="a"
|
|
data-heading
|
|
// @ts-expect-error href is an anchor prop the stack type does not carry
|
|
href={`#${id}`}
|
|
id={id}
|
|
data-id={id}
|
|
display="inline-flex"
|
|
items="center"
|
|
gap="$4"
|
|
{...props}
|
|
>
|
|
{children}
|
|
<YStack render="span" opacity={0.3}>
|
|
<LinkIcon size={12} color="var(--color)" aria-hidden />
|
|
</YStack>
|
|
</XStack>
|
|
)
|
|
|
|
const getNonTextChildren = (children) => {
|
|
return React.Children.map(children, (x) => {
|
|
if (typeof x === 'string') return null
|
|
if (x['type'] === code) return null
|
|
return x
|
|
}).flat()
|
|
}
|