mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
282 lines
7.3 KiB
TypeScript
282 lines
7.3 KiB
TypeScript
import { useStoreSelector } from '@hanzogui/use-store'
|
|
import { memo, useEffect } from 'react'
|
|
import {
|
|
Button,
|
|
H4,
|
|
Label,
|
|
ScrollView,
|
|
Separator,
|
|
SizableText,
|
|
XStack,
|
|
YGroup,
|
|
YStack,
|
|
} from '@hanzo/gui'
|
|
|
|
import { Select } from '../../../../../components/Select'
|
|
import {
|
|
ThemeBuilderStore,
|
|
themeBuilderStore,
|
|
useThemeBuilderStore,
|
|
} from '~/features/studio/theme/store/ThemeBuilderStore'
|
|
import { AddDropdown } from '../../views/AddDropdown'
|
|
import { Stage, StageButtonBar, useSteps } from '../views/Stage'
|
|
// import { ThemeBuilderPalettesPane } from '../views/ThemeBuilderPalettesPane'
|
|
// import type { PreviewComponent } from './components'
|
|
import { components } from './components'
|
|
|
|
const useComponentThemesSteps = () => {
|
|
return useSteps({ id: 'step-component-themes', total: 2, initial: 1 })
|
|
}
|
|
|
|
export const StepComponentThemes = memo(() => {
|
|
const store = useThemeBuilderStore()
|
|
const steps = useComponentThemesSteps()
|
|
const componentThemes = Object.entries(store.componentThemes)
|
|
const { selectedComponentTheme = '' } = store
|
|
|
|
// ensure first is always selected
|
|
useEffect(() => {
|
|
// timeout prevents the bug where we're left with a selected, but not applies theme
|
|
const timeoutId = setTimeout(() => {
|
|
if (componentThemes.length > 0 && !store.selectedComponentTheme) {
|
|
const id = componentThemes[0][0]
|
|
store.setSelectedComponentTheme(id)
|
|
}
|
|
}, 500)
|
|
return () => {
|
|
clearTimeout(timeoutId)
|
|
}
|
|
}, [componentThemes.length, store.selectedComponentTheme])
|
|
|
|
return (
|
|
<YStack mx="$-5" flex={1}>
|
|
<XStack
|
|
pointerEvents="auto"
|
|
z={100}
|
|
px="$4"
|
|
py="$2"
|
|
items="center"
|
|
justify="space-between"
|
|
>
|
|
<H4 fontFamily="$mono" size="$5">
|
|
Component: {selectedComponentTheme?.replace('Preview', '')}
|
|
</H4>
|
|
<SelectComponentTheme />
|
|
</XStack>
|
|
<Separator />
|
|
<StageButtonBar steps={steps} />
|
|
<Stage
|
|
current={steps.index}
|
|
steps={[<YStack key="palettes" />, <Themes key="themes" />]}
|
|
/>
|
|
</YStack>
|
|
)
|
|
})
|
|
|
|
const SelectComponentTheme = () => {
|
|
// const store = useThemeBuilderStore()
|
|
// const selected = store.selectedComponentTheme
|
|
|
|
return (
|
|
<XStack items="center" gap="$3">
|
|
<Label fontFamily="$mono">Theme:</Label>
|
|
<Select size="$3" value="ok" width={200}>
|
|
<Select.Item index={0} value="" />
|
|
</Select>
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
export const Themes = memo(() => {
|
|
// const store = useThemeBuilderStore()
|
|
|
|
return (
|
|
<YStack gap="$4" py="$4" px="$2">
|
|
{[]
|
|
// .sort((a, b) => (a.id === store.selectedComponentTheme ? -1 : 1))
|
|
.map(() => {
|
|
return null
|
|
// const handleUpdate = (next) => {
|
|
// store.updateComponentTheme({
|
|
// ...theme,
|
|
// ...next,
|
|
// })
|
|
// }
|
|
|
|
// const handleDelete = () => {
|
|
// store.deleteComponentTheme(theme)
|
|
// }
|
|
|
|
// return (
|
|
// <BuildThemeItem
|
|
// key={theme.id}
|
|
// isActive={store.selectedComponentTheme === theme.id}
|
|
// enableEditLabel
|
|
// label={theme.name}
|
|
// theme={theme}
|
|
// onUpdate={handleUpdate}
|
|
// onDelete={handleDelete}
|
|
// onPress={() => {
|
|
// store.setSelectedComponentTheme(theme.id)
|
|
// }}
|
|
// />
|
|
// )
|
|
})}
|
|
</YStack>
|
|
)
|
|
})
|
|
|
|
export function StepComponentThemesActions() {
|
|
const store = useThemeBuilderStore()
|
|
const show = store.showAddThemeMenu
|
|
const setShow = (val: boolean) => (store.showAddThemeMenu = val)
|
|
|
|
return (
|
|
<XStack items="center" gap="$4">
|
|
<AddDropdown open={show} onOpenChange={setShow}>
|
|
<YGroup>
|
|
<AddDropdown.Title>Palette Themes</AddDropdown.Title>
|
|
{[].map(
|
|
() => null
|
|
// <AddDropdown.Item
|
|
// size="$3"
|
|
// key={theme.name}
|
|
// onPress={() => {
|
|
// store.addPalette(palette)
|
|
// store.addComponentTheme(theme)
|
|
// setShow(false)
|
|
// }}
|
|
// iconAfter={<ColorThemeIndicator size={10} theme={theme} />}
|
|
// title={theme.name}
|
|
// />
|
|
)}
|
|
|
|
<AddDropdown.Separator />
|
|
</YGroup>
|
|
</AddDropdown>
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
export const StepComponentThemesPreview = () => {
|
|
return (
|
|
<YStack mt="$-8" gap="$5">
|
|
<SelectParentTheme />
|
|
<PreviewGrid />
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
const PreviewGrid = memo(() => {
|
|
return (
|
|
<XStack
|
|
gap="$4"
|
|
display="grid"
|
|
gridTemplateColumns="repeat(auto-fit, minmax(350px, 1fr))"
|
|
>
|
|
{Object.keys(components).map((name) => {
|
|
return <ThemeBuilderComponentCard key={name} name={name} />
|
|
})}
|
|
</XStack>
|
|
)
|
|
})
|
|
|
|
const SelectParentTheme = () => {
|
|
const store = useThemeBuilderStore()
|
|
|
|
return (
|
|
<XStack gap="$4">
|
|
<Label>Parent Theme:</Label>
|
|
<Select
|
|
size="$3"
|
|
width={200}
|
|
value="base"
|
|
onValueChange={(val) => {
|
|
store.componentParentTheme = val
|
|
}}
|
|
>
|
|
<Select.Item index={0} value="base">
|
|
Light/Dark
|
|
</Select.Item>
|
|
|
|
<Select.Item index={1} value="accent" disabled={!store.baseTheme.accent}>
|
|
Accent
|
|
</Select.Item>
|
|
</Select>
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
const ThemeBuilderComponentCard = memo(({ name }: { name: string }) => {
|
|
const component = components.find((c) => c.name === name)
|
|
const Preview = component?.component || (() => null)
|
|
const parts: any[] = []
|
|
const isActive = useStoreSelector(
|
|
ThemeBuilderStore,
|
|
(x) => x.selectedComponentTheme === name
|
|
)
|
|
|
|
return (
|
|
<YStack
|
|
key={name}
|
|
flex={1}
|
|
minW={300}
|
|
height={400}
|
|
overflow="hidden"
|
|
elevation="$0.5"
|
|
rounded="$4"
|
|
borderWidth={1}
|
|
borderColor="$borderColor"
|
|
{...(isActive && {
|
|
outlineColor: 'blue',
|
|
outlineWidth: 2,
|
|
outlineStyle: 'solid',
|
|
})}
|
|
onPress={() => {
|
|
if (!isActive) {
|
|
themeBuilderStore.setSelectedComponentTheme(name)
|
|
}
|
|
}}
|
|
>
|
|
<XStack position="absolute" t={0} l={0} r={0} items="center" justify="center">
|
|
<SizableText size="$5" py="$3" fontWeight="600">
|
|
{name.replace('Preview', '')}
|
|
</SizableText>
|
|
</XStack>
|
|
|
|
<YStack flex={1} items="center" justify="center">
|
|
<Preview />
|
|
</YStack>
|
|
|
|
<YStack
|
|
bg="$color2"
|
|
py="$2"
|
|
position="absolute"
|
|
b={0}
|
|
l={0}
|
|
r={0}
|
|
items="center"
|
|
justify="center"
|
|
>
|
|
<SizableText size="$3" color="$color9">{`${parts.length} theme${
|
|
parts.length === 1 ? '' : 's'
|
|
}:`}</SizableText>
|
|
|
|
<XStack maxW="100%" overflow="hidden">
|
|
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
<XStack items="center" justify="center" gap="$2" px="$4" py="$2">
|
|
{parts.map((part: any) => {
|
|
return (
|
|
<YStack key={part.name}>
|
|
<Button size="$3">{part.name}</Button>
|
|
</YStack>
|
|
)
|
|
})}
|
|
</XStack>
|
|
</ScrollView>
|
|
</XStack>
|
|
</YStack>
|
|
</YStack>
|
|
)
|
|
})
|