@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,116 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Segments, Flex, Separator, Toggle } from "@heliosgraphics/ui"
|
|
4
|
+
import { LiveComponent } from "./_components/LiveComponent"
|
|
5
|
+
import { GridBackground } from "./_components/GridBackground"
|
|
6
|
+
import { PermutationsGrid } from "./_components/PermutationsGrid"
|
|
7
|
+
import { useContext, useEffect, useMemo } from "react"
|
|
8
|
+
import { LayoutContext, DEFAULT_DEMO_PATTERN } from "../../contexts/LayoutProvider"
|
|
9
|
+
import { WorkshopContext } from "../../contexts/WorkshopProvider"
|
|
10
|
+
import type { FC, ReactNode } from "react"
|
|
11
|
+
import type { LiveComponentsProps } from "./LiveComponents.types"
|
|
12
|
+
import { toScreamingCase } from "./LiveComponents.utils"
|
|
13
|
+
|
|
14
|
+
export const LiveComponents: FC<LiveComponentsProps> = ({ demoComponent, demoMeta, knobs }) => {
|
|
15
|
+
const { activeDemoPattern, setActiveDemoPattern, isPermutationsMode, setIsPermutationsMode } =
|
|
16
|
+
useContext(LayoutContext)
|
|
17
|
+
const { knobValues, knobSetters, componentScope } = useContext(WorkshopContext)
|
|
18
|
+
|
|
19
|
+
const isDefaultPattern: boolean = activeDemoPattern === DEFAULT_DEMO_PATTERN
|
|
20
|
+
const hasWorkshopBackground = knobValues["hasWorkshopBackground"] === true
|
|
21
|
+
const canShowPermutations: boolean = !isDefaultPattern
|
|
22
|
+
|
|
23
|
+
const onBackgroundToggle = (): void => {
|
|
24
|
+
const setter = knobSetters["hasWorkshopBackground"]
|
|
25
|
+
|
|
26
|
+
if (setter) setter(!hasWorkshopBackground)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const onTogglePermutations = (): void => {
|
|
30
|
+
if (isDefaultPattern) return
|
|
31
|
+
|
|
32
|
+
setIsPermutationsMode((prev) => !prev)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const onSelectDemo = (): void => {
|
|
36
|
+
setActiveDemoPattern(DEFAULT_DEMO_PATTERN)
|
|
37
|
+
setIsPermutationsMode(false)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const hasDemoComponent: boolean = !!demoComponent
|
|
41
|
+
const firstPatternContent: string | undefined = demoMeta._patterns[0]?.content
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!hasDemoComponent && firstPatternContent) {
|
|
45
|
+
setActiveDemoPattern(firstPatternContent)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (): void => {
|
|
49
|
+
setActiveDemoPattern(DEFAULT_DEMO_PATTERN)
|
|
50
|
+
setIsPermutationsMode(false)
|
|
51
|
+
}
|
|
52
|
+
}, [hasDemoComponent, firstPatternContent, setActiveDemoPattern, setIsPermutationsMode])
|
|
53
|
+
|
|
54
|
+
const fullScope = useMemo<Record<string, unknown>>(() => {
|
|
55
|
+
const scope: Record<string, unknown> = { ...componentScope }
|
|
56
|
+
|
|
57
|
+
for (const [key, value] of Object.entries(knobValues)) {
|
|
58
|
+
if (knobs[key]?.isInternal) continue
|
|
59
|
+
|
|
60
|
+
scope[toScreamingCase(key)] = value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return scope
|
|
64
|
+
}, [componentScope, knobValues, knobs])
|
|
65
|
+
|
|
66
|
+
const renderContent = (): ReactNode => {
|
|
67
|
+
if (isDefaultPattern) return demoComponent
|
|
68
|
+
|
|
69
|
+
if (isPermutationsMode) {
|
|
70
|
+
return <PermutationsGrid code={activeDemoPattern} scope={fullScope} knobs={knobs} knobValues={knobValues} />
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return <LiveComponent code={activeDemoPattern} scope={fullScope} />
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Flex isColumn={true}>
|
|
78
|
+
<Flex gap={8} padding={8} isYCentered={true} isBetween={true}>
|
|
79
|
+
<Flex gap={8} isYCentered={true}>
|
|
80
|
+
<Segments isSmall={true}>
|
|
81
|
+
{demoComponent && (
|
|
82
|
+
<Segments.Button
|
|
83
|
+
key="demoComponent"
|
|
84
|
+
value="demo"
|
|
85
|
+
iconLeft="arrow-4way"
|
|
86
|
+
isActive={isDefaultPattern && !isPermutationsMode}
|
|
87
|
+
onClick={onSelectDemo}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
{demoMeta._patterns.map((p, key) => {
|
|
91
|
+
const onSelectPattern = (): void => setActiveDemoPattern(p.content)
|
|
92
|
+
const isActive: boolean = !isDefaultPattern && !isPermutationsMode && activeDemoPattern === p.content
|
|
93
|
+
|
|
94
|
+
return <Segments.Button key={key} value={p.description} isActive={isActive} onClick={onSelectPattern} />
|
|
95
|
+
})}
|
|
96
|
+
</Segments>
|
|
97
|
+
{canShowPermutations && (
|
|
98
|
+
<Segments isSmall={true}>
|
|
99
|
+
<Segments.Button
|
|
100
|
+
value="Permutations"
|
|
101
|
+
iconLeft="layout-bottom"
|
|
102
|
+
isActive={isPermutationsMode}
|
|
103
|
+
onClick={onTogglePermutations}
|
|
104
|
+
/>
|
|
105
|
+
</Segments>
|
|
106
|
+
)}
|
|
107
|
+
</Flex>
|
|
108
|
+
{knobSetters["hasWorkshopBackground"] && (
|
|
109
|
+
<Toggle label="Show grid" isChecked={hasWorkshopBackground} isSmall={true} onChange={onBackgroundToggle} />
|
|
110
|
+
)}
|
|
111
|
+
</Flex>
|
|
112
|
+
<Separator emphasis="secondary" />
|
|
113
|
+
<GridBackground>{renderContent()}</GridBackground>
|
|
114
|
+
</Flex>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WorkshopAttributeMeta } from "../../types/meta"
|
|
2
|
+
import type { KnobRegistry } from "../../types/knobs"
|
|
3
|
+
import type { ReactNode } from "react"
|
|
4
|
+
|
|
5
|
+
export interface LiveComponentsProps {
|
|
6
|
+
demoComponent?: ReactNode
|
|
7
|
+
demoMeta: WorkshopAttributeMeta<unknown>
|
|
8
|
+
knobs: KnobRegistry
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const toScreamingCase = (str: string): string => str.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.gridBackground {
|
|
2
|
+
margin-top: -1px;
|
|
3
|
+
margin-left: -1px;
|
|
4
|
+
|
|
5
|
+
background-color: var(--ui-bg);
|
|
6
|
+
box-shadow: inset 0 0 0 8px var(--ui-bg);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.gridBackgroundAlt {
|
|
10
|
+
background-image:
|
|
11
|
+
linear-gradient(90deg, var(--ui-border-secondary) 0, transparent 1px),
|
|
12
|
+
linear-gradient(180deg, var(--ui-border-secondary) 0, transparent 1px);
|
|
13
|
+
background-size: 8px 8px;
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { getClasses } from "@heliosgraphics/utils"
|
|
4
|
+
import styles from "./GridBackground.module.css"
|
|
5
|
+
import { useContext, type FC } from "react"
|
|
6
|
+
import type { GridBackgroundProps } from "./GridBackground.types"
|
|
7
|
+
import { WorkshopContext } from "../../../../contexts/WorkshopProvider"
|
|
8
|
+
|
|
9
|
+
export const GridBackground: FC<GridBackgroundProps> = ({ children }) => {
|
|
10
|
+
const { knobValues } = useContext(WorkshopContext)
|
|
11
|
+
const hasWorkshopBackground = knobValues["hasWorkshopBackground"] === true
|
|
12
|
+
|
|
13
|
+
const gridBackgroundClasses: string = getClasses(styles.gridBackground, {
|
|
14
|
+
[styles.gridBackgroundAlt]: hasWorkshopBackground,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return <div className={gridBackgroundClasses}>{children}</div>
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GridBackground } from "./GridBackground"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.liveEditor {
|
|
2
|
+
background-color: var(--ui-bg-primary);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.liveEditor pre {
|
|
6
|
+
position: relative;
|
|
7
|
+
|
|
8
|
+
padding: 24px 24px 24px 0 !important;
|
|
9
|
+
|
|
10
|
+
border-top: 1px solid var(--ui-border-secondary);
|
|
11
|
+
|
|
12
|
+
counter-reset: token-line;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.liveEditor pre:before {
|
|
16
|
+
position: absolute;
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 0;
|
|
19
|
+
z-index: 0;
|
|
20
|
+
|
|
21
|
+
height: 100%;
|
|
22
|
+
padding-block: 16px;
|
|
23
|
+
width: 40px;
|
|
24
|
+
|
|
25
|
+
background-color: var(--ui-bg-secondary);
|
|
26
|
+
border-right: 1px solid var(--ui-border-secondary);
|
|
27
|
+
content: " ";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.liveEditor pre > span::before {
|
|
31
|
+
position: relative;
|
|
32
|
+
z-index: 1;
|
|
33
|
+
|
|
34
|
+
justify-content: flex-end;
|
|
35
|
+
display: inline-flex;
|
|
36
|
+
margin-right: 16px;
|
|
37
|
+
min-width: 40px;
|
|
38
|
+
padding: 0 8px;
|
|
39
|
+
width: 40px;
|
|
40
|
+
|
|
41
|
+
color: var(--ui-text-tertiary);
|
|
42
|
+
|
|
43
|
+
counter-increment: token-line;
|
|
44
|
+
content: counter(token-line);
|
|
45
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import styles from "./LiveComponent.module.css"
|
|
4
|
+
import type { LiveComponentProps } from "./LiveComponent.types"
|
|
5
|
+
import type { PrismTheme } from "prism-react-renderer"
|
|
6
|
+
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "recat"
|
|
7
|
+
import { Flex } from "@heliosgraphics/ui"
|
|
8
|
+
import { getClasses } from "@heliosgraphics/utils"
|
|
9
|
+
import type { FC } from "react"
|
|
10
|
+
|
|
11
|
+
const PRISM_THEME: PrismTheme = {
|
|
12
|
+
plain: {
|
|
13
|
+
backgroundColor: "var(--ui-bg)",
|
|
14
|
+
color: "var(--ui-text-secondary)",
|
|
15
|
+
},
|
|
16
|
+
styles: [
|
|
17
|
+
{
|
|
18
|
+
types: ["comment", "prolog", "doctype", "cdata", "punctuation"],
|
|
19
|
+
style: { color: "var(--ui-text-secondary)" },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
types: [
|
|
23
|
+
"boolean",
|
|
24
|
+
"string",
|
|
25
|
+
"entity",
|
|
26
|
+
"url",
|
|
27
|
+
"attr-value",
|
|
28
|
+
"keyword",
|
|
29
|
+
"control",
|
|
30
|
+
"directive",
|
|
31
|
+
"unit",
|
|
32
|
+
"statement",
|
|
33
|
+
"regex",
|
|
34
|
+
"at-rule",
|
|
35
|
+
"placeholder",
|
|
36
|
+
"variable",
|
|
37
|
+
"script",
|
|
38
|
+
],
|
|
39
|
+
style: {
|
|
40
|
+
color: "hsl(var(--orange-hue), var(--orange-saturation), 50%)",
|
|
41
|
+
fontWeight: "700",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const LiveComponent: FC<LiveComponentProps> = ({ code, scope }) => {
|
|
48
|
+
const liveEditorClasses: string = getClasses(styles.liveEditor, "mono tiny", {})
|
|
49
|
+
|
|
50
|
+
if (!code) return null
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Flex isColumn={true}>
|
|
54
|
+
<LiveProvider code={code} scope={scope} theme={PRISM_THEME}>
|
|
55
|
+
<Flex isColumn={true} gap={12} padding={16}>
|
|
56
|
+
<LiveError />
|
|
57
|
+
<LivePreview />
|
|
58
|
+
</Flex>
|
|
59
|
+
<LiveEditor className={liveEditorClasses} disabled />
|
|
60
|
+
</LiveProvider>
|
|
61
|
+
</Flex>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LiveComponent } from "./LiveComponent"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.permutationsGrid {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
4
|
+
gap: 1px;
|
|
5
|
+
/*background-color: var(--ui-border-secondary);*/
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.permutationsCell {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
background-color: var(--ui-bg);
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.permutationsCellPreview {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
padding: 16px;
|
|
20
|
+
flex: 1;
|
|
21
|
+
min-height: 80px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.permutationsCellLabel {
|
|
25
|
+
padding: 4px 8px;
|
|
26
|
+
background-color: var(--ui-bg-secondary);
|
|
27
|
+
border-top: 1px solid var(--ui-border-secondary);
|
|
28
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import styles from "./PermutationsGrid.module.css"
|
|
4
|
+
import { LiveProvider, LivePreview, LiveError } from "recat"
|
|
5
|
+
import { Checkbox, Flex, Text } from "@heliosgraphics/ui"
|
|
6
|
+
import { useState, useEffect, useMemo, useCallback } from "react"
|
|
7
|
+
import type { FC, ChangeEvent } from "react"
|
|
8
|
+
import type { PermutationsGridProps } from "./PermutationsGrid.types"
|
|
9
|
+
import type { Combination } from "./PermutationsGrid.utils"
|
|
10
|
+
import { DEFAULT_AXIS_COUNT, getPatternKnobNames, getCartesianProduct } from "./PermutationsGrid.utils"
|
|
11
|
+
import { toScreamingCase } from "../../LiveComponents.utils"
|
|
12
|
+
|
|
13
|
+
export const PermutationsGrid: FC<PermutationsGridProps> = ({ code, scope, knobs }) => {
|
|
14
|
+
const relevantKnobNames: Array<string> = useMemo(
|
|
15
|
+
() =>
|
|
16
|
+
getPatternKnobNames(
|
|
17
|
+
code,
|
|
18
|
+
Object.keys(knobs).filter((k) => !knobs[k]?.isInternal),
|
|
19
|
+
),
|
|
20
|
+
[code, knobs],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const [activeAxes, setActiveAxes] = useState<Array<string>>(() => relevantKnobNames.slice(0, DEFAULT_AXIS_COUNT))
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const validAxes: Array<string> = activeAxes.filter((axis) => relevantKnobNames.includes(axis))
|
|
27
|
+
|
|
28
|
+
if (validAxes.length !== activeAxes.length) {
|
|
29
|
+
const nextAxes: Array<string> = validAxes.length ? validAxes : relevantKnobNames.slice(0, DEFAULT_AXIS_COUNT)
|
|
30
|
+
setActiveAxes(nextAxes)
|
|
31
|
+
}
|
|
32
|
+
}, [relevantKnobNames, activeAxes])
|
|
33
|
+
|
|
34
|
+
const onToggleAxis = useCallback(
|
|
35
|
+
(name: string) =>
|
|
36
|
+
(_event: ChangeEvent<HTMLInputElement>): void => {
|
|
37
|
+
setActiveAxes((prev) => {
|
|
38
|
+
if (prev.includes(name)) {
|
|
39
|
+
if (prev.length <= 1) return prev
|
|
40
|
+
return prev.filter((axis) => axis !== name)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return [...prev, name]
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
[],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const combinations: Array<Combination> = useMemo(() => {
|
|
50
|
+
const axes = activeAxes.map((name) => ({ name, values: knobs[name]?.values ?? [] }))
|
|
51
|
+
return getCartesianProduct(axes)
|
|
52
|
+
}, [activeAxes, knobs])
|
|
53
|
+
|
|
54
|
+
const buildCellScope = useCallback(
|
|
55
|
+
(combination: Combination): Record<string, unknown> => {
|
|
56
|
+
const cellScope: Record<string, unknown> = { ...scope }
|
|
57
|
+
|
|
58
|
+
for (const [key, value] of Object.entries(combination)) {
|
|
59
|
+
cellScope[toScreamingCase(key)] = value
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return cellScope
|
|
63
|
+
},
|
|
64
|
+
[scope],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const getCombinationLabel = useCallback(
|
|
68
|
+
(combination: Combination): string => activeAxes.map((axis) => String(combination[axis])).join(" · "),
|
|
69
|
+
[activeAxes],
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if (!relevantKnobNames.length) {
|
|
73
|
+
return (
|
|
74
|
+
<Flex padding={16}>
|
|
75
|
+
<Text emphasis="secondary" type="small">
|
|
76
|
+
No permutable knobs detected in this pattern.
|
|
77
|
+
</Text>
|
|
78
|
+
</Flex>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Flex isColumn={true}>
|
|
84
|
+
<Flex padding={8} gap={8} isYCentered={true} isWrapped={true}>
|
|
85
|
+
<Text type="tiny" emphasis="secondary">
|
|
86
|
+
Axes:
|
|
87
|
+
</Text>
|
|
88
|
+
{relevantKnobNames.map((name) => (
|
|
89
|
+
<Checkbox
|
|
90
|
+
key={name}
|
|
91
|
+
label={knobs[name]?.label ?? name}
|
|
92
|
+
isChecked={activeAxes.includes(name)}
|
|
93
|
+
onChange={onToggleAxis(name)}
|
|
94
|
+
isSmall={true}
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
</Flex>
|
|
98
|
+
<div className={styles.permutationsGrid}>
|
|
99
|
+
{combinations.map((combination, index) => (
|
|
100
|
+
<div key={index} className={styles.permutationsCell}>
|
|
101
|
+
<div className={styles.permutationsCellPreview}>
|
|
102
|
+
<LiveProvider code={code} scope={buildCellScope(combination)}>
|
|
103
|
+
<LiveError />
|
|
104
|
+
<LivePreview />
|
|
105
|
+
</LiveProvider>
|
|
106
|
+
</div>
|
|
107
|
+
<div className={styles.permutationsCellLabel}>
|
|
108
|
+
<Text type="tiny" emphasis="secondary">
|
|
109
|
+
{getCombinationLabel(combination)}
|
|
110
|
+
</Text>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
</Flex>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { toScreamingCase } from "../../LiveComponents.utils"
|
|
2
|
+
|
|
3
|
+
type KnobValue = string | number | boolean
|
|
4
|
+
|
|
5
|
+
export type Combination = Record<string, KnobValue>
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_AXIS_COUNT: number = 2
|
|
8
|
+
|
|
9
|
+
export const getPatternKnobNames = (code: string, knobKeys: Array<string>): Array<string> => {
|
|
10
|
+
return knobKeys.filter((key) => {
|
|
11
|
+
const screamingKey: string = toScreamingCase(key)
|
|
12
|
+
return code.includes(`{${screamingKey}}`) || code.includes(`=${screamingKey}`)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const getCartesianProduct = (axes: Array<{ name: string; values: Array<KnobValue> }>): Array<Combination> => {
|
|
17
|
+
if (!axes.length) return []
|
|
18
|
+
|
|
19
|
+
return axes.reduce<Array<Combination>>(
|
|
20
|
+
(combinations, axis) =>
|
|
21
|
+
combinations.flatMap((combo) => axis.values.map((value) => ({ ...combo, [axis.name]: value }))),
|
|
22
|
+
[{}],
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Flex, Text, Heading, Separator } from "@heliosgraphics/ui"
|
|
4
|
+
import type { HeliosScaleType } from "@heliosgraphics/ui"
|
|
5
|
+
import type { FC } from "react"
|
|
6
|
+
import type { PageProps } from "./Page.types"
|
|
7
|
+
|
|
8
|
+
export const Page: FC<PageProps> = ({
|
|
9
|
+
children,
|
|
10
|
+
description,
|
|
11
|
+
breadcrumb,
|
|
12
|
+
disabledPadding,
|
|
13
|
+
footer,
|
|
14
|
+
header,
|
|
15
|
+
contentFlexProps,
|
|
16
|
+
title,
|
|
17
|
+
}) => {
|
|
18
|
+
const commonFlexProps = {
|
|
19
|
+
isColumn: true as const,
|
|
20
|
+
gap: disabledPadding ? (0 as const) : (16 as const),
|
|
21
|
+
padding: disabledPadding ? (0 as const) : ([8, 16, 24] as [HeliosScaleType, HeliosScaleType, HeliosScaleType]),
|
|
22
|
+
withBackground: "primary" as const,
|
|
23
|
+
...contentFlexProps,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
{header}
|
|
29
|
+
<Separator emphasis="tertiary" />
|
|
30
|
+
<Flex {...commonFlexProps}>
|
|
31
|
+
{title && (
|
|
32
|
+
<Flex isColumn={true} padding={disabledPadding ? 16 : 0}>
|
|
33
|
+
<Flex isYCentered={true} gap={4} isBetween={true}>
|
|
34
|
+
{breadcrumb}
|
|
35
|
+
</Flex>
|
|
36
|
+
<Heading level={0}>{title}</Heading>
|
|
37
|
+
</Flex>
|
|
38
|
+
)}
|
|
39
|
+
{description && <Text type="paragraph">{description}</Text>}
|
|
40
|
+
</Flex>
|
|
41
|
+
<Separator emphasis="tertiary" />
|
|
42
|
+
<Flex {...commonFlexProps}>{children}</Flex>
|
|
43
|
+
{footer ?? (
|
|
44
|
+
<Flex withBackground="primary" padding={16} gap={4} isBetween={true} isYCentered={true}>
|
|
45
|
+
<Text type="tiny" emphasis="tertiary">
|
|
46
|
+
© {new Date().getFullYear()}
|
|
47
|
+
</Text>
|
|
48
|
+
</Flex>
|
|
49
|
+
)}
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
import type { FlexProps } from "@heliosgraphics/ui"
|
|
3
|
+
|
|
4
|
+
export interface PageProps {
|
|
5
|
+
breadcrumb?: ReactNode | undefined
|
|
6
|
+
children?: ReactNode | undefined
|
|
7
|
+
description?: string | undefined
|
|
8
|
+
disabledPadding?: boolean | undefined
|
|
9
|
+
footer?: ReactNode | undefined
|
|
10
|
+
header?: ReactNode | undefined
|
|
11
|
+
contentFlexProps?: Partial<FlexProps> | undefined
|
|
12
|
+
title?: string | undefined
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Flex, Heading } from "@heliosgraphics/ui"
|
|
2
|
+
import type { FC } from "react"
|
|
3
|
+
import type { PageBlockProps } from "./PageBlock.types"
|
|
4
|
+
|
|
5
|
+
export const PageBlock: FC<PageBlockProps> = ({ children, title }) => {
|
|
6
|
+
return (
|
|
7
|
+
<Flex isColumn={true} gap={8}>
|
|
8
|
+
{title && (
|
|
9
|
+
<Heading level={2} fontWeight="semibold">
|
|
10
|
+
{title}
|
|
11
|
+
</Heading>
|
|
12
|
+
)}
|
|
13
|
+
{children}
|
|
14
|
+
</Flex>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { type ReactNode, useContext, type FC } from "react"
|
|
4
|
+
import { Text, Flex, Table, Pill, Separator } from "@heliosgraphics/ui"
|
|
5
|
+
import { WorkshopContext } from "../../contexts/WorkshopProvider"
|
|
6
|
+
import { LayoutContext, DEFAULT_DEMO_PATTERN } from "../../contexts/LayoutProvider"
|
|
7
|
+
import { getEditorComponent } from "./PropsTable.utils"
|
|
8
|
+
import type { PropsTableProps } from "./PropsTable.types"
|
|
9
|
+
|
|
10
|
+
export const PropsTable: FC<PropsTableProps> = ({ meta, knobs }) => {
|
|
11
|
+
const context = useContext(WorkshopContext)
|
|
12
|
+
const { activeDemoPattern } = useContext(LayoutContext)
|
|
13
|
+
|
|
14
|
+
if (!meta) return null
|
|
15
|
+
|
|
16
|
+
const renderRow = ([name, value]: [string, unknown], key: number): ReactNode => {
|
|
17
|
+
const row = value as Record<string, unknown>
|
|
18
|
+
const type: string = (row?.["type"] as string) ?? row?.toString?.() ?? "Unknown"
|
|
19
|
+
const isExtends: boolean = name === "_extends"
|
|
20
|
+
const isRequired: boolean = !isExtends && !row?.["isOptional"]
|
|
21
|
+
|
|
22
|
+
if ((name.startsWith("_") && !isExtends) || row?.["alias"] != null) return null
|
|
23
|
+
|
|
24
|
+
const isDisabled: boolean =
|
|
25
|
+
activeDemoPattern !== DEFAULT_DEMO_PATTERN && !activeDemoPattern.toLowerCase().includes(`${name}=`.toLowerCase())
|
|
26
|
+
|
|
27
|
+
const editorComponent = getEditorComponent(
|
|
28
|
+
name,
|
|
29
|
+
type,
|
|
30
|
+
row?.["knob"] as string | undefined,
|
|
31
|
+
knobs,
|
|
32
|
+
context,
|
|
33
|
+
isDisabled,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<tr key={key} className={isExtends ? "ui-bg-secondary" : ""}>
|
|
38
|
+
<td>
|
|
39
|
+
<Flex isYCentered={true} gap={4}>
|
|
40
|
+
{isExtends ? <em>extends</em> : name}
|
|
41
|
+
{isRequired && (
|
|
42
|
+
<Flex>
|
|
43
|
+
<Pill color="orange" label="Required" size="tiny" isMono={true} />
|
|
44
|
+
</Flex>
|
|
45
|
+
)}
|
|
46
|
+
</Flex>
|
|
47
|
+
</td>
|
|
48
|
+
<td>
|
|
49
|
+
<Flex gap={8} isYCentered={true} isBetween={true}>
|
|
50
|
+
<Pill label={type} color="gray" size="tiny" isMono={true} />
|
|
51
|
+
{editorComponent}
|
|
52
|
+
</Flex>
|
|
53
|
+
</td>
|
|
54
|
+
<td>
|
|
55
|
+
<Text type="tiny" emphasis="secondary">
|
|
56
|
+
{(row?.["default"] as string) || "-"}
|
|
57
|
+
</Text>
|
|
58
|
+
</td>
|
|
59
|
+
<td>
|
|
60
|
+
<Text type="tiny" emphasis="secondary">
|
|
61
|
+
{(row?.["description"] as string) || "-"}
|
|
62
|
+
</Text>
|
|
63
|
+
</td>
|
|
64
|
+
</tr>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<Table isMonoHeader={true}>
|
|
71
|
+
<thead>
|
|
72
|
+
<tr>
|
|
73
|
+
<th scope="col">Name</th>
|
|
74
|
+
<th scope="col">Type</th>
|
|
75
|
+
<th scope="col">Default</th>
|
|
76
|
+
<th scope="col">Description</th>
|
|
77
|
+
</tr>
|
|
78
|
+
</thead>
|
|
79
|
+
<tbody>{Object.entries(meta).map(renderRow)}</tbody>
|
|
80
|
+
</Table>
|
|
81
|
+
<Separator emphasis="secondary" />
|
|
82
|
+
</>
|
|
83
|
+
)
|
|
84
|
+
}
|