@heliosgraphics/fx 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/LiveComponents/LiveComponents.tsx +116 -0
- package/components/LiveComponents/LiveComponents.types.ts +9 -0
- package/components/LiveComponents/LiveComponents.utils.ts +1 -0
- package/components/LiveComponents/_components/GridBackground/GridBackground.module.css +14 -0
- package/components/LiveComponents/_components/GridBackground/GridBackground.tsx +18 -0
- package/components/LiveComponents/_components/GridBackground/GridBackground.types.ts +5 -0
- package/components/LiveComponents/_components/GridBackground/index.ts +1 -0
- package/components/LiveComponents/_components/LiveComponent/LiveComponent.module.css +45 -0
- package/components/LiveComponents/_components/LiveComponent/LiveComponent.tsx +63 -0
- package/components/LiveComponents/_components/LiveComponent/LiveComponent.types.ts +4 -0
- package/components/LiveComponents/_components/LiveComponent/index.ts +1 -0
- package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.module.css +28 -0
- package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.tsx +117 -0
- package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.types.ts +8 -0
- package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.utils.ts +24 -0
- package/components/LiveComponents/_components/PermutationsGrid/index.ts +2 -0
- package/components/LiveComponents/index.ts +2 -0
- package/components/Page/Page.tsx +52 -0
- package/components/Page/Page.types.ts +13 -0
- package/components/Page/index.ts +2 -0
- package/components/PageBlock/PageBlock.tsx +16 -0
- package/components/PageBlock/PageBlock.types.ts +6 -0
- package/components/PageBlock/index.ts +2 -0
- package/components/PropsTable/PropsTable.tsx +84 -0
- package/components/PropsTable/PropsTable.types.ts +7 -0
- package/components/PropsTable/PropsTable.utils.tsx +81 -0
- package/components/PropsTable/_components/BooleanEditor/BooleanEditor.tsx +23 -0
- package/components/PropsTable/_components/BooleanEditor/BooleanEditor.types.ts +6 -0
- package/components/PropsTable/_components/BooleanEditor/index.ts +2 -0
- package/components/PropsTable/_components/EnumEditor/EnumEditor.tsx +64 -0
- package/components/PropsTable/_components/EnumEditor/EnumEditor.types.ts +9 -0
- package/components/PropsTable/_components/EnumEditor/index.ts +2 -0
- package/components/PropsTable/_components/NumberEditor/NumberEditor.tsx +26 -0
- package/components/PropsTable/_components/NumberEditor/NumberEditor.types.ts +9 -0
- package/components/PropsTable/_components/NumberEditor/index.ts +2 -0
- package/components/PropsTable/_components/StringEditor/StringEditor.tsx +22 -0
- package/components/PropsTable/_components/StringEditor/StringEditor.types.ts +6 -0
- package/components/PropsTable/_components/StringEditor/index.ts +2 -0
- package/components/PropsTable/index.ts +2 -0
- package/components/WorkshopAside/WorkshopAside.tsx +103 -0
- package/components/WorkshopAside/WorkshopAside.types.ts +6 -0
- package/components/WorkshopAside/index.ts +2 -0
- package/components/WorkshopNavigation/WorkshopNavigation.tsx +36 -0
- package/components/WorkshopNavigation/WorkshopNavigation.types.ts +1 -0
- package/components/WorkshopNavigation/index.ts +2 -0
- package/components/index.ts +17 -0
- package/config/index.ts +24 -0
- package/contexts/LayoutProvider/LayoutProvider.tsx +28 -0
- package/contexts/LayoutProvider/LayoutProvider.types.ts +8 -0
- package/contexts/LayoutProvider/index.ts +2 -0
- package/contexts/WorkshopProvider/WorkshopProvider.tsx +57 -0
- package/contexts/WorkshopProvider/WorkshopProvider.types.ts +11 -0
- package/contexts/WorkshopProvider/index.ts +2 -0
- package/contexts/index.ts +5 -0
- package/index.ts +17 -0
- package/package.json +41 -0
- package/pages/ComponentPage/ComponentPage.tsx +26 -0
- package/pages/ComponentPage/ComponentPage.types.ts +10 -0
- package/pages/ComponentPage/index.ts +2 -0
- package/pages/DashboardPage/DashboardPage.tsx +88 -0
- package/pages/DashboardPage/DashboardPage.types.ts +9 -0
- package/pages/DashboardPage/DashboardPage.utils.ts +33 -0
- package/pages/DashboardPage/index.ts +2 -0
- package/pages/index.ts +5 -0
- package/types/config.ts +18 -0
- package/types/knobs.ts +13 -0
- package/types/meta.ts +28 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BooleanEditor } from "./_components/BooleanEditor"
|
|
2
|
+
import { EnumEditor } from "./_components/EnumEditor"
|
|
3
|
+
import { NumberEditor } from "./_components/NumberEditor"
|
|
4
|
+
import { StringEditor } from "./_components/StringEditor"
|
|
5
|
+
import type { ReactElement } from "react"
|
|
6
|
+
import type { KnobRegistry } from "../../types/knobs"
|
|
7
|
+
import type { WorkshopProviderProps } from "../../contexts/WorkshopProvider/WorkshopProvider.types"
|
|
8
|
+
|
|
9
|
+
export const getEditorComponent = (
|
|
10
|
+
name: string,
|
|
11
|
+
type: string,
|
|
12
|
+
knobField: string | undefined,
|
|
13
|
+
knobs: KnobRegistry,
|
|
14
|
+
context: WorkshopProviderProps,
|
|
15
|
+
isDisabled: boolean,
|
|
16
|
+
): ReactElement | null => {
|
|
17
|
+
if (knobField && knobs[knobField] && !knobs[knobField].isInternal) {
|
|
18
|
+
const knobDef = knobs[knobField]
|
|
19
|
+
const value = context.knobValues[knobField] ?? knobDef.defaultValue
|
|
20
|
+
const setter = context.knobSetters[knobField]
|
|
21
|
+
|
|
22
|
+
if (!setter) return null
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<EnumEditor
|
|
26
|
+
knobName={knobDef.label}
|
|
27
|
+
values={knobDef.values}
|
|
28
|
+
value={value}
|
|
29
|
+
onChange={setter}
|
|
30
|
+
isDisabled={isDisabled}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!knobField && knobs[name] && !knobs[name].isInternal) {
|
|
36
|
+
const knobDef = knobs[name]
|
|
37
|
+
const value = context.knobValues[name] ?? knobDef.defaultValue
|
|
38
|
+
const setter = context.knobSetters[name]
|
|
39
|
+
|
|
40
|
+
if (!setter) return null
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<EnumEditor
|
|
44
|
+
knobName={knobDef.label}
|
|
45
|
+
values={knobDef.values}
|
|
46
|
+
value={value}
|
|
47
|
+
onChange={setter}
|
|
48
|
+
isDisabled={isDisabled}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (type === "boolean" || name.startsWith("is") || name.startsWith("has")) {
|
|
54
|
+
const boolValue = (context.knobValues[name] as boolean) ?? false
|
|
55
|
+
const boolSetter = context.knobSetters[name]
|
|
56
|
+
|
|
57
|
+
if (!boolSetter) return null
|
|
58
|
+
|
|
59
|
+
return <BooleanEditor label={name} value={boolValue} onChange={(v) => boolSetter(v)} isDisabled={isDisabled} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (type === "number") {
|
|
63
|
+
const numValue = Number(context.knobValues[name] ?? 0)
|
|
64
|
+
const numSetter = context.knobSetters[name]
|
|
65
|
+
|
|
66
|
+
if (!numSetter) return null
|
|
67
|
+
|
|
68
|
+
return <NumberEditor label={name} value={numValue} onChange={(v) => numSetter(v)} isDisabled={isDisabled} />
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (type === "string") {
|
|
72
|
+
const strValue = String(context.knobValues[name] ?? "")
|
|
73
|
+
const strSetter = context.knobSetters[name]
|
|
74
|
+
|
|
75
|
+
if (!strSetter) return null
|
|
76
|
+
|
|
77
|
+
return <StringEditor label={name} value={strValue} onChange={(v) => strSetter(v)} isDisabled={isDisabled} />
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Flex, Toggle } from "@heliosgraphics/ui"
|
|
4
|
+
import type { FC } from "react"
|
|
5
|
+
import type { BooleanEditorProps } from "./BooleanEditor.types"
|
|
6
|
+
|
|
7
|
+
export const BooleanEditor: FC<BooleanEditorProps> = ({ label, value, onChange, isDisabled }) => {
|
|
8
|
+
const onToggle = (): void => onChange(!value)
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Flex isCentered={true}>
|
|
12
|
+
<Toggle
|
|
13
|
+
intent="neutral"
|
|
14
|
+
isChecked={value}
|
|
15
|
+
label={label}
|
|
16
|
+
isSmall={true}
|
|
17
|
+
onChange={onToggle}
|
|
18
|
+
isLabelHidden={true}
|
|
19
|
+
{...(isDisabled !== undefined && { isDisabled })}
|
|
20
|
+
/>
|
|
21
|
+
</Flex>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Flex, Select, ButtonGroup, Button } from "@heliosgraphics/ui"
|
|
4
|
+
import { useRef, type FC, type ChangeEvent } from "react"
|
|
5
|
+
import type { EnumEditorProps } from "./EnumEditor.types"
|
|
6
|
+
|
|
7
|
+
export const EnumEditor: FC<EnumEditorProps> = ({
|
|
8
|
+
knobName,
|
|
9
|
+
values,
|
|
10
|
+
value,
|
|
11
|
+
onChange,
|
|
12
|
+
hasUnsetOption = true,
|
|
13
|
+
hasRandom = true,
|
|
14
|
+
isDisabled,
|
|
15
|
+
}) => {
|
|
16
|
+
const lastValueRef = useRef<string | number | boolean>(value)
|
|
17
|
+
|
|
18
|
+
const items = hasUnsetOption
|
|
19
|
+
? [{ name: "unset", value: "" }, ...values.map((v) => ({ name: String(v), value: String(v) }))]
|
|
20
|
+
: values.map((v) => ({ name: String(v), value: String(v) }))
|
|
21
|
+
|
|
22
|
+
const onSelectChange = (event: ChangeEvent<HTMLSelectElement>): void => {
|
|
23
|
+
const raw: string = event.target.value
|
|
24
|
+
const original = values.find((v) => String(v) === raw)
|
|
25
|
+
|
|
26
|
+
onChange(original ?? raw)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const onRandom = (): void => {
|
|
30
|
+
let next: string | number | boolean = value
|
|
31
|
+
|
|
32
|
+
do {
|
|
33
|
+
next = values[Math.floor(Math.random() * values.length)] ?? value
|
|
34
|
+
} while (next === lastValueRef.current && values.length > 1)
|
|
35
|
+
|
|
36
|
+
lastValueRef.current = next
|
|
37
|
+
onChange(next)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Flex gap={4}>
|
|
42
|
+
<Select
|
|
43
|
+
label={`${knobName} Selector`}
|
|
44
|
+
isLabelHidden={true}
|
|
45
|
+
isSmall={true}
|
|
46
|
+
items={items}
|
|
47
|
+
onChange={onSelectChange}
|
|
48
|
+
selectedValue={String(value ?? "")}
|
|
49
|
+
{...(isDisabled !== undefined && { isDisabled })}
|
|
50
|
+
/>
|
|
51
|
+
{hasRandom && (
|
|
52
|
+
<ButtonGroup>
|
|
53
|
+
<Button
|
|
54
|
+
intent="neutral"
|
|
55
|
+
value="Random"
|
|
56
|
+
onClick={onRandom}
|
|
57
|
+
size="small"
|
|
58
|
+
{...(isDisabled !== undefined && { isDisabled })}
|
|
59
|
+
/>
|
|
60
|
+
</ButtonGroup>
|
|
61
|
+
)}
|
|
62
|
+
</Flex>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Flex, Input } from "@heliosgraphics/ui"
|
|
4
|
+
import type { FC, ChangeEvent } from "react"
|
|
5
|
+
import type { NumberEditorProps } from "./NumberEditor.types"
|
|
6
|
+
|
|
7
|
+
export const NumberEditor: FC<NumberEditorProps> = ({ label, value, onChange, isDisabled }) => {
|
|
8
|
+
const onInputChange = (event?: ChangeEvent<HTMLInputElement>): void => {
|
|
9
|
+
const parsed: number = Number(event?.target?.value ?? 0)
|
|
10
|
+
|
|
11
|
+
if (!Number.isNaN(parsed)) onChange(parsed)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Flex isCentered={true}>
|
|
16
|
+
<Input
|
|
17
|
+
value={String(value)}
|
|
18
|
+
onChange={onInputChange}
|
|
19
|
+
placeholder={label}
|
|
20
|
+
label={label}
|
|
21
|
+
isLabelHidden={true}
|
|
22
|
+
{...(isDisabled !== undefined && { isDisabled })}
|
|
23
|
+
/>
|
|
24
|
+
</Flex>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Flex, Input } from "@heliosgraphics/ui"
|
|
4
|
+
import type { FC, ChangeEvent } from "react"
|
|
5
|
+
import type { StringEditorProps } from "./StringEditor.types"
|
|
6
|
+
|
|
7
|
+
export const StringEditor: FC<StringEditorProps> = ({ label, value, onChange, isDisabled }) => {
|
|
8
|
+
const onInputChange = (event?: ChangeEvent<HTMLInputElement>): void => onChange(event?.target?.value ?? "")
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Flex isCentered={true}>
|
|
12
|
+
<Input
|
|
13
|
+
value={value}
|
|
14
|
+
onChange={onInputChange}
|
|
15
|
+
placeholder={label}
|
|
16
|
+
label={label}
|
|
17
|
+
isLabelHidden={true}
|
|
18
|
+
{...(isDisabled !== undefined && { isDisabled })}
|
|
19
|
+
/>
|
|
20
|
+
</Flex>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useContext, useState, useMemo, type FC, type ChangeEvent } from "react"
|
|
4
|
+
import Link from "next/link"
|
|
5
|
+
import { Menu, Layout, Flex, Text } from "@heliosgraphics/ui"
|
|
6
|
+
import { usePathname } from "next/navigation"
|
|
7
|
+
import { WorkshopContext } from "../../contexts/WorkshopProvider"
|
|
8
|
+
import type { WorkshopAsideProps } from "./WorkshopAside.types"
|
|
9
|
+
import type { WorkshopComponentStatus } from "../../types/meta"
|
|
10
|
+
|
|
11
|
+
export const WorkshopAside: FC<WorkshopAsideProps> = ({ name, components }) => {
|
|
12
|
+
const pathname = usePathname()
|
|
13
|
+
const { knobValues, visibleStatuses, pages } = useContext(WorkshopContext)
|
|
14
|
+
const hasMenu = knobValues["hasMenu"] !== false
|
|
15
|
+
const [filter, setFilter] = useState<string>("")
|
|
16
|
+
|
|
17
|
+
const visibleComponents = useMemo(
|
|
18
|
+
() =>
|
|
19
|
+
Object.keys(components).filter((c) => {
|
|
20
|
+
const status: WorkshopComponentStatus = components[c]?._status ?? "experimental"
|
|
21
|
+
|
|
22
|
+
return visibleStatuses.includes(status)
|
|
23
|
+
}),
|
|
24
|
+
[components, visibleStatuses],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const filteredComponents = useMemo(
|
|
28
|
+
() =>
|
|
29
|
+
filter ? visibleComponents.filter((c) => c.toLowerCase().includes(filter.toLowerCase())) : visibleComponents,
|
|
30
|
+
[visibleComponents, filter],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const onValueChange = (event?: ChangeEvent<HTMLInputElement>): void => {
|
|
34
|
+
setFilter(event?.target?.value ?? "")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const onClear = (): void => setFilter("")
|
|
38
|
+
|
|
39
|
+
const groupedComponents: Record<string, Array<string>> = filteredComponents.reduce<Record<string, Array<string>>>(
|
|
40
|
+
(acc, component) => {
|
|
41
|
+
const category: string = components[component]?._category ?? "uncategorized"
|
|
42
|
+
|
|
43
|
+
acc[category] = acc[category] || []
|
|
44
|
+
acc[category].push(component)
|
|
45
|
+
|
|
46
|
+
return acc
|
|
47
|
+
},
|
|
48
|
+
{},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if (!hasMenu) return null
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Layout.Aside>
|
|
55
|
+
<Layout.Navigation gap={8} isBetween={true}>
|
|
56
|
+
<Flex gap={6} isYCentered={true}>
|
|
57
|
+
<Link href="/" className="flex">
|
|
58
|
+
<Text type="small" fontWeight="semibold">
|
|
59
|
+
{name}
|
|
60
|
+
</Text>
|
|
61
|
+
</Link>
|
|
62
|
+
</Flex>
|
|
63
|
+
<Layout.Aside.Toggle />
|
|
64
|
+
</Layout.Navigation>
|
|
65
|
+
<Layout.Aside.Content>
|
|
66
|
+
<Menu>
|
|
67
|
+
<Menu.Filter value={filter} onChange={onValueChange} {...(filter && { onClear })} />
|
|
68
|
+
{!filter && (
|
|
69
|
+
<Menu.Category category="Workshop" isFolder={true}>
|
|
70
|
+
<Link href="/">
|
|
71
|
+
<Menu.Item icon="home" title="Dashboard" isActive={pathname === "/"} />
|
|
72
|
+
</Link>
|
|
73
|
+
{pages.map((page) => (
|
|
74
|
+
<Link href={page.href} key={page.href}>
|
|
75
|
+
<Menu.Item
|
|
76
|
+
{...(page.icon && { icon: page.icon })}
|
|
77
|
+
title={page.title}
|
|
78
|
+
isActive={pathname === page.href}
|
|
79
|
+
/>
|
|
80
|
+
</Link>
|
|
81
|
+
))}
|
|
82
|
+
</Menu.Category>
|
|
83
|
+
)}
|
|
84
|
+
{Object.entries(groupedComponents).map(([category, items]) => (
|
|
85
|
+
<Menu.Category
|
|
86
|
+
key={`${category}-${Boolean(filter)}`}
|
|
87
|
+
category={category}
|
|
88
|
+
isFolder={true}
|
|
89
|
+
isInitiallyClosed={!filter}
|
|
90
|
+
{...(filter && { isOpen: true })}
|
|
91
|
+
>
|
|
92
|
+
{items.map((component) => (
|
|
93
|
+
<Link href={`/components/${component}`} key={component}>
|
|
94
|
+
<Menu.Item title={component} isActive={pathname === `/components/${component}`} />
|
|
95
|
+
</Link>
|
|
96
|
+
))}
|
|
97
|
+
</Menu.Category>
|
|
98
|
+
))}
|
|
99
|
+
</Menu>
|
|
100
|
+
</Layout.Aside.Content>
|
|
101
|
+
</Layout.Aside>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useContext, type FC } from "react"
|
|
4
|
+
import { Flex, Layout, ButtonGroup, Button, useTheme, useLayoutContext } from "@heliosgraphics/ui"
|
|
5
|
+
import { WorkshopContext } from "../../contexts/WorkshopProvider"
|
|
6
|
+
import type { WorkshopNavigationProps } from "./WorkshopNavigation.types"
|
|
7
|
+
|
|
8
|
+
export const WorkshopNavigation: FC<WorkshopNavigationProps> = () => {
|
|
9
|
+
const { isMenuVisible, isWideEnough } = useLayoutContext()
|
|
10
|
+
const { toggleTheme, isDark } = useTheme()
|
|
11
|
+
const { knobValues } = useContext(WorkshopContext)
|
|
12
|
+
const hasHeader = knobValues["hasHeader"] !== false
|
|
13
|
+
const hasSubHeader = knobValues["hasSubHeader"] === true
|
|
14
|
+
|
|
15
|
+
if (!hasHeader) return null
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Layout.Navigation isBetween={true}>
|
|
20
|
+
<Flex isYCentered={true} gap={8}>
|
|
21
|
+
{!isMenuVisible && !isWideEnough && <Layout.Aside.Toggle />}
|
|
22
|
+
</Flex>
|
|
23
|
+
<ButtonGroup>
|
|
24
|
+
<Button
|
|
25
|
+
intent="neutral"
|
|
26
|
+
onClick={toggleTheme}
|
|
27
|
+
value={isDark ? "Light" : "Dark"}
|
|
28
|
+
size="small"
|
|
29
|
+
icon={isDark ? "sun" : "moon"}
|
|
30
|
+
/>
|
|
31
|
+
</ButtonGroup>
|
|
32
|
+
</Layout.Navigation>
|
|
33
|
+
{hasSubHeader && <Layout.SubNavigation />}
|
|
34
|
+
</>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export interface WorkshopNavigationProps {}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { LiveComponents } from "./LiveComponents"
|
|
2
|
+
export type { LiveComponentsProps } from "./LiveComponents"
|
|
3
|
+
|
|
4
|
+
export { Page } from "./Page"
|
|
5
|
+
export type { PageProps } from "./Page"
|
|
6
|
+
|
|
7
|
+
export { PageBlock } from "./PageBlock"
|
|
8
|
+
export type { PageBlockProps } from "./PageBlock"
|
|
9
|
+
|
|
10
|
+
export { PropsTable } from "./PropsTable"
|
|
11
|
+
export type { PropsTableProps } from "./PropsTable"
|
|
12
|
+
|
|
13
|
+
export { WorkshopAside } from "./WorkshopAside"
|
|
14
|
+
export type { WorkshopAsideProps } from "./WorkshopAside"
|
|
15
|
+
|
|
16
|
+
export { WorkshopNavigation } from "./WorkshopNavigation"
|
|
17
|
+
export type { WorkshopNavigationProps } from "./WorkshopNavigation"
|
package/config/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WorkshopConfig } from "../types/config"
|
|
2
|
+
|
|
3
|
+
export const createWorkshopConfig = (config: WorkshopConfig): Record<string, unknown> => {
|
|
4
|
+
const nextConfig = {
|
|
5
|
+
devIndicators: false,
|
|
6
|
+
reactStrictMode: true,
|
|
7
|
+
typescript: {
|
|
8
|
+
ignoreBuildErrors: true,
|
|
9
|
+
},
|
|
10
|
+
transpilePackages: [
|
|
11
|
+
"@heliosgraphics/utils",
|
|
12
|
+
"@heliosgraphics/icons",
|
|
13
|
+
"@heliosgraphics/ui",
|
|
14
|
+
"@heliosgraphics/css",
|
|
15
|
+
"@heliosgraphics/workshop",
|
|
16
|
+
...(config.transpilePackages ?? []),
|
|
17
|
+
],
|
|
18
|
+
experimental: {
|
|
19
|
+
externalDir: true,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return nextConfig
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, createContext, type FC, type PropsWithChildren } from "react"
|
|
4
|
+
import type { LayoutProviderProps } from "./LayoutProvider.types"
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_DEMO_PATTERN = "default" as const
|
|
7
|
+
|
|
8
|
+
const LayoutContext = createContext<LayoutProviderProps>({
|
|
9
|
+
activeDemoPattern: DEFAULT_DEMO_PATTERN,
|
|
10
|
+
setActiveDemoPattern: () => null,
|
|
11
|
+
isPermutationsMode: false,
|
|
12
|
+
setIsPermutationsMode: () => null,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const LayoutProvider: FC<PropsWithChildren> = ({ children }) => {
|
|
16
|
+
const [activeDemoPattern, setActiveDemoPattern] = useState<string>(DEFAULT_DEMO_PATTERN)
|
|
17
|
+
const [isPermutationsMode, setIsPermutationsMode] = useState<boolean>(false)
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<LayoutContext.Provider
|
|
21
|
+
value={{ activeDemoPattern, setActiveDemoPattern, isPermutationsMode, setIsPermutationsMode }}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</LayoutContext.Provider>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { LayoutContext, LayoutProvider }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Dispatch, SetStateAction } from "react"
|
|
2
|
+
|
|
3
|
+
export interface LayoutProviderProps {
|
|
4
|
+
activeDemoPattern: string
|
|
5
|
+
setActiveDemoPattern: Dispatch<SetStateAction<string>>
|
|
6
|
+
isPermutationsMode: boolean
|
|
7
|
+
setIsPermutationsMode: Dispatch<SetStateAction<boolean>>
|
|
8
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, createContext, useMemo, type FC, type PropsWithChildren } from "react"
|
|
4
|
+
import type { WorkshopProviderProps } from "./WorkshopProvider.types"
|
|
5
|
+
import type { KnobRegistry, KnobValues, KnobSetters } from "../../types/knobs"
|
|
6
|
+
import type { WorkshopComponentStatus } from "../../types/meta"
|
|
7
|
+
import type { WorkshopPageConfig } from "../../types/config"
|
|
8
|
+
|
|
9
|
+
const DEFAULT_VISIBLE_STATUSES: Array<WorkshopComponentStatus> = ["stable", "nominal"]
|
|
10
|
+
|
|
11
|
+
interface WorkshopProviderComponentProps {
|
|
12
|
+
knobs: KnobRegistry
|
|
13
|
+
componentScope: Record<string, unknown>
|
|
14
|
+
visibleStatuses?: Array<WorkshopComponentStatus>
|
|
15
|
+
pages?: Array<WorkshopPageConfig>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const buildDefaultValues = (knobs: KnobRegistry): KnobValues =>
|
|
19
|
+
Object.fromEntries(Object.entries(knobs).map(([key, def]) => [key, def.defaultValue]))
|
|
20
|
+
|
|
21
|
+
const WorkshopContext = createContext<WorkshopProviderProps>({
|
|
22
|
+
knobValues: {},
|
|
23
|
+
knobSetters: {},
|
|
24
|
+
componentScope: {},
|
|
25
|
+
visibleStatuses: DEFAULT_VISIBLE_STATUSES,
|
|
26
|
+
pages: [],
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const WorkshopProvider: FC<PropsWithChildren<WorkshopProviderComponentProps>> = ({
|
|
30
|
+
knobs,
|
|
31
|
+
componentScope,
|
|
32
|
+
visibleStatuses = DEFAULT_VISIBLE_STATUSES,
|
|
33
|
+
pages = [],
|
|
34
|
+
children,
|
|
35
|
+
}) => {
|
|
36
|
+
const [values, setValues] = useState<KnobValues>(() => buildDefaultValues(knobs))
|
|
37
|
+
|
|
38
|
+
const knobSetters = useMemo<KnobSetters>(() => {
|
|
39
|
+
const setters: KnobSetters = {}
|
|
40
|
+
|
|
41
|
+
for (const key of Object.keys(knobs)) {
|
|
42
|
+
setters[key] = (next: string | number | boolean): void => {
|
|
43
|
+
setValues((prev) => ({ ...prev, [key]: next }))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return setters
|
|
48
|
+
}, [knobs])
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<WorkshopContext.Provider value={{ knobValues: values, knobSetters, componentScope, visibleStatuses, pages }}>
|
|
52
|
+
{children}
|
|
53
|
+
</WorkshopContext.Provider>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { WorkshopContext, WorkshopProvider }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { KnobValues, KnobSetters } from "../../types/knobs"
|
|
2
|
+
import type { WorkshopComponentStatus } from "../../types/meta"
|
|
3
|
+
import type { WorkshopPageConfig } from "../../types/config"
|
|
4
|
+
|
|
5
|
+
export interface WorkshopProviderProps {
|
|
6
|
+
knobValues: KnobValues
|
|
7
|
+
knobSetters: KnobSetters
|
|
8
|
+
componentScope: Record<string, unknown>
|
|
9
|
+
visibleStatuses: Array<WorkshopComponentStatus>
|
|
10
|
+
pages: Array<WorkshopPageConfig>
|
|
11
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { LayoutContext, LayoutProvider, DEFAULT_DEMO_PATTERN } from "./LayoutProvider"
|
|
2
|
+
export type { LayoutProviderProps } from "./LayoutProvider"
|
|
3
|
+
|
|
4
|
+
export { WorkshopContext, WorkshopProvider } from "./WorkshopProvider"
|
|
5
|
+
export type { WorkshopProviderProps } from "./WorkshopProvider"
|
package/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
WorkshopAttributeItem,
|
|
3
|
+
WorkshopAttributeMeta,
|
|
4
|
+
WorkshopComponentCategory,
|
|
5
|
+
WorkshopComponentRegistry,
|
|
6
|
+
WorkshopComponentStatus,
|
|
7
|
+
WorkshopPattern,
|
|
8
|
+
} from "./types/meta"
|
|
9
|
+
|
|
10
|
+
export type { KnobDefinition, KnobRegistry, KnobValues, KnobSetters } from "./types/knobs"
|
|
11
|
+
|
|
12
|
+
export type { WorkshopConfig, WorkshopPageConfig } from "./types/config"
|
|
13
|
+
|
|
14
|
+
export { Page } from "./components/Page"
|
|
15
|
+
export type { PageProps } from "./components/Page"
|
|
16
|
+
export { PageBlock } from "./components/PageBlock"
|
|
17
|
+
export type { PageBlockProps } from "./components/PageBlock"
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heliosgraphics/fx",
|
|
3
|
+
"version": "0.0.1-alpha.0",
|
|
4
|
+
"author": "Chris Puska <chris@puska.org>",
|
|
5
|
+
"description": "Tool building tools",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./index.ts",
|
|
11
|
+
"./pages": "./pages/index.ts",
|
|
12
|
+
"./components": "./components/index.ts",
|
|
13
|
+
"./contexts": "./contexts/index.ts",
|
|
14
|
+
"./config": "./config/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"components/**/*",
|
|
18
|
+
"config/**/*",
|
|
19
|
+
"contexts/**/*",
|
|
20
|
+
"pages/**/*",
|
|
21
|
+
"types/**/*",
|
|
22
|
+
"index.ts"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"recat": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@heliosgraphics/ui": "2.0.0-alpha.93",
|
|
29
|
+
"@heliosgraphics/utils": "6.0.0-alpha.11",
|
|
30
|
+
"next": ">=16.1",
|
|
31
|
+
"react": ">=19.2",
|
|
32
|
+
"react-dom": ">=19.2"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25",
|
|
36
|
+
"@types/react": "^19",
|
|
37
|
+
"@types/react-dom": "^19",
|
|
38
|
+
"prism-react-renderer": "^2.4.1",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
}
|
|
41
|
+
}
|