@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.
Files changed (67) hide show
  1. package/components/LiveComponents/LiveComponents.tsx +116 -0
  2. package/components/LiveComponents/LiveComponents.types.ts +9 -0
  3. package/components/LiveComponents/LiveComponents.utils.ts +1 -0
  4. package/components/LiveComponents/_components/GridBackground/GridBackground.module.css +14 -0
  5. package/components/LiveComponents/_components/GridBackground/GridBackground.tsx +18 -0
  6. package/components/LiveComponents/_components/GridBackground/GridBackground.types.ts +5 -0
  7. package/components/LiveComponents/_components/GridBackground/index.ts +1 -0
  8. package/components/LiveComponents/_components/LiveComponent/LiveComponent.module.css +45 -0
  9. package/components/LiveComponents/_components/LiveComponent/LiveComponent.tsx +63 -0
  10. package/components/LiveComponents/_components/LiveComponent/LiveComponent.types.ts +4 -0
  11. package/components/LiveComponents/_components/LiveComponent/index.ts +1 -0
  12. package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.module.css +28 -0
  13. package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.tsx +117 -0
  14. package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.types.ts +8 -0
  15. package/components/LiveComponents/_components/PermutationsGrid/PermutationsGrid.utils.ts +24 -0
  16. package/components/LiveComponents/_components/PermutationsGrid/index.ts +2 -0
  17. package/components/LiveComponents/index.ts +2 -0
  18. package/components/Page/Page.tsx +52 -0
  19. package/components/Page/Page.types.ts +13 -0
  20. package/components/Page/index.ts +2 -0
  21. package/components/PageBlock/PageBlock.tsx +16 -0
  22. package/components/PageBlock/PageBlock.types.ts +6 -0
  23. package/components/PageBlock/index.ts +2 -0
  24. package/components/PropsTable/PropsTable.tsx +84 -0
  25. package/components/PropsTable/PropsTable.types.ts +7 -0
  26. package/components/PropsTable/PropsTable.utils.tsx +81 -0
  27. package/components/PropsTable/_components/BooleanEditor/BooleanEditor.tsx +23 -0
  28. package/components/PropsTable/_components/BooleanEditor/BooleanEditor.types.ts +6 -0
  29. package/components/PropsTable/_components/BooleanEditor/index.ts +2 -0
  30. package/components/PropsTable/_components/EnumEditor/EnumEditor.tsx +64 -0
  31. package/components/PropsTable/_components/EnumEditor/EnumEditor.types.ts +9 -0
  32. package/components/PropsTable/_components/EnumEditor/index.ts +2 -0
  33. package/components/PropsTable/_components/NumberEditor/NumberEditor.tsx +26 -0
  34. package/components/PropsTable/_components/NumberEditor/NumberEditor.types.ts +9 -0
  35. package/components/PropsTable/_components/NumberEditor/index.ts +2 -0
  36. package/components/PropsTable/_components/StringEditor/StringEditor.tsx +22 -0
  37. package/components/PropsTable/_components/StringEditor/StringEditor.types.ts +6 -0
  38. package/components/PropsTable/_components/StringEditor/index.ts +2 -0
  39. package/components/PropsTable/index.ts +2 -0
  40. package/components/WorkshopAside/WorkshopAside.tsx +103 -0
  41. package/components/WorkshopAside/WorkshopAside.types.ts +6 -0
  42. package/components/WorkshopAside/index.ts +2 -0
  43. package/components/WorkshopNavigation/WorkshopNavigation.tsx +36 -0
  44. package/components/WorkshopNavigation/WorkshopNavigation.types.ts +1 -0
  45. package/components/WorkshopNavigation/index.ts +2 -0
  46. package/components/index.ts +17 -0
  47. package/config/index.ts +24 -0
  48. package/contexts/LayoutProvider/LayoutProvider.tsx +28 -0
  49. package/contexts/LayoutProvider/LayoutProvider.types.ts +8 -0
  50. package/contexts/LayoutProvider/index.ts +2 -0
  51. package/contexts/WorkshopProvider/WorkshopProvider.tsx +57 -0
  52. package/contexts/WorkshopProvider/WorkshopProvider.types.ts +11 -0
  53. package/contexts/WorkshopProvider/index.ts +2 -0
  54. package/contexts/index.ts +5 -0
  55. package/index.ts +17 -0
  56. package/package.json +41 -0
  57. package/pages/ComponentPage/ComponentPage.tsx +26 -0
  58. package/pages/ComponentPage/ComponentPage.types.ts +10 -0
  59. package/pages/ComponentPage/index.ts +2 -0
  60. package/pages/DashboardPage/DashboardPage.tsx +88 -0
  61. package/pages/DashboardPage/DashboardPage.types.ts +9 -0
  62. package/pages/DashboardPage/DashboardPage.utils.ts +33 -0
  63. package/pages/DashboardPage/index.ts +2 -0
  64. package/pages/index.ts +5 -0
  65. package/types/config.ts +18 -0
  66. package/types/knobs.ts +13 -0
  67. 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,6 @@
1
+ export interface BooleanEditorProps {
2
+ label: string
3
+ value: boolean
4
+ onChange: (value: boolean) => void
5
+ isDisabled?: boolean
6
+ }
@@ -0,0 +1,2 @@
1
+ export { BooleanEditor } from "./BooleanEditor"
2
+ export type { BooleanEditorProps } from "./BooleanEditor.types"
@@ -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,9 @@
1
+ export interface EnumEditorProps {
2
+ knobName: string
3
+ values: Array<string | number | boolean>
4
+ value: string | number | boolean
5
+ onChange: (value: string | number | boolean) => void
6
+ hasUnsetOption?: boolean
7
+ hasRandom?: boolean
8
+ isDisabled?: boolean
9
+ }
@@ -0,0 +1,2 @@
1
+ export { EnumEditor } from "./EnumEditor"
2
+ export type { EnumEditorProps } from "./EnumEditor.types"
@@ -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,9 @@
1
+ export interface NumberEditorProps {
2
+ label: string
3
+ value: number
4
+ onChange: (value: number) => void
5
+ min?: number
6
+ max?: number
7
+ step?: number
8
+ isDisabled?: boolean
9
+ }
@@ -0,0 +1,2 @@
1
+ export { NumberEditor } from "./NumberEditor"
2
+ export type { NumberEditorProps } from "./NumberEditor.types"
@@ -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,6 @@
1
+ export interface StringEditorProps {
2
+ label: string
3
+ value: string
4
+ onChange: (value: string) => void
5
+ isDisabled?: boolean
6
+ }
@@ -0,0 +1,2 @@
1
+ export { StringEditor } from "./StringEditor"
2
+ export type { StringEditorProps } from "./StringEditor.types"
@@ -0,0 +1,2 @@
1
+ export { PropsTable } from "./PropsTable"
2
+ export type { PropsTableProps } from "./PropsTable.types"
@@ -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,6 @@
1
+ import type { WorkshopComponentRegistry } from "../../types/meta"
2
+
3
+ export interface WorkshopAsideProps {
4
+ name: string
5
+ components: WorkshopComponentRegistry
6
+ }
@@ -0,0 +1,2 @@
1
+ export { WorkshopAside } from "./WorkshopAside"
2
+ export type { WorkshopAsideProps } from "./WorkshopAside.types"
@@ -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,2 @@
1
+ export { WorkshopNavigation } from "./WorkshopNavigation"
2
+ export type { WorkshopNavigationProps } from "./WorkshopNavigation.types"
@@ -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"
@@ -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,2 @@
1
+ export { LayoutContext, LayoutProvider, DEFAULT_DEMO_PATTERN } from "./LayoutProvider"
2
+ export type { LayoutProviderProps } from "./LayoutProvider.types"
@@ -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,2 @@
1
+ export { WorkshopContext, WorkshopProvider } from "./WorkshopProvider"
2
+ export type { WorkshopProviderProps } from "./WorkshopProvider.types"
@@ -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
+ }