@scaffold-ui-kit/preview 0.0.0-stage → 2.3.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/LICENSE +21 -0
- package/README.md +1868 -2
- package/application/host/bootstrap.ts +43 -0
- package/application/host/main.ts +31 -0
- package/application/host/shell-host.ts +47 -0
- package/application/index.html +12 -0
- package/application/lib/base-url.ts +15 -0
- package/application/lib/no-code-block.ts +7 -0
- package/application/lib/no-setup.ts +3 -0
- package/application/lib/no-views.ts +6 -0
- package/application/preview/code/code.html +12 -0
- package/application/preview/code/main.ts +89 -0
- package/application/preview/globals.ts +254 -0
- package/application/preview/html/main.ts +93 -0
- package/application/preview/html/preview.html +12 -0
- package/application/preview/react/main.tsx +146 -0
- package/application/preview/react/preview.html +12 -0
- package/application/preview/svelte/Preview.svelte +104 -0
- package/application/preview/svelte/main.ts +13 -0
- package/application/preview/svelte/preview.html +12 -0
- package/application/preview/svelte/story-props.ts +172 -0
- package/application/preview/view/main.ts +124 -0
- package/application/preview/view/view.html +12 -0
- package/application/preview/vue/main.ts +145 -0
- package/application/preview/vue/preview.html +12 -0
- package/application/shells/charcell/components/Application/index.css +37 -0
- package/application/shells/charcell/components/Application/index.tsx +65 -0
- package/application/shells/charcell/components/Bar/index.css +68 -0
- package/application/shells/charcell/components/Bar/index.tsx +60 -0
- package/application/shells/charcell/components/CodePane/index.css +60 -0
- package/application/shells/charcell/components/CodePane/index.tsx +113 -0
- package/application/shells/charcell/components/ComponentStage/index.css +26 -0
- package/application/shells/charcell/components/ComponentStage/index.tsx +110 -0
- package/application/shells/charcell/components/Control/index.css +20 -0
- package/application/shells/charcell/components/Control/index.tsx +118 -0
- package/application/shells/charcell/components/CopyLink/index.tsx +35 -0
- package/application/shells/charcell/components/Description/index.css +6 -0
- package/application/shells/charcell/components/Description/index.tsx +24 -0
- package/application/shells/charcell/components/EditorAddDialog/index.css +9 -0
- package/application/shells/charcell/components/EditorAddDialog/index.tsx +158 -0
- package/application/shells/charcell/components/EditorPanel/index.css +48 -0
- package/application/shells/charcell/components/EditorPanel/index.tsx +496 -0
- package/application/shells/charcell/components/EventsRow/index.css +42 -0
- package/application/shells/charcell/components/EventsRow/index.tsx +68 -0
- package/application/shells/charcell/components/HeadingRow/index.css +11 -0
- package/application/shells/charcell/components/HeadingRow/index.tsx +16 -0
- package/application/shells/charcell/components/Hint/index.css +16 -0
- package/application/shells/charcell/components/Hint/index.tsx +81 -0
- package/application/shells/charcell/components/HtmlWarnings/index.tsx +45 -0
- package/application/shells/charcell/components/KeyHints/index.css +27 -0
- package/application/shells/charcell/components/KeyHints/index.tsx +106 -0
- package/application/shells/charcell/components/KitCodeBlock/index.css +55 -0
- package/application/shells/charcell/components/KitCodeBlock/index.tsx +18 -0
- package/application/shells/charcell/components/Markdown/index.tsx +16 -0
- package/application/shells/charcell/components/Navigation/index.css +13 -0
- package/application/shells/charcell/components/Navigation/index.tsx +98 -0
- package/application/shells/charcell/components/NavigationSplit/index.css +16 -0
- package/application/shells/charcell/components/NavigationSplit/index.tsx +82 -0
- package/application/shells/charcell/components/OptionControls/index.tsx +93 -0
- package/application/shells/charcell/components/PaneStatus/index.css +7 -0
- package/application/shells/charcell/components/PaneStatus/index.tsx +47 -0
- package/application/shells/charcell/components/PaneWidth/index.tsx +45 -0
- package/application/shells/charcell/components/PreviewGrid/index.css +39 -0
- package/application/shells/charcell/components/PreviewGrid/index.tsx +344 -0
- package/application/shells/charcell/components/PreviewPane/index.css +127 -0
- package/application/shells/charcell/components/PreviewPane/index.tsx +182 -0
- package/application/shells/charcell/components/PromptBar/index.tsx +133 -0
- package/application/shells/charcell/components/ScenarioRow/index.css +36 -0
- package/application/shells/charcell/components/ScenarioRow/index.tsx +71 -0
- package/application/shells/charcell/components/Select/index.tsx +48 -0
- package/application/shells/charcell/components/SlotOutlet/index.css +7 -0
- package/application/shells/charcell/components/SlotOutlet/index.tsx +25 -0
- package/application/shells/charcell/components/StageBar/index.css +7 -0
- package/application/shells/charcell/components/StageBar/index.tsx +109 -0
- package/application/shells/charcell/components/StageSplit/index.css +11 -0
- package/application/shells/charcell/components/StageSplit/index.tsx +99 -0
- package/application/shells/charcell/components/StatusBar/index.tsx +30 -0
- package/application/shells/charcell/components/ViewControls/index.css +15 -0
- package/application/shells/charcell/components/ViewControls/index.tsx +58 -0
- package/application/shells/charcell/components/ViewHeading/index.css +13 -0
- package/application/shells/charcell/components/ViewHeading/index.tsx +8 -0
- package/application/shells/charcell/components/ViewStage/index.css +9 -0
- package/application/shells/charcell/components/ViewStage/index.tsx +25 -0
- package/application/shells/charcell/components/WrapToggle/index.tsx +27 -0
- package/application/shells/charcell/configuration.ts +45 -0
- package/application/shells/charcell/controller-context.ts +27 -0
- package/application/shells/charcell/fit-labels.ts +42 -0
- package/application/shells/charcell/html-warnings.ts +18 -0
- package/application/shells/charcell/index.tsx +82 -0
- package/application/shells/charcell/page-loading.html +57 -0
- package/application/shells/charcell/placement.ts +24 -0
- package/application/shells/charcell/shell-options.ts +23 -0
- package/application/shells/charcell/shell.css +37 -0
- package/application/shells/charcell/shell.tsx +25 -0
- package/application/shells/charcell/split-reset.ts +47 -0
- package/application/shells/charcell/stacked-rows.ts +117 -0
- package/application/shells/charcell/stage-toggles.ts +93 -0
- package/application/types/manifest.d.ts +6 -0
- package/application/types/preview-configuration.d.ts +10 -0
- package/application/types/preview-shell.d.ts +7 -0
- package/application/types/react-registry.d.ts +15 -0
- package/application/types/vue-registry.d.ts +6 -0
- package/dist/build.d.ts +24 -0
- package/dist/build.js +34 -0
- package/dist/check.d.ts +48 -0
- package/dist/check.js +781 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +88 -0
- package/dist/context.d.ts +179 -0
- package/dist/context.js +783 -0
- package/dist/controller/catalog.d.ts +339 -0
- package/dist/controller/catalog.js +367 -0
- package/dist/controller/create-preview-controller.d.ts +532 -0
- package/dist/controller/create-preview-controller.js +1707 -0
- package/dist/controller/editor-tree.d.ts +161 -0
- package/dist/controller/editor-tree.js +373 -0
- package/dist/controller/html-source.d.ts +62 -0
- package/dist/controller/html-source.js +61 -0
- package/dist/controller/index.d.ts +9 -0
- package/dist/controller/index.js +12 -0
- package/dist/controller/persistence.d.ts +191 -0
- package/dist/controller/persistence.js +315 -0
- package/dist/controller/regeneration.d.ts +30 -0
- package/dist/controller/regeneration.js +27 -0
- package/dist/controller/source.d.ts +80 -0
- package/dist/controller/source.js +236 -0
- package/dist/controller/store.d.ts +27 -0
- package/dist/controller/store.js +40 -0
- package/dist/declarations/application/shells/charcell/components/Application/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Bar/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/CodePane/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/ComponentStage/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Control/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/CopyLink/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/Description/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/EditorAddDialog/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/EditorPanel/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/EventsRow/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/HeadingRow/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/Hint/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/HtmlWarnings/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/KeyHints/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/KitCodeBlock/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/Markdown/index.d.ts +3 -0
- package/dist/declarations/application/shells/charcell/components/Navigation/index.d.ts +8 -0
- package/dist/declarations/application/shells/charcell/components/NavigationSplit/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/OptionControls/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PaneStatus/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/PaneWidth/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/PreviewGrid/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PreviewPane/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/PromptBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ScenarioRow/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Select/index.d.ts +16 -0
- package/dist/declarations/application/shells/charcell/components/SlotOutlet/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/StageBar/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/StageSplit/index.d.ts +17 -0
- package/dist/declarations/application/shells/charcell/components/StatusBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ViewControls/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/ViewHeading/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/ViewStage/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/WrapToggle/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/configuration.d.ts +31 -0
- package/dist/declarations/application/shells/charcell/controller-context.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/fit-labels.d.ts +18 -0
- package/dist/declarations/application/shells/charcell/html-warnings.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/index.d.ts +39 -0
- package/dist/declarations/application/shells/charcell/placement.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/shell-options.d.ts +15 -0
- package/dist/declarations/application/shells/charcell/shell.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/split-reset.d.ts +30 -0
- package/dist/declarations/application/shells/charcell/stacked-rows.d.ts +58 -0
- package/dist/declarations/application/shells/charcell/stage-toggles.d.ts +28 -0
- package/dist/declarations/src/controller/catalog.d.ts +339 -0
- package/dist/declarations/src/controller/create-preview-controller.d.ts +532 -0
- package/dist/declarations/src/controller/editor-tree.d.ts +161 -0
- package/dist/declarations/src/controller/html-source.d.ts +62 -0
- package/dist/declarations/src/controller/index.d.ts +9 -0
- package/dist/declarations/src/controller/persistence.d.ts +191 -0
- package/dist/declarations/src/controller/regeneration.d.ts +30 -0
- package/dist/declarations/src/controller/source.d.ts +80 -0
- package/dist/declarations/src/controller/store.d.ts +27 -0
- package/dist/declarations/src/emit/common.d.ts +103 -0
- package/dist/declarations/src/emit/manifest.d.ts +138 -0
- package/dist/declarations/src/emit/source.d.ts +87 -0
- package/dist/declarations/src/lib/jsx-roots.d.ts +22 -0
- package/dist/declarations/src/lib/module-of-source.d.ts +18 -0
- package/dist/declarations/src/lib/naming.d.ts +29 -0
- package/dist/declarations/src/lib/react-spelling.d.ts +68 -0
- package/dist/declarations/src/lib/scenario-story.d.ts +41 -0
- package/dist/declarations/src/lib/slot-fill.d.ts +15 -0
- package/dist/declarations/src/lib/types.d.ts +741 -0
- package/dist/declarations/src/options.d.ts +224 -0
- package/dist/declarations/src/protocol.d.ts +541 -0
- package/dist/declarations/src/react-view.d.ts +22 -0
- package/dist/declarations/src/react.d.ts +17 -0
- package/dist/declarations/src/shell.d.ts +99 -0
- package/dist/declarations/src/views.d.ts +152 -0
- package/dist/development-server.d.ts +121 -0
- package/dist/development-server.js +331 -0
- package/dist/emit/common.d.ts +103 -0
- package/dist/emit/common.js +170 -0
- package/dist/emit/html-source.d.ts +46 -0
- package/dist/emit/html-source.js +322 -0
- package/dist/emit/manifest.d.ts +138 -0
- package/dist/emit/manifest.js +74 -0
- package/dist/emit/react.d.ts +16 -0
- package/dist/emit/react.js +210 -0
- package/dist/emit/scratch.d.ts +40 -0
- package/dist/emit/scratch.js +116 -0
- package/dist/emit/source.d.ts +87 -0
- package/dist/emit/source.js +789 -0
- package/dist/emit/svelte.d.ts +9 -0
- package/dist/emit/svelte.js +177 -0
- package/dist/emit/vue.d.ts +2 -0
- package/dist/emit/vue.js +152 -0
- package/dist/enumerate.d.ts +2 -0
- package/dist/enumerate.js +162 -0
- package/dist/generate.d.ts +43 -0
- package/dist/generate.js +302 -0
- package/dist/html-stylesheet.d.ts +43 -0
- package/dist/html-stylesheet.js +145 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -0
- package/dist/landing.d.ts +16 -0
- package/dist/landing.js +45 -0
- package/dist/lib/exported-types.d.ts +42 -0
- package/dist/lib/exported-types.js +86 -0
- package/dist/lib/html-attributes.d.ts +2 -0
- package/dist/lib/html-attributes.js +111 -0
- package/dist/lib/html-warnings.d.ts +19 -0
- package/dist/lib/html-warnings.js +79 -0
- package/dist/lib/jsx-roots.d.ts +22 -0
- package/dist/lib/jsx-roots.js +243 -0
- package/dist/lib/module-of-source.d.ts +18 -0
- package/dist/lib/module-of-source.js +24 -0
- package/dist/lib/naming.d.ts +29 -0
- package/dist/lib/naming.js +56 -0
- package/dist/lib/parse-type.d.ts +32 -0
- package/dist/lib/parse-type.js +103 -0
- package/dist/lib/react-spelling.d.ts +68 -0
- package/dist/lib/react-spelling.js +197 -0
- package/dist/lib/scenario-story.d.ts +41 -0
- package/dist/lib/scenario-story.js +84 -0
- package/dist/lib/slot-fill.d.ts +15 -0
- package/dist/lib/slot-fill.js +92 -0
- package/dist/lib/template-files.d.ts +14 -0
- package/dist/lib/template-files.js +25 -0
- package/dist/lib/types.d.ts +741 -0
- package/dist/lib/types.js +13 -0
- package/dist/lib/void-elements.d.ts +7 -0
- package/dist/lib/void-elements.js +13 -0
- package/dist/load.d.ts +37 -0
- package/dist/load.js +82 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.js +464 -0
- package/dist/options.d.ts +224 -0
- package/dist/options.js +223 -0
- package/dist/prop-types.d.ts +26 -0
- package/dist/prop-types.js +102 -0
- package/dist/props-from.d.ts +84 -0
- package/dist/props-from.js +510 -0
- package/dist/protocol.d.ts +541 -0
- package/dist/protocol.js +819 -0
- package/dist/react-view.d.ts +22 -0
- package/dist/react-view.js +36 -0
- package/dist/react.d.ts +17 -0
- package/dist/react.js +23 -0
- package/dist/sass-importers.d.ts +26 -0
- package/dist/sass-importers.js +32 -0
- package/dist/shell-acceptance.d.ts +21 -0
- package/dist/shell-acceptance.js +37 -0
- package/dist/shell-page.d.ts +36 -0
- package/dist/shell-page.js +90 -0
- package/dist/shell-peers.d.ts +38 -0
- package/dist/shell-peers.js +61 -0
- package/dist/shell.d.ts +99 -0
- package/dist/shell.js +52 -0
- package/dist/shells/charcell/page.d.ts +8 -0
- package/dist/shells/charcell/page.js +69 -0
- package/dist/story-source.d.ts +113 -0
- package/dist/story-source.js +271 -0
- package/dist/story.d.ts +5 -0
- package/dist/story.js +28 -0
- package/dist/testing.d.ts +218 -0
- package/dist/testing.js +284 -0
- package/dist/views.d.ts +152 -0
- package/dist/views.js +7 -0
- package/dist/vite-configuration.d.ts +114 -0
- package/dist/vite-configuration.js +578 -0
- package/package.json +123 -3
- package/src/build.ts +51 -0
- package/src/check.ts +1062 -0
- package/src/cli.ts +123 -0
- package/src/context.ts +1168 -0
- package/src/controller/catalog.ts +719 -0
- package/src/controller/create-preview-controller.ts +2683 -0
- package/src/controller/editor-tree.ts +530 -0
- package/src/controller/html-source.ts +114 -0
- package/src/controller/index.ts +105 -0
- package/src/controller/persistence.ts +403 -0
- package/src/controller/regeneration.ts +55 -0
- package/src/controller/source.ts +333 -0
- package/src/controller/store.ts +55 -0
- package/src/development-server.ts +442 -0
- package/src/emit/common.ts +201 -0
- package/src/emit/html-source.ts +402 -0
- package/src/emit/manifest.ts +241 -0
- package/src/emit/react.ts +263 -0
- package/src/emit/scratch.ts +175 -0
- package/src/emit/source.ts +1094 -0
- package/src/emit/svelte.ts +230 -0
- package/src/emit/vue.ts +194 -0
- package/src/enumerate.ts +220 -0
- package/src/generate.ts +443 -0
- package/src/html-stylesheet.ts +173 -0
- package/src/index.ts +60 -0
- package/src/landing.ts +58 -0
- package/src/lib/exported-types.ts +114 -0
- package/src/lib/html-attributes.ts +113 -0
- package/src/lib/html-warnings.ts +81 -0
- package/src/lib/jsx-roots.ts +238 -0
- package/src/lib/module-of-source.ts +26 -0
- package/src/lib/naming.ts +65 -0
- package/src/lib/parse-type.ts +143 -0
- package/src/lib/react-spelling.ts +235 -0
- package/src/lib/scenario-story.ts +97 -0
- package/src/lib/slot-fill.ts +102 -0
- package/src/lib/template-files.ts +31 -0
- package/src/lib/types.ts +778 -0
- package/src/lib/void-elements.ts +15 -0
- package/src/load.ts +144 -0
- package/src/model.ts +568 -0
- package/src/options.ts +435 -0
- package/src/prop-types.ts +133 -0
- package/src/props-from.ts +678 -0
- package/src/protocol.ts +1135 -0
- package/src/react-view.ts +45 -0
- package/src/react.ts +34 -0
- package/src/sass-importers.ts +45 -0
- package/src/shell-acceptance.ts +45 -0
- package/src/shell-page.ts +133 -0
- package/src/shell-peers.ts +70 -0
- package/src/shell.ts +194 -0
- package/src/shells/charcell/page.ts +82 -0
- package/src/story-source.ts +391 -0
- package/src/story.ts +42 -0
- package/src/testing.ts +497 -0
- package/src/views.ts +183 -0
- package/src/vite-configuration.ts +675 -0
package/src/options.ts
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// The composable preview-options contract: the preview block's `options`,
|
|
2
|
+
// `canvas`, `mount` and `viewCanvas`, and the derived helpers the host +
|
|
3
|
+
// iframes share. Browser-safe - no node imports; both the Vite app and the
|
|
4
|
+
// kit's configuration type against this file.
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
PreviewPaneWidth,
|
|
8
|
+
PreviewThemeConfiguration,
|
|
9
|
+
} from './lib/types.js';
|
|
10
|
+
|
|
11
|
+
/** An option's runtime value: a string (a choice) or a boolean. */
|
|
12
|
+
export type OptionValue = string | boolean;
|
|
13
|
+
|
|
14
|
+
/** One value a choice option offers. */
|
|
15
|
+
export interface OptionChoice {
|
|
16
|
+
value: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** What kind of value an option holds: one of its `values`, or a boolean. */
|
|
21
|
+
export type OptionKind = 'choice' | 'boolean';
|
|
22
|
+
|
|
23
|
+
/** The group of the kit's own settings, such as its theme: what sets up the
|
|
24
|
+
* kit as a whole rather than the setting a component is shown in. */
|
|
25
|
+
export const KIT_OPTION_GROUP = 'kit';
|
|
26
|
+
|
|
27
|
+
/** The group of the options that set what a component is shown in, such as a
|
|
28
|
+
* color mode, a surface or the padding around it. An option without a group
|
|
29
|
+
* belongs to it. */
|
|
30
|
+
export const CONTEXT_OPTION_GROUP = 'context';
|
|
31
|
+
|
|
32
|
+
/** The prefix of a shell option's id in the preview's state, address and
|
|
33
|
+
* storage: a shell's `mode` is `shell.mode`, so it cannot collide with a kit
|
|
34
|
+
* option of the same name. */
|
|
35
|
+
export const SHELL_OPTION_PREFIX = 'shell.';
|
|
36
|
+
|
|
37
|
+
/** Which element an apply targets. `'root'` is the preview `<html>` (the
|
|
38
|
+
* default), `'body'` is `<body>`, and `{ ref }` is a named node in the
|
|
39
|
+
* kit's `mount` wrapper (resolved against the ref map). */
|
|
40
|
+
export type ApplyTarget = 'root' | 'body' | { ref: string };
|
|
41
|
+
|
|
42
|
+
/** How an option affects the preview canvas. The union is open by design - a
|
|
43
|
+
* new strategy slots in without touching the registry. `root-attribute` /
|
|
44
|
+
* `class` / `css-var` may carry a `target` to hit a `mount` wrapper
|
|
45
|
+
* node instead of the document root. */
|
|
46
|
+
export type OptionApply =
|
|
47
|
+
/** Set `<attribute>="<value>"` on the target element (default the preview
|
|
48
|
+
* `<html>`); `false` or an empty value removes it. With `omitAtDefault`,
|
|
49
|
+
* the option's default value removes it too, for a kit whose default
|
|
50
|
+
* renders with no attribute, such as a default color mode. */
|
|
51
|
+
| {
|
|
52
|
+
type: 'root-attribute';
|
|
53
|
+
attribute: string;
|
|
54
|
+
target?: ApplyTarget;
|
|
55
|
+
omitAtDefault?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Swap the active theme stylesheet by value (`themes/<id>/theme.css`
|
|
58
|
+
* under the URL the preview is served from). */
|
|
59
|
+
| { type: 'stylesheet' }
|
|
60
|
+
/** Toggle `className` on the target by the value's truth; without
|
|
61
|
+
* `className`, set the value as the target's `data-<namePrefix>-preview-class`
|
|
62
|
+
* attribute for the kit's CSS to key off, `false` or an empty value removing
|
|
63
|
+
* it. `on` names `'root'` or `'body'`; `target` names either as well, or
|
|
64
|
+
* a wrapper node, and wins where both are given. */
|
|
65
|
+
| {
|
|
66
|
+
type: 'class';
|
|
67
|
+
on?: 'root' | 'body';
|
|
68
|
+
className?: string;
|
|
69
|
+
target?: ApplyTarget;
|
|
70
|
+
}
|
|
71
|
+
/** Set a CSS custom property to the value on the target element; `false` or
|
|
72
|
+
* an empty value removes it. */
|
|
73
|
+
| { type: 'css-var'; var: string; target?: ApplyTarget };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* One preview option: what it means, never where or how it is shown. Its kind
|
|
77
|
+
* follows from its fields - an option with `values` is a choice among them
|
|
78
|
+
* with a string `default`, and one without is a boolean with a boolean
|
|
79
|
+
* `default` - and the preview's interface picks the widget for that kind.
|
|
80
|
+
*/
|
|
81
|
+
export interface PreviewOption {
|
|
82
|
+
/** Stable key - the message payload key and the manifest per-story override key. */
|
|
83
|
+
id: string;
|
|
84
|
+
/** The option's name, as its control is labeled. */
|
|
85
|
+
label: string;
|
|
86
|
+
/** A longer description, such as a control's tooltip; markdown allowed. */
|
|
87
|
+
title?: string;
|
|
88
|
+
/** The values of a choice option. Omitted, the option is a boolean. */
|
|
89
|
+
values?: OptionChoice[];
|
|
90
|
+
/** The starting value: one of `values` for a choice, a boolean otherwise. */
|
|
91
|
+
default: OptionValue;
|
|
92
|
+
/** A free-form hint for where the option belongs: `'context'` (the
|
|
93
|
+
* default) for the setting a component is shown in, such as its color
|
|
94
|
+
* mode or the padding around it; `'kit'` for the kit's own settings, such
|
|
95
|
+
* as its theme; or a name of the kit's choosing for a set that belongs
|
|
96
|
+
* together, such as canvas settings. An interface places a group it does
|
|
97
|
+
* not know in a default place. */
|
|
98
|
+
group?: string;
|
|
99
|
+
/** `global` = one value for the whole session; `story` = a value per
|
|
100
|
+
* selected story, seeded from its story file, that lasts until another story
|
|
101
|
+
* is selected. */
|
|
102
|
+
scope: 'global' | 'story';
|
|
103
|
+
/** How the value hits the canvas. */
|
|
104
|
+
apply: OptionApply;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* An option a shell declares for itself, such as its own color mode or
|
|
109
|
+
* density: a {@link PreviewOption} without `apply` or `scope`. Its value
|
|
110
|
+
* never reaches the kit's iframes; the shell reads it from the preview's
|
|
111
|
+
* state under the id {@link shellOptionId} gives it, and it is global.
|
|
112
|
+
*/
|
|
113
|
+
export type PreviewShellOption = Omit<PreviewOption, 'apply' | 'scope'>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The id a shell option has in the preview's state, address and storage.
|
|
117
|
+
*
|
|
118
|
+
* @param id - The option's id as the shell declares it.
|
|
119
|
+
* @returns The id prefixed with `shell.`.
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* shellOptionId('mode'); // 'shell.mode'
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function shellOptionId(id: string): string {
|
|
126
|
+
return `${SHELL_OPTION_PREFIX}${id}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The kit-authored canvas styling: what the preview page paints with. The
|
|
130
|
+
* preview's canvas stylesheet is a generic template over these values - the
|
|
131
|
+
* kit's token vocabulary lives HERE, never in the preview. Any wrapper
|
|
132
|
+
* geometry (surface fills, insets, padding) a kit wants lives in `extraCss`,
|
|
133
|
+
* keyed off the nodes it declares in `mount`. Values are raw CSS. */
|
|
134
|
+
export interface PreviewCanvasConfiguration {
|
|
135
|
+
/** Prefix for every preview-owned DOM name - injected element ids, the
|
|
136
|
+
* class-apply data attribute (`data-<prefix>-preview-class`). */
|
|
137
|
+
namePrefix: string;
|
|
138
|
+
/** Canvas page background, painted on `<body>`. Optional: a kit whose
|
|
139
|
+
* stylesheet already states its page canvas (the kit's own `html`/`body`
|
|
140
|
+
* rules, loaded with the theme) omits it, and the preview paints nothing -
|
|
141
|
+
* the preview document *is* the kit's page, and what the page is stays
|
|
142
|
+
* the kit's (and the theme's) call rather than a value restated here. */
|
|
143
|
+
pageBackground?: string;
|
|
144
|
+
/** Extra kit-authored CSS appended to the canvas stylesheet - where a kit
|
|
145
|
+
* styles its `mount` wrapper nodes and any option-driven geometry. */
|
|
146
|
+
extraCss?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The canvas of a custom view's iframe, which shows a kit-authored view
|
|
150
|
+
* rather than a mounted component: no `mount` wrapper, so none of the
|
|
151
|
+
* component frame's geometry applies. A kit sets it as `viewCanvas`;
|
|
152
|
+
* without it a view gets `canvas.pageBackground` and nothing else. The
|
|
153
|
+
* name prefix stays `canvas.namePrefix`. Options still apply to a view,
|
|
154
|
+
* except one targeting a `mount` node by `ref`, which a view lacks. */
|
|
155
|
+
export interface PreviewViewCanvasConfiguration {
|
|
156
|
+
/** The view page's background, painted on `<body>`. */
|
|
157
|
+
pageBackground?: string;
|
|
158
|
+
/** Extra kit-authored CSS for the view page. */
|
|
159
|
+
extraCss?: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** A node in a kit's `mount` wrapper tree - plain, framework-agnostic
|
|
163
|
+
* DOM rendered around the `#app` mount, identical across every preview iframe.
|
|
164
|
+
* The preview knows nothing of what these nodes mean; the kit styles them via
|
|
165
|
+
* `canvas.extraCss` and targets options at them by `ref`. */
|
|
166
|
+
export interface PreviewMountNode {
|
|
167
|
+
/** Element tag. Defaults to `'div'`. */
|
|
168
|
+
tag?: string;
|
|
169
|
+
/** Static attributes rendered on the element (e.g. a surface-cascade attribute). */
|
|
170
|
+
attributes?: Record<string, string>;
|
|
171
|
+
/** Static class names. */
|
|
172
|
+
classes?: string[];
|
|
173
|
+
/** Inline `style` text. */
|
|
174
|
+
style?: string;
|
|
175
|
+
/** Name for the ref map - the handle `apply.target = { ref }` resolves to,
|
|
176
|
+
* and how kit CSS and option targeting address this node. */
|
|
177
|
+
ref?: string;
|
|
178
|
+
/** Child nodes, rendered in order. */
|
|
179
|
+
children?: PreviewMountNode[];
|
|
180
|
+
/** Marks the node the `#app` mount is reparented into. Exactly one node in
|
|
181
|
+
* the tree sets this. */
|
|
182
|
+
mount?: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The kind of an option: a choice when it has `values`, a boolean otherwise.
|
|
187
|
+
*
|
|
188
|
+
* @param option - The option.
|
|
189
|
+
* @returns `'choice'` or `'boolean'`.
|
|
190
|
+
* @example
|
|
191
|
+
* ```ts
|
|
192
|
+
* optionKind({ values: [{ value: 'light', label: 'light' }] }); // 'choice'
|
|
193
|
+
* optionKind({}); // 'boolean'
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
export function optionKind(option: Pick<PreviewOption, 'values'>): OptionKind {
|
|
197
|
+
return option.values !== undefined ? 'choice' : 'boolean';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The group an option belongs to: its `group`, or `'context'` without one.
|
|
202
|
+
*
|
|
203
|
+
* @param option - The option.
|
|
204
|
+
* @returns The group name.
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* optionGroup({ group: 'kit' }); // 'kit'
|
|
208
|
+
* optionGroup({}); // 'context'
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export function optionGroup(option: Pick<PreviewOption, 'group'>): string {
|
|
212
|
+
return option.group ?? CONTEXT_OPTION_GROUP;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Whether `value` is a value `option` can hold: one of its values for a
|
|
217
|
+
* choice, a boolean for a boolean.
|
|
218
|
+
*
|
|
219
|
+
* @param option - The option.
|
|
220
|
+
* @param value - The candidate value.
|
|
221
|
+
* @returns Whether the option accepts it.
|
|
222
|
+
*/
|
|
223
|
+
export function acceptsOptionValue(
|
|
224
|
+
option: Pick<PreviewOption, 'values'>,
|
|
225
|
+
value: unknown
|
|
226
|
+
): value is OptionValue {
|
|
227
|
+
if (optionKind(option) === 'boolean') return typeof value === 'boolean';
|
|
228
|
+
return (
|
|
229
|
+
typeof value === 'string' &&
|
|
230
|
+
(option.values ?? []).some((choice) => choice.value === value)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Every key of `T`, each `true`: a record a key list is read from, so a
|
|
235
|
+
* field added to `T` fails to type-check until the record lists it. */
|
|
236
|
+
type KeySet<T> = { [Key in keyof Required<T>]: true };
|
|
237
|
+
|
|
238
|
+
/** The keys an option takes. */
|
|
239
|
+
const OPTION_KEYS = Object.keys({
|
|
240
|
+
id: true,
|
|
241
|
+
label: true,
|
|
242
|
+
title: true,
|
|
243
|
+
values: true,
|
|
244
|
+
default: true,
|
|
245
|
+
group: true,
|
|
246
|
+
scope: true,
|
|
247
|
+
apply: true,
|
|
248
|
+
} satisfies KeySet<PreviewOption>);
|
|
249
|
+
|
|
250
|
+
/** The keys a choice option's value takes. */
|
|
251
|
+
const CHOICE_KEYS = Object.keys({
|
|
252
|
+
value: true,
|
|
253
|
+
label: true,
|
|
254
|
+
} satisfies KeySet<OptionChoice>);
|
|
255
|
+
|
|
256
|
+
/** The keys each apply strategy takes, by its type: one entry per member of
|
|
257
|
+
* `OptionApply`, listing every key of that member. A strategy of a type
|
|
258
|
+
* the preview does not know is not checked. */
|
|
259
|
+
const APPLY_KEY_SETS = {
|
|
260
|
+
'root-attribute': {
|
|
261
|
+
type: true,
|
|
262
|
+
attribute: true,
|
|
263
|
+
target: true,
|
|
264
|
+
omitAtDefault: true,
|
|
265
|
+
},
|
|
266
|
+
stylesheet: { type: true },
|
|
267
|
+
class: { type: true, on: true, className: true, target: true },
|
|
268
|
+
'css-var': { type: true, var: true, target: true },
|
|
269
|
+
} satisfies {
|
|
270
|
+
[Type in OptionApply['type']]: KeySet<Extract<OptionApply, { type: Type }>>;
|
|
271
|
+
};
|
|
272
|
+
const APPLY_KEYS: Record<string, readonly string[]> = Object.fromEntries(
|
|
273
|
+
Object.entries(APPLY_KEY_SETS).map(([type, keys]) => [
|
|
274
|
+
type,
|
|
275
|
+
Object.keys(keys),
|
|
276
|
+
])
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
/** The keys of `value` that are not among `allowed`, `skipped` aside. A
|
|
280
|
+
* key set to `undefined` is absent. */
|
|
281
|
+
function unknownKeys(
|
|
282
|
+
value: object,
|
|
283
|
+
allowed: readonly string[],
|
|
284
|
+
skipped: readonly string[] = []
|
|
285
|
+
): string[] {
|
|
286
|
+
return Object.entries(value)
|
|
287
|
+
.filter(
|
|
288
|
+
([key, entry]) =>
|
|
289
|
+
entry !== undefined && !allowed.includes(key) && !skipped.includes(key)
|
|
290
|
+
)
|
|
291
|
+
.map(([key]) => key);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The problems with a kit's options, each naming the option: a missing or
|
|
296
|
+
* repeated id, a scope other than `global` or `story`, an option whose
|
|
297
|
+
* kind is ambiguous - a choice needs `values` and a string `default` among
|
|
298
|
+
* them, a boolean no `values` and a boolean `default` - and a key the
|
|
299
|
+
* option, one of its values or its `apply` does not take. Empty when every
|
|
300
|
+
* option is sound.
|
|
301
|
+
*
|
|
302
|
+
* @param options - The preview block's `options`.
|
|
303
|
+
* @returns One message per problem.
|
|
304
|
+
*/
|
|
305
|
+
export function optionProblems(options: unknown): string[] {
|
|
306
|
+
if (!Array.isArray(options)) return ['preview.options must be an array'];
|
|
307
|
+
const problems: string[] = [];
|
|
308
|
+
const seen = new Set<string>();
|
|
309
|
+
options.forEach((candidate: unknown, index) => {
|
|
310
|
+
const option = candidate as Partial<PreviewOption> &
|
|
311
|
+
Record<string, unknown>;
|
|
312
|
+
if (!option || typeof option !== 'object') {
|
|
313
|
+
problems.push(`preview.options[${index}] must be an object`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const name =
|
|
317
|
+
typeof option.id === 'string' && option.id
|
|
318
|
+
? `preview.options[${index}] ('${option.id}')`
|
|
319
|
+
: `preview.options[${index}]`;
|
|
320
|
+
if (typeof option.id !== 'string' || !option.id) {
|
|
321
|
+
problems.push(`${name}: id must be a non-empty string`);
|
|
322
|
+
} else if (seen.has(option.id)) {
|
|
323
|
+
problems.push(`${name}: another option has the id '${option.id}'`);
|
|
324
|
+
} else {
|
|
325
|
+
seen.add(option.id);
|
|
326
|
+
}
|
|
327
|
+
if (typeof option.label !== 'string')
|
|
328
|
+
problems.push(`${name}: label must be a string`);
|
|
329
|
+
if (option.scope !== 'global' && option.scope !== 'story')
|
|
330
|
+
problems.push(`${name}: scope must be 'global' or 'story'`);
|
|
331
|
+
if (
|
|
332
|
+
option.group !== undefined &&
|
|
333
|
+
(typeof option.group !== 'string' || !option.group)
|
|
334
|
+
)
|
|
335
|
+
problems.push(`${name}: group must be a non-empty string`);
|
|
336
|
+
if ('slot' in option)
|
|
337
|
+
problems.push(
|
|
338
|
+
`${name}: slot is not an option field - an option says where it belongs with group`
|
|
339
|
+
);
|
|
340
|
+
if ('control' in option)
|
|
341
|
+
problems.push(
|
|
342
|
+
`${name}: control is not an option field - an option with values is a choice, one with a boolean default a boolean`
|
|
343
|
+
);
|
|
344
|
+
// `slot` and `control` are named above with where their meaning went.
|
|
345
|
+
for (const key of unknownKeys(option, OPTION_KEYS, ['slot', 'control']))
|
|
346
|
+
problems.push(
|
|
347
|
+
`${name}: unknown key '${key}'; an option takes ${OPTION_KEYS.join(', ')}`
|
|
348
|
+
);
|
|
349
|
+
if (option.values !== undefined) {
|
|
350
|
+
const values = option.values as unknown;
|
|
351
|
+
if (
|
|
352
|
+
!Array.isArray(values) ||
|
|
353
|
+
values.length === 0 ||
|
|
354
|
+
values.some(
|
|
355
|
+
(choice: Partial<OptionChoice>) =>
|
|
356
|
+
!choice ||
|
|
357
|
+
typeof choice.value !== 'string' ||
|
|
358
|
+
typeof choice.label !== 'string'
|
|
359
|
+
)
|
|
360
|
+
) {
|
|
361
|
+
problems.push(
|
|
362
|
+
`${name}: values must be a non-empty list of { value, label } strings`
|
|
363
|
+
);
|
|
364
|
+
} else if (typeof option.default !== 'string') {
|
|
365
|
+
problems.push(
|
|
366
|
+
`${name}: a choice option (one with values) needs a string default`
|
|
367
|
+
);
|
|
368
|
+
} else if (
|
|
369
|
+
!values.some((choice: OptionChoice) => choice.value === option.default)
|
|
370
|
+
) {
|
|
371
|
+
problems.push(
|
|
372
|
+
`${name}: default '${option.default}' is not one of its values`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
} else if (typeof option.default !== 'boolean') {
|
|
376
|
+
problems.push(
|
|
377
|
+
`${name}: an option without values is a boolean and needs a boolean default; a choice lists its values`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
if (Array.isArray(option.values))
|
|
381
|
+
option.values.forEach((choice: unknown, choiceIndex) => {
|
|
382
|
+
if (!choice || typeof choice !== 'object') return;
|
|
383
|
+
for (const key of unknownKeys(choice, CHOICE_KEYS))
|
|
384
|
+
problems.push(
|
|
385
|
+
`${name}: unknown key 'values[${choiceIndex}].${key}'; a value takes ${CHOICE_KEYS.join(', ')}`
|
|
386
|
+
);
|
|
387
|
+
});
|
|
388
|
+
if (!option.apply || typeof option.apply !== 'object') {
|
|
389
|
+
problems.push(
|
|
390
|
+
`${name}: apply must describe how the value hits the canvas`
|
|
391
|
+
);
|
|
392
|
+
} else {
|
|
393
|
+
const type = (option.apply as { type?: unknown }).type;
|
|
394
|
+
if (typeof type === 'string' && Object.hasOwn(APPLY_KEYS, type)) {
|
|
395
|
+
const allowed = APPLY_KEYS[type];
|
|
396
|
+
for (const key of unknownKeys(option.apply, allowed))
|
|
397
|
+
problems.push(
|
|
398
|
+
`${name}: unknown key 'apply.${key}'; a '${type}' apply takes ${allowed.join(', ')}`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return problems;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** The option registry keyed by id - O(1) lookup when applying a payload. */
|
|
407
|
+
export function optionsById(
|
|
408
|
+
options: PreviewOption[]
|
|
409
|
+
): Record<string, PreviewOption> {
|
|
410
|
+
return Object.fromEntries(options.map((option) => [option.id, option]));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** Default values for the options in a scope (or all scopes). */
|
|
414
|
+
export function optionDefaults(
|
|
415
|
+
options: Array<Pick<PreviewOption, 'id' | 'default' | 'scope'>>,
|
|
416
|
+
scope?: 'global' | 'story'
|
|
417
|
+
): Record<string, OptionValue> {
|
|
418
|
+
const out: Record<string, OptionValue> = {};
|
|
419
|
+
for (const option of options) {
|
|
420
|
+
if (!scope || option.scope === scope) out[option.id] = option.default;
|
|
421
|
+
}
|
|
422
|
+
return out;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* The served URL of a theme's stylesheet (the generator copies it there).
|
|
427
|
+
*
|
|
428
|
+
* @param themeId - The theme's id.
|
|
429
|
+
* @param baseUrl - The URL the preview is served under, ending in `/`; the
|
|
430
|
+
* root of the origin, `/`, by default.
|
|
431
|
+
* @returns The stylesheet's URL, such as `/themes/light/theme.css`.
|
|
432
|
+
*/
|
|
433
|
+
export function themeHref(themeId: string, baseUrl = '/'): string {
|
|
434
|
+
return `${baseUrl}themes/${themeId}/theme.css`;
|
|
435
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Map one component prop → a control spec for the host's controls panel. Pure
|
|
2
|
+
// function of the prop (name, parsed type, default) plus the kit's control
|
|
3
|
+
// hints (its token-config hook - see PreviewControlHints).
|
|
4
|
+
|
|
5
|
+
import { parseType } from './lib/parse-type.js';
|
|
6
|
+
import type {
|
|
7
|
+
PreviewControlHints,
|
|
8
|
+
PropTypeSpecification,
|
|
9
|
+
JsonValue,
|
|
10
|
+
} from './lib/types.js';
|
|
11
|
+
|
|
12
|
+
function isColorProp(name: string): boolean {
|
|
13
|
+
return name === 'color' || /color$/i.test(name);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Whether the kit gives color tokens for a color select to offer. */
|
|
17
|
+
function hasColorTokens(hints: PreviewControlHints): boolean {
|
|
18
|
+
return (hints.colorTokens ?? []).some((token) => token != null);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A union of string literals, template literal types (`` `accent-${string}` ``),
|
|
22
|
+
* `string`, or named types - the shapes a color-token type takes, which the
|
|
23
|
+
* color select can offer from the kit's token list whatever they expand to. */
|
|
24
|
+
const NON_STRING_KEYWORDS = new Set([
|
|
25
|
+
'boolean',
|
|
26
|
+
'number',
|
|
27
|
+
'bigint',
|
|
28
|
+
'object',
|
|
29
|
+
'symbol',
|
|
30
|
+
'unknown',
|
|
31
|
+
'any',
|
|
32
|
+
'never',
|
|
33
|
+
'null',
|
|
34
|
+
'void',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
function isStringLikeType(type: string): boolean {
|
|
38
|
+
return type
|
|
39
|
+
.split('|')
|
|
40
|
+
.map((part) => part.trim())
|
|
41
|
+
.every(
|
|
42
|
+
(part) =>
|
|
43
|
+
part === 'string' ||
|
|
44
|
+
part === 'undefined' ||
|
|
45
|
+
/^'[^']*'$/.test(part) ||
|
|
46
|
+
/^"[^"]*"$/.test(part) ||
|
|
47
|
+
/^`[^`]*`$/.test(part) ||
|
|
48
|
+
(!NON_STRING_KEYWORDS.has(part) &&
|
|
49
|
+
/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(part))
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The control a prop gets in the controls panel, from its declared type. A
|
|
55
|
+
* prop whose name ends in `color`, in any case (`color`, `borderColor`), and
|
|
56
|
+
* whose type is string-like gets the
|
|
57
|
+
* color select, offering the kit's color tokens, when the kit gives any;
|
|
58
|
+
* otherwise it is treated by its declared type like any other prop.
|
|
59
|
+
*
|
|
60
|
+
* @param componentName - The component the prop belongs to.
|
|
61
|
+
* @param propName - The prop's name.
|
|
62
|
+
* @param def - The prop's declared type, resolved type and default.
|
|
63
|
+
* @param hints - The kit's control hints; their `colorTokens` turn on the
|
|
64
|
+
* color select.
|
|
65
|
+
* @returns The prop's control specification.
|
|
66
|
+
*/
|
|
67
|
+
export function propTypeForProp(
|
|
68
|
+
componentName: string,
|
|
69
|
+
propName: string,
|
|
70
|
+
def: { type: string; resolvedType?: string; default?: JsonValue },
|
|
71
|
+
hints: PreviewControlHints = {}
|
|
72
|
+
): PropTypeSpecification {
|
|
73
|
+
const parsed = parseType(def.resolvedType ?? def.type);
|
|
74
|
+
const hasDefault = def.default !== undefined;
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
hasColorTokens(hints) &&
|
|
78
|
+
isColorProp(propName) &&
|
|
79
|
+
(parsed.kind === 'string' ||
|
|
80
|
+
(parsed.kind === 'enum' &&
|
|
81
|
+
parsed.options.every((option) => typeof option === 'string')) ||
|
|
82
|
+
isStringLikeType(def.type))
|
|
83
|
+
) {
|
|
84
|
+
return { name: propName, control: 'color-select', include: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
switch (parsed.kind) {
|
|
88
|
+
case 'boolean':
|
|
89
|
+
return { name: propName, control: 'boolean', include: true };
|
|
90
|
+
|
|
91
|
+
case 'number': {
|
|
92
|
+
if (propName === 'size') {
|
|
93
|
+
return {
|
|
94
|
+
name: propName,
|
|
95
|
+
control: 'number',
|
|
96
|
+
number: { min: 0.25, max: 4, step: 0.05 },
|
|
97
|
+
include: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { name: propName, control: 'number', include: true };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
case 'enum': {
|
|
104
|
+
const options: (string | boolean | undefined)[] = [...parsed.options];
|
|
105
|
+
if (!hasDefault) options.unshift(undefined);
|
|
106
|
+
return { name: propName, control: 'select', options, include: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case 'string':
|
|
110
|
+
case 'stringOrNumber':
|
|
111
|
+
return { name: propName, control: 'text', include: true };
|
|
112
|
+
|
|
113
|
+
case 'complex':
|
|
114
|
+
// Record<...>, X[], injected interfaces - driver-internal or too complex
|
|
115
|
+
// for a control. Omit; a story file can supply a default prop if required.
|
|
116
|
+
return { name: propName, control: 'text', include: false };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Suggested default prop value from the template default (and the kit's
|
|
121
|
+
* component-size hint, when it supplies one). */
|
|
122
|
+
export function defaultPropForProp(
|
|
123
|
+
componentName: string,
|
|
124
|
+
propName: string,
|
|
125
|
+
def: { type: string; default?: JsonValue },
|
|
126
|
+
hints: PreviewControlHints
|
|
127
|
+
): JsonValue | undefined {
|
|
128
|
+
if (def.default !== undefined) return def.default;
|
|
129
|
+
if (propName === 'size' && parseType(def.type).kind === 'number') {
|
|
130
|
+
return hints.componentSizeRem?.(componentName);
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|