@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Shared data shapes for the story generator. Everything here is
|
|
2
|
+
// framework-agnostic - the emitters (emit/<target>.ts) translate these into
|
|
3
|
+
// each framework's own syntax.
|
|
4
|
+
/** The default: a component renders in every target unless it narrows the set. */
|
|
5
|
+
export const ALL_TARGETS = ['react', 'vue', 'svelte'];
|
|
6
|
+
/** The keys of the preview block its pages read, which must be JSON. */
|
|
7
|
+
export const PREVIEW_BROWSER_KEYS = [
|
|
8
|
+
'options',
|
|
9
|
+
'canvas',
|
|
10
|
+
'themes',
|
|
11
|
+
'mount',
|
|
12
|
+
'viewCanvas',
|
|
13
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// HTML void elements: an html slot fill with one of these tags renders
|
|
2
|
+
// self-closed and takes no children. The list is the engine core's, so the
|
|
3
|
+
// preview and the templates' own output agree on it.
|
|
4
|
+
import { VOID_ELEMENTS } from '@js-template-engine/core';
|
|
5
|
+
/**
|
|
6
|
+
* Whether a tag is an HTML void element.
|
|
7
|
+
*
|
|
8
|
+
* @param tag - The tag name, as an html fill writes it.
|
|
9
|
+
* @returns True for a void element (`img`, `input`, `br`, ...).
|
|
10
|
+
*/
|
|
11
|
+
export function isVoidElement(tag) {
|
|
12
|
+
return VOID_ELEMENTS.has(tag.toLowerCase());
|
|
13
|
+
}
|
package/dist/load.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type PreviewContext, type ResolvedSource } from './context.js';
|
|
2
|
+
import type { EnumeratedComponent, PreviewPaneWidth, Story } from './lib/types.js';
|
|
3
|
+
/** The pane widths the kit offers and what is wrong with them. */
|
|
4
|
+
export interface KitPaneWidths {
|
|
5
|
+
/** The widths: the preview block's `paneWidths`, else none (the
|
|
6
|
+
* preview's own). */
|
|
7
|
+
paneWidths?: PreviewPaneWidth[];
|
|
8
|
+
/** What makes the kit's widths unusable. */
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The pane widths a kit offers: its preview block's `paneWidths`, which a
|
|
13
|
+
* kit can derive from its own breakpoints.
|
|
14
|
+
*
|
|
15
|
+
* @param context - The kit's preview context.
|
|
16
|
+
* @returns The widths and the errors.
|
|
17
|
+
*/
|
|
18
|
+
export declare function kitPaneWidths(context: PreviewContext): KitPaneWidths;
|
|
19
|
+
/** One component with its source and story file. */
|
|
20
|
+
export interface LoadedComponent {
|
|
21
|
+
component: EnumeratedComponent;
|
|
22
|
+
story: Story | undefined;
|
|
23
|
+
source: ResolvedSource;
|
|
24
|
+
}
|
|
25
|
+
export interface LoadedKit {
|
|
26
|
+
/** Every source's components, each source sorted by name, sources in order. */
|
|
27
|
+
components: LoadedComponent[];
|
|
28
|
+
/**
|
|
29
|
+
* Each compound part, by name, with the root that lists it: in its
|
|
30
|
+
* template's `parts`, or, for a root with no template, in the kit
|
|
31
|
+
* configuration's. A listed part is not standalone, whatever its story
|
|
32
|
+
* file says.
|
|
33
|
+
*/
|
|
34
|
+
parts: Map<string, string>;
|
|
35
|
+
}
|
|
36
|
+
/** Loads every source's components and story files. */
|
|
37
|
+
export declare function loadKit(context: PreviewContext): Promise<LoadedKit>;
|
package/dist/load.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Load what the generator and the check both read from a kit: every source's
|
|
2
|
+
// components (globbed templates plus resolved manual specs),
|
|
3
|
+
// each paired with its story file. One loader, so the check sees exactly what
|
|
4
|
+
// generation renders.
|
|
5
|
+
import { enumerateComponents } from './enumerate.js';
|
|
6
|
+
import { resolveManualComponent } from './props-from.js';
|
|
7
|
+
import { loadStory } from './story.js';
|
|
8
|
+
import { paneWidthsProblem, } from './context.js';
|
|
9
|
+
/**
|
|
10
|
+
* The pane widths a kit offers: its preview block's `paneWidths`, which a
|
|
11
|
+
* kit can derive from its own breakpoints.
|
|
12
|
+
*
|
|
13
|
+
* @param context - The kit's preview context.
|
|
14
|
+
* @returns The widths and the errors.
|
|
15
|
+
*/
|
|
16
|
+
export function kitPaneWidths(context) {
|
|
17
|
+
const paneWidths = context.configuration.paneWidths;
|
|
18
|
+
if (paneWidths === undefined)
|
|
19
|
+
return { errors: [] };
|
|
20
|
+
const problem = paneWidthsProblem(paneWidths, 'preview.paneWidths');
|
|
21
|
+
return problem ? { errors: [problem] } : { paneWidths, errors: [] };
|
|
22
|
+
}
|
|
23
|
+
/** Loads every source's components and story files. */
|
|
24
|
+
export async function loadKit(context) {
|
|
25
|
+
const { sources, importModule } = context;
|
|
26
|
+
// Every source's templates first, so a manual component's `propsFrom` can
|
|
27
|
+
// build on a template from any source - its own source's first.
|
|
28
|
+
const templatesBySource = new Map();
|
|
29
|
+
for (const source of sources) {
|
|
30
|
+
templatesBySource.set(source, source.templatesDirectoryAbsolute
|
|
31
|
+
? await enumerateComponents(source.templatesDirectoryAbsolute, importModule)
|
|
32
|
+
: []);
|
|
33
|
+
}
|
|
34
|
+
// A compound's root names its parts, in its template or, with no template,
|
|
35
|
+
// in the kit configuration; none is standalone, as a story file's
|
|
36
|
+
// `standalone: false` also marks one.
|
|
37
|
+
const parts = new Map();
|
|
38
|
+
for (const [root, rootParts] of Object.entries(context.parts ?? {})) {
|
|
39
|
+
for (const part of rootParts)
|
|
40
|
+
parts.set(part, root);
|
|
41
|
+
}
|
|
42
|
+
for (const templates of templatesBySource.values()) {
|
|
43
|
+
for (const { name, template } of templates) {
|
|
44
|
+
if (template && !Array.isArray(template)) {
|
|
45
|
+
for (const part of template.parts ?? [])
|
|
46
|
+
parts.set(part, name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const components = [];
|
|
51
|
+
for (const source of sources) {
|
|
52
|
+
const templates = templatesBySource.get(source) ?? [];
|
|
53
|
+
const visibleTemplates = [
|
|
54
|
+
...templates,
|
|
55
|
+
...sources
|
|
56
|
+
.filter((other) => other !== source)
|
|
57
|
+
.flatMap((other) => templatesBySource.get(other) ?? []),
|
|
58
|
+
];
|
|
59
|
+
const manual = source.manualModuleAbsolute
|
|
60
|
+
? (await importModule(source.manualModuleAbsolute))
|
|
61
|
+
: undefined;
|
|
62
|
+
if (source.manualModuleAbsolute && typeof manual?.default !== 'function') {
|
|
63
|
+
throw new Error(`[preview] ${source.manualModule} must default-export a () => ManualComponentSpecification[]`);
|
|
64
|
+
}
|
|
65
|
+
const sourceComponents = [
|
|
66
|
+
...templates,
|
|
67
|
+
...(manual?.default
|
|
68
|
+
? manual
|
|
69
|
+
.default()
|
|
70
|
+
.map((specification) => resolveManualComponent(specification, context.kitRoot, visibleTemplates))
|
|
71
|
+
: []),
|
|
72
|
+
].sort((a, b) => a.name.localeCompare(b.name));
|
|
73
|
+
for (const listed of sourceComponents) {
|
|
74
|
+
const component = parts.has(listed.name)
|
|
75
|
+
? { ...listed, standalone: false }
|
|
76
|
+
: listed;
|
|
77
|
+
const story = await loadStory(component.name, source.storiesDirectoryAbsolute, importModule);
|
|
78
|
+
components.push({ component, story, source });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { components, parts };
|
|
82
|
+
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PropTypeSpecification, EnumeratedComponent, JsonValue, Story, PreviewBinding, ResolvedStory, PreviewControlHints, PreviewLabels, Target } from './lib/types.js';
|
|
2
|
+
/** The category a component without a story file `category` sits in. */
|
|
3
|
+
export declare const DEFAULT_CATEGORY = "Components";
|
|
4
|
+
/**
|
|
5
|
+
* The two-way props a component's template declares, in prop order: each
|
|
6
|
+
* `bindable` object's prop, copy form or relayed, the props it names, and
|
|
7
|
+
* whether a Vue consumer binds it with a bare `v-model`. A component written
|
|
8
|
+
* by hand declares none.
|
|
9
|
+
*
|
|
10
|
+
* @param component - The enumerated component.
|
|
11
|
+
* @returns The bindings.
|
|
12
|
+
*/
|
|
13
|
+
export declare function bindingsOf(component: EnumeratedComponent): PreviewBinding[];
|
|
14
|
+
/**
|
|
15
|
+
* The values a prop's scenarios should cover: each select option and each
|
|
16
|
+
* boolean value other than the prop's default (a select's `false` too only
|
|
17
|
+
* when the prop has one), for every included select and
|
|
18
|
+
* boolean control. Shared by scenario generation and the check's coverage.
|
|
19
|
+
*/
|
|
20
|
+
export declare function scenarioValues(propTypes: readonly PropTypeSpecification[], defaultProps: Record<string, JsonValue>): Array<{
|
|
21
|
+
prop: string;
|
|
22
|
+
value: string | boolean;
|
|
23
|
+
}>;
|
|
24
|
+
/** Whether a story file's `generate` lets this prop generate scenarios. */
|
|
25
|
+
export declare function generatesProp(generate: Story['generate'], prop: string): boolean;
|
|
26
|
+
/** The id a generated scenario takes: `<prop>-<value>`, or the prop for a
|
|
27
|
+
* boolean turned on, unless that is the preview's own Empty scenario's id,
|
|
28
|
+
* which a boolean named `empty` takes as `empty-true`. A curated scenario
|
|
29
|
+
* with this id replaces it. */
|
|
30
|
+
export declare function generatedScenarioId(prop: string, value: string | boolean): string;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the story of one component: its controls, default props,
|
|
33
|
+
* scenarios, slot content and the targets it renders in.
|
|
34
|
+
*
|
|
35
|
+
* @param component - The enumerated component.
|
|
36
|
+
* @param story - The component's story file, when it has one.
|
|
37
|
+
* @param hints - The kit's control hints.
|
|
38
|
+
* @param labels - The kit's preview labels.
|
|
39
|
+
* @param omitPattern - Component names matching it get no story.
|
|
40
|
+
* @param kitTargets - The scripted targets the kit builds; the component's own
|
|
41
|
+
* targets are narrowed to them.
|
|
42
|
+
* @returns The resolved story.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveStory(component: EnumeratedComponent, story: Story | undefined, hints?: PreviewControlHints, labels?: PreviewLabels, omitPattern?: RegExp, kitTargets?: readonly Target[]): ResolvedStory;
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
// Build the framework-agnostic ResolvedStory for one component: merge prop-derived
|
|
2
|
+
// propTypes with the story file (controls, prop defaults, slots, scenarios, omit).
|
|
3
|
+
import { isEventType, relayedBindingsOf, twoWayBindingsOf, } from '@js-template-engine/core';
|
|
4
|
+
import { propTypeForProp, defaultPropForProp } from './prop-types.js';
|
|
5
|
+
import { isStructurallyOmitted } from './story.js';
|
|
6
|
+
import { collectSlotComponents } from './lib/slot-fill.js';
|
|
7
|
+
import { EMPTY_SCENARIO_ID } from './lib/naming.js';
|
|
8
|
+
import { isVoidElement } from './lib/void-elements.js';
|
|
9
|
+
import { ALL_TARGETS } from './lib/types.js';
|
|
10
|
+
/** The category a component without a story file `category` sits in. */
|
|
11
|
+
export const DEFAULT_CATEGORY = 'Components';
|
|
12
|
+
/**
|
|
13
|
+
* The two-way props a component's template declares, in prop order: each
|
|
14
|
+
* `bindable` object's prop, copy form or relayed, the props it names, and
|
|
15
|
+
* whether a Vue consumer binds it with a bare `v-model`. A component written
|
|
16
|
+
* by hand declares none.
|
|
17
|
+
*
|
|
18
|
+
* @param component - The enumerated component.
|
|
19
|
+
* @returns The bindings.
|
|
20
|
+
*/
|
|
21
|
+
export function bindingsOf(component) {
|
|
22
|
+
const template = component.template;
|
|
23
|
+
if (!template || Array.isArray(template) || !template.props)
|
|
24
|
+
return [];
|
|
25
|
+
const vue = template.extensions?.['vue'];
|
|
26
|
+
const vueModel = typeof vue === 'object' && vue !== null
|
|
27
|
+
? vue.model
|
|
28
|
+
: undefined;
|
|
29
|
+
const order = Object.keys(template.props);
|
|
30
|
+
return [
|
|
31
|
+
...twoWayBindingsOf(template.props).map((binding) => ({
|
|
32
|
+
prop: binding.prop,
|
|
33
|
+
...(binding.defaultProp !== undefined
|
|
34
|
+
? { defaultProp: binding.defaultProp }
|
|
35
|
+
: {}),
|
|
36
|
+
changeProp: binding.changeProp,
|
|
37
|
+
})),
|
|
38
|
+
...relayedBindingsOf(template.props).map((binding) => ({
|
|
39
|
+
prop: binding.prop,
|
|
40
|
+
changeProp: binding.changeProp,
|
|
41
|
+
})),
|
|
42
|
+
]
|
|
43
|
+
.sort((first, second) => order.indexOf(first.prop) - order.indexOf(second.prop))
|
|
44
|
+
.map(({ changeProp, ...binding }) => ({
|
|
45
|
+
...binding,
|
|
46
|
+
...(changeProp !== undefined ? { changeProp } : {}),
|
|
47
|
+
...(vueModel === binding.prop ? { vueModel: true } : {}),
|
|
48
|
+
...(template.typeParameters?.length ? { generic: true } : {}),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The values a prop's scenarios should cover: each select option and each
|
|
53
|
+
* boolean value other than the prop's default (a select's `false` too only
|
|
54
|
+
* when the prop has one), for every included select and
|
|
55
|
+
* boolean control. Shared by scenario generation and the check's coverage.
|
|
56
|
+
*/
|
|
57
|
+
export function scenarioValues(propTypes, defaultProps) {
|
|
58
|
+
const values = [];
|
|
59
|
+
for (const propType of propTypes) {
|
|
60
|
+
if (!propType.include)
|
|
61
|
+
continue;
|
|
62
|
+
const current = defaultProps[propType.name];
|
|
63
|
+
if (propType.control === 'boolean') {
|
|
64
|
+
values.push({ prop: propType.name, value: current !== true });
|
|
65
|
+
}
|
|
66
|
+
else if (propType.control === 'select') {
|
|
67
|
+
for (const option of propType.options ?? []) {
|
|
68
|
+
// As for a toggle, `false` is no departure from a prop left unset.
|
|
69
|
+
if (option !== undefined &&
|
|
70
|
+
option !== current &&
|
|
71
|
+
!(option === false && current === undefined))
|
|
72
|
+
values.push({ prop: propType.name, value: option });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return values;
|
|
77
|
+
}
|
|
78
|
+
/** Whether a story file's `generate` lets this prop generate scenarios. */
|
|
79
|
+
export function generatesProp(generate, prop) {
|
|
80
|
+
if (generate === undefined)
|
|
81
|
+
return true;
|
|
82
|
+
if (generate === false)
|
|
83
|
+
return false;
|
|
84
|
+
if (Array.isArray(generate))
|
|
85
|
+
return generate.includes(prop);
|
|
86
|
+
return !generate.exclude.includes(prop);
|
|
87
|
+
}
|
|
88
|
+
/** The keys a story file sets. */
|
|
89
|
+
const STORY_KEYS = [
|
|
90
|
+
'omit',
|
|
91
|
+
'standalone',
|
|
92
|
+
'unlisted',
|
|
93
|
+
'category',
|
|
94
|
+
'label',
|
|
95
|
+
'defaultProps',
|
|
96
|
+
'controls',
|
|
97
|
+
'fixed',
|
|
98
|
+
'generate',
|
|
99
|
+
'slots',
|
|
100
|
+
'around',
|
|
101
|
+
'scenarios',
|
|
102
|
+
'options',
|
|
103
|
+
];
|
|
104
|
+
/** What to write for a key a story file does not take, where one thing is meant. */
|
|
105
|
+
const STORY_KEY_REPLACEMENTS = new Map([
|
|
106
|
+
['partial', 'standalone: false'],
|
|
107
|
+
['description', 'description on the template'],
|
|
108
|
+
]);
|
|
109
|
+
/** The keys a curated scenario may set. */
|
|
110
|
+
const CURATED_SCENARIO_KEYS = new Set([
|
|
111
|
+
'id',
|
|
112
|
+
'label',
|
|
113
|
+
'description',
|
|
114
|
+
'props',
|
|
115
|
+
'slots',
|
|
116
|
+
'options',
|
|
117
|
+
'default',
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* The component of the first element fill, at any depth of a fill tree,
|
|
121
|
+
* that fills its default slot through `slots.default` rather than
|
|
122
|
+
* `children`, or `undefined` when none does.
|
|
123
|
+
*/
|
|
124
|
+
function elementFillSettingDefaultSlot(fill) {
|
|
125
|
+
if (!fill || typeof fill !== 'object' || Array.isArray(fill))
|
|
126
|
+
return undefined;
|
|
127
|
+
if (fill.kind === 'element') {
|
|
128
|
+
if (fill.slots && 'default' in fill.slots)
|
|
129
|
+
return fill.component;
|
|
130
|
+
for (const nested of [...Object.values(fill.slots ?? {}), fill.children]) {
|
|
131
|
+
const found = elementFillSettingDefaultSlot(nested);
|
|
132
|
+
if (found !== undefined)
|
|
133
|
+
return found;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (fill.kind === 'html') {
|
|
137
|
+
return elementFillSettingDefaultSlot(fill.children);
|
|
138
|
+
}
|
|
139
|
+
else if (fill.kind === 'fragment') {
|
|
140
|
+
for (const child of fill.children ?? []) {
|
|
141
|
+
const found = elementFillSettingDefaultSlot(child);
|
|
142
|
+
if (found !== undefined)
|
|
143
|
+
return found;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The tag of the first html fill, at any depth of a fill tree, that gives a
|
|
150
|
+
* void element (img, input, br) children, or `undefined` when none does.
|
|
151
|
+
*/
|
|
152
|
+
function voidElementWithChildren(fill) {
|
|
153
|
+
if (!fill || typeof fill !== 'object' || Array.isArray(fill))
|
|
154
|
+
return undefined;
|
|
155
|
+
if (fill.kind === 'html') {
|
|
156
|
+
if (fill.children !== undefined && isVoidElement(fill.tag))
|
|
157
|
+
return fill.tag;
|
|
158
|
+
return voidElementWithChildren(fill.children);
|
|
159
|
+
}
|
|
160
|
+
if (fill.kind === 'element') {
|
|
161
|
+
for (const nested of [...Object.values(fill.slots ?? {}), fill.children]) {
|
|
162
|
+
const found = voidElementWithChildren(nested);
|
|
163
|
+
if (found !== undefined)
|
|
164
|
+
return found;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (fill.kind === 'fragment') {
|
|
168
|
+
for (const child of fill.children ?? []) {
|
|
169
|
+
const found = voidElementWithChildren(child);
|
|
170
|
+
if (found !== undefined)
|
|
171
|
+
return found;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
/** The id a generated scenario takes: `<prop>-<value>`, or the prop for a
|
|
177
|
+
* boolean turned on, unless that is the preview's own Empty scenario's id,
|
|
178
|
+
* which a boolean named `empty` takes as `empty-true`. A curated scenario
|
|
179
|
+
* with this id replaces it. */
|
|
180
|
+
export function generatedScenarioId(prop, value) {
|
|
181
|
+
if (value === true)
|
|
182
|
+
return prop === EMPTY_SCENARIO_ID ? `${prop}-true` : prop;
|
|
183
|
+
return `${prop}-${String(value)}`;
|
|
184
|
+
}
|
|
185
|
+
/** The default label of a generated scenario: `prop: value`, or the bare prop
|
|
186
|
+
* for a boolean turned on. */
|
|
187
|
+
function defaultGeneratedLabel(prop, value) {
|
|
188
|
+
return value === true ? prop : `${prop}: ${String(value)}`;
|
|
189
|
+
}
|
|
190
|
+
/** Whether a scenario sets exactly one prop, to this value. */
|
|
191
|
+
function setsOnly(scenario, prop, value) {
|
|
192
|
+
const entries = Object.entries(scenario.props ?? {});
|
|
193
|
+
return (entries.length === 1 && entries[0][0] === prop && entries[0][1] === value);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* One scenario per value `scenarioValues` lists, narrowed by the story file's
|
|
197
|
+
* `generate`, skipping a value a curated scenario already sets alone, and any
|
|
198
|
+
* id a curated scenario already uses: that scenario replaces the generated one.
|
|
199
|
+
* Labeled by the kit's `generatedScenario`, or `prop: value` and the bare prop
|
|
200
|
+
* for a boolean turned on.
|
|
201
|
+
*/
|
|
202
|
+
function generateScenarios(propTypes, defaultProps, story, labels) {
|
|
203
|
+
const generate = story?.generate;
|
|
204
|
+
if (generate === false)
|
|
205
|
+
return [];
|
|
206
|
+
const curated = story?.scenarios ?? [];
|
|
207
|
+
const takenIds = new Set(curated.map((scenario) => scenario.id));
|
|
208
|
+
const label = labels.generatedScenario ?? defaultGeneratedLabel;
|
|
209
|
+
const scenarios = [];
|
|
210
|
+
for (const { prop, value } of scenarioValues(propTypes, defaultProps)) {
|
|
211
|
+
if (!generatesProp(generate, prop))
|
|
212
|
+
continue;
|
|
213
|
+
if (curated.some((scenario) => setsOnly(scenario, prop, value)))
|
|
214
|
+
continue;
|
|
215
|
+
const id = generatedScenarioId(prop, value);
|
|
216
|
+
if (takenIds.has(id))
|
|
217
|
+
continue;
|
|
218
|
+
takenIds.add(id);
|
|
219
|
+
scenarios.push({
|
|
220
|
+
id,
|
|
221
|
+
label: label(prop, value),
|
|
222
|
+
props: { [prop]: value },
|
|
223
|
+
generated: true,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return scenarios;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Resolve the story of one component: its controls, default props,
|
|
230
|
+
* scenarios, slot content and the targets it renders in.
|
|
231
|
+
*
|
|
232
|
+
* @param component - The enumerated component.
|
|
233
|
+
* @param story - The component's story file, when it has one.
|
|
234
|
+
* @param hints - The kit's control hints.
|
|
235
|
+
* @param labels - The kit's preview labels.
|
|
236
|
+
* @param omitPattern - Component names matching it get no story.
|
|
237
|
+
* @param kitTargets - The scripted targets the kit builds; the component's own
|
|
238
|
+
* targets are narrowed to them.
|
|
239
|
+
* @returns The resolved story.
|
|
240
|
+
*/
|
|
241
|
+
export function resolveStory(component, story, hints = {}, labels = {}, omitPattern, kitTargets = ALL_TARGETS) {
|
|
242
|
+
const omit = (story?.omit ?? false) ||
|
|
243
|
+
isStructurallyOmitted(component.name, omitPattern);
|
|
244
|
+
// A component that is not standalone (a compound's part, a shared building
|
|
245
|
+
// block) stays in the manifest for the editor but gets no story - set on the
|
|
246
|
+
// story file (templates) or the manual spec (hand-written components).
|
|
247
|
+
const standalone = (story?.standalone ?? true) && (component.standalone ?? true);
|
|
248
|
+
// An unlisted story is generated as any other, only left out of what a
|
|
249
|
+
// reader browses; a component with no story of its own has nothing to hide.
|
|
250
|
+
const unlisted = ((story?.unlisted ?? false) || (component.unlisted ?? false)) &&
|
|
251
|
+
!omit &&
|
|
252
|
+
standalone;
|
|
253
|
+
// propTypes from props, then story file control overrides (false disables).
|
|
254
|
+
const propTypes = [];
|
|
255
|
+
for (const [propName, def] of Object.entries(component.props)) {
|
|
256
|
+
let propType = propTypeForProp(component.name, propName, def, hints);
|
|
257
|
+
if (def.description)
|
|
258
|
+
propType = { ...propType, description: def.description };
|
|
259
|
+
const override = story?.controls?.[propName];
|
|
260
|
+
if (override === false)
|
|
261
|
+
propType = { ...propType, include: false };
|
|
262
|
+
else if (override)
|
|
263
|
+
propType = { ...propType, ...override };
|
|
264
|
+
// A fixed prop is always passed and never shown.
|
|
265
|
+
if (story?.fixed && propName in story.fixed)
|
|
266
|
+
propType = { ...propType, include: false };
|
|
267
|
+
propTypes.push({
|
|
268
|
+
...propType,
|
|
269
|
+
declaredType: def.type,
|
|
270
|
+
required: def.required === true,
|
|
271
|
+
...(def.default !== undefined ? { declaredDefault: def.default } : {}),
|
|
272
|
+
...(def.documentedDefault !== undefined
|
|
273
|
+
? { documentedDefault: def.documentedDefault }
|
|
274
|
+
: {}),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// default props: template defaults + size config, then story file defaultProps.
|
|
278
|
+
const defaultProps = {};
|
|
279
|
+
// The values the preview starts a prop from that a consumer never writes:
|
|
280
|
+
// a hint's, for a prop the component gives no default and the story file
|
|
281
|
+
// leaves alone.
|
|
282
|
+
const previewDefaults = {};
|
|
283
|
+
for (const [propName, def] of Object.entries(component.props)) {
|
|
284
|
+
const value = defaultPropForProp(component.name, propName, def, hints);
|
|
285
|
+
if (value !== undefined)
|
|
286
|
+
defaultProps[propName] = value;
|
|
287
|
+
if (value !== undefined &&
|
|
288
|
+
def.default === undefined &&
|
|
289
|
+
!(propName in (story?.defaultProps ?? {})) &&
|
|
290
|
+
!(propName in (story?.fixed ?? {})))
|
|
291
|
+
previewDefaults[propName] = value;
|
|
292
|
+
}
|
|
293
|
+
// A component with a default slot gets visible placeholder text - but only
|
|
294
|
+
// when the story file declares no slot plan at all. A story file that curates any
|
|
295
|
+
// slot (a named fill, an explicit default) owns the slot story: injecting
|
|
296
|
+
// children beside a named fill fabricates a mount no consumer writes.
|
|
297
|
+
// `slots: {}` forces a genuinely slotless mount. A default slot that
|
|
298
|
+
// exposes values gets none either: its content is a function of those
|
|
299
|
+
// values (React calls `children(scope)`), so text there is a mount no
|
|
300
|
+
// consumer writes, and the slot's fallback renders instead.
|
|
301
|
+
if (component.hasDefaultSlot &&
|
|
302
|
+
!component.defaultSlotScoped &&
|
|
303
|
+
!story?.slots &&
|
|
304
|
+
defaultProps.children === undefined) {
|
|
305
|
+
defaultProps.children = component.name;
|
|
306
|
+
// The preview's own, which a consumer's source leaves out, unless the
|
|
307
|
+
// story file gives the slot text of its own.
|
|
308
|
+
if (!('children' in (story?.defaultProps ?? {})) &&
|
|
309
|
+
!('children' in (story?.fixed ?? {})))
|
|
310
|
+
previewDefaults.children = component.name;
|
|
311
|
+
}
|
|
312
|
+
Object.assign(defaultProps, story?.defaultProps ?? {}, story?.fixed ?? {});
|
|
313
|
+
// A named slot spelled flat (`slots: { header: ... }` instead of
|
|
314
|
+
// `slots: { named: { header: ... } }`) would silently never render - the
|
|
315
|
+
// emitters read only `default` and `named`. Fail loudly instead. A
|
|
316
|
+
// scenario's own slots are a plan of the same shape.
|
|
317
|
+
const slotPlans = [
|
|
318
|
+
['', story?.slots],
|
|
319
|
+
...(story?.scenarios ?? [])
|
|
320
|
+
.filter((scenario) => scenario.generated !== true)
|
|
321
|
+
.map((scenario) => [
|
|
322
|
+
`scenario '${scenario.id}': `,
|
|
323
|
+
scenario.slots,
|
|
324
|
+
]),
|
|
325
|
+
];
|
|
326
|
+
for (const [where, plan] of slotPlans) {
|
|
327
|
+
if (plan === undefined)
|
|
328
|
+
continue;
|
|
329
|
+
if (typeof plan !== 'object' || plan === null || Array.isArray(plan)) {
|
|
330
|
+
throw new Error(`${component.name}: ${where}slots must be a slot plan, { default, named }`);
|
|
331
|
+
}
|
|
332
|
+
const unknownSlotKeys = Object.keys(plan).filter((key) => key !== 'default' && key !== 'named');
|
|
333
|
+
if (unknownSlotKeys.length > 0) {
|
|
334
|
+
throw new Error(`${component.name}: ${where}unknown slot-plan key(s) ${unknownSlotKeys.join(', ')} - ` +
|
|
335
|
+
`named slots go under "named": { slots: { named: { ... } } }.`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const planFills = slotPlans.flatMap(([, plan]) => [
|
|
339
|
+
plan?.default,
|
|
340
|
+
...Object.values(plan?.named ?? {}),
|
|
341
|
+
]);
|
|
342
|
+
// `around` holds the content before and after the component, and a scoped
|
|
343
|
+
// fill has no slot there to give it values.
|
|
344
|
+
const around = story?.around;
|
|
345
|
+
if (around !== undefined) {
|
|
346
|
+
if (typeof around !== 'object' || around === null || Array.isArray(around))
|
|
347
|
+
throw new Error(`${component.name}: around must be { before, after }, each a fill`);
|
|
348
|
+
const unknownAroundKeys = Object.keys(around).filter((key) => key !== 'before' && key !== 'after');
|
|
349
|
+
if (unknownAroundKeys.length > 0)
|
|
350
|
+
throw new Error(`${component.name}: unknown around key(s) ${unknownAroundKeys.join(', ')} - around takes before and after`);
|
|
351
|
+
for (const [where, fill] of Object.entries(around)) {
|
|
352
|
+
if (fill?.kind === 'scoped')
|
|
353
|
+
throw new Error(`${component.name}: around.${where} is a scoped fill, which takes values from a slot; beside the component there is none`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const aroundFills = [story?.around?.before, story?.around?.after];
|
|
357
|
+
// The composed component's default slot takes an element fill's
|
|
358
|
+
// `children`; `slots` names its named slots only. A `default` there would
|
|
359
|
+
// otherwise reach the emitters as a slot named `default`, which Svelte
|
|
360
|
+
// refuses as a reserved word.
|
|
361
|
+
for (const fill of [...planFills, ...aroundFills]) {
|
|
362
|
+
const filled = elementFillSettingDefaultSlot(fill);
|
|
363
|
+
if (filled !== undefined) {
|
|
364
|
+
throw new Error(`${component.name}: the element fill of '${filled}' sets slots.default - its default slot takes children`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// A void element renders self-closed, so children on one would be dropped
|
|
368
|
+
// in one framework and refused by another's compiler.
|
|
369
|
+
for (const fill of [...planFills, ...aroundFills]) {
|
|
370
|
+
const tag = voidElementWithChildren(fill);
|
|
371
|
+
if (tag !== undefined) {
|
|
372
|
+
throw new Error(`${component.name}: the html fill <${tag}> has children - ${tag} is a void element and takes none`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// A story file sets only what a story holds; a key the preview does not
|
|
376
|
+
// read would otherwise change nothing without a word.
|
|
377
|
+
const unknownStoryKey = Object.keys(story ?? {}).find((key) => !STORY_KEYS.includes(key));
|
|
378
|
+
if (unknownStoryKey !== undefined) {
|
|
379
|
+
const replacement = STORY_KEY_REPLACEMENTS.get(unknownStoryKey);
|
|
380
|
+
throw new Error(`${component.name}: story sets unknown key '${unknownStoryKey}' - ${replacement === undefined
|
|
381
|
+
? `a story sets ${STORY_KEYS.join(', ')}`
|
|
382
|
+
: `use ${replacement}`}`);
|
|
383
|
+
}
|
|
384
|
+
// A curated scenario sets only what a scenario holds. The scenarios the
|
|
385
|
+
// preview generates itself reach the model beside them, marked
|
|
386
|
+
// `generated`, and are not the story file's to check.
|
|
387
|
+
for (const scenario of story?.scenarios ?? []) {
|
|
388
|
+
if (scenario.generated === true)
|
|
389
|
+
continue;
|
|
390
|
+
if (scenario.id === EMPTY_SCENARIO_ID)
|
|
391
|
+
throw new Error(`${component.name}: scenario id '${EMPTY_SCENARIO_ID}' is the preview's own Empty scenario's; give the scenario another id`);
|
|
392
|
+
const unknownKey = Object.keys(scenario).find((key) => !CURATED_SCENARIO_KEYS.has(key));
|
|
393
|
+
if (unknownKey !== undefined) {
|
|
394
|
+
throw new Error(`${component.name}: scenario '${scenario.id}' sets unknown key '${unknownKey}' - a scenario sets ${[...CURATED_SCENARIO_KEYS].join(', ')}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// A scenario's description is text for a shell to show, and its options
|
|
398
|
+
// are option values by id; anything else is a story file mistake that would
|
|
399
|
+
// otherwise reach the catalog unnoticed. Whether each names one of the
|
|
400
|
+
// kit's options is the check's to say, which reads the kit's options.
|
|
401
|
+
for (const scenario of story?.scenarios ?? []) {
|
|
402
|
+
if (scenario.description !== undefined &&
|
|
403
|
+
typeof scenario.description !== 'string') {
|
|
404
|
+
throw new Error(`${component.name}: scenario '${scenario.id}' has a description that is not a string.`);
|
|
405
|
+
}
|
|
406
|
+
const options = scenario.options;
|
|
407
|
+
if (options !== undefined &&
|
|
408
|
+
(typeof options !== 'object' ||
|
|
409
|
+
options === null ||
|
|
410
|
+
Array.isArray(options) ||
|
|
411
|
+
Object.values(options).some((value) => typeof value !== 'string' && typeof value !== 'boolean'))) {
|
|
412
|
+
throw new Error(`${component.name}: scenario '${scenario.id}' has options that are not option values by id, each a string or a boolean.`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// slot components to import
|
|
416
|
+
const slotComponentSet = new Set();
|
|
417
|
+
try {
|
|
418
|
+
// A scenario's own content is imported with the story's: one module
|
|
419
|
+
// per target holds them all.
|
|
420
|
+
for (const fill of planFills)
|
|
421
|
+
collectSlotComponents(fill, slotComponentSet);
|
|
422
|
+
for (const fill of aroundFills)
|
|
423
|
+
collectSlotComponents(fill, slotComponentSet);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
// Name the component so a bad story file is findable without a bisect.
|
|
427
|
+
throw new Error(`${component.name}: ${error.message}`, {
|
|
428
|
+
cause: error,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
name: component.name,
|
|
433
|
+
...(component.description !== undefined
|
|
434
|
+
? { description: component.description }
|
|
435
|
+
: {}),
|
|
436
|
+
title: `${story?.category ?? labels.category ?? DEFAULT_CATEGORY}/${story?.label ?? component.name}`,
|
|
437
|
+
omit,
|
|
438
|
+
standalone,
|
|
439
|
+
unlisted,
|
|
440
|
+
propTypes,
|
|
441
|
+
events: propTypes
|
|
442
|
+
.filter((propType) => isEventType(propType.declaredType))
|
|
443
|
+
.map((propType) => propType.name),
|
|
444
|
+
bindings: bindingsOf(component),
|
|
445
|
+
defaultProps,
|
|
446
|
+
...(Object.keys(previewDefaults).length > 0 ? { previewDefaults } : {}),
|
|
447
|
+
slots: story?.slots,
|
|
448
|
+
...(story?.around ? { around: story.around } : {}),
|
|
449
|
+
scenarios: [
|
|
450
|
+
...(story?.scenarios ?? []),
|
|
451
|
+
...generateScenarios(propTypes, defaultProps, story, labels),
|
|
452
|
+
],
|
|
453
|
+
slotComponents: [...slotComponentSet],
|
|
454
|
+
contentSlots: {
|
|
455
|
+
default: component.hasDefaultSlot,
|
|
456
|
+
named: component.slotNames,
|
|
457
|
+
...(component.scopeRequiredSlots?.length
|
|
458
|
+
? { scopeRequired: component.scopeRequiredSlots }
|
|
459
|
+
: {}),
|
|
460
|
+
},
|
|
461
|
+
options: story?.options,
|
|
462
|
+
targets: (component.targets ?? ALL_TARGETS).filter((target) => kitTargets.includes(target)),
|
|
463
|
+
};
|
|
464
|
+
}
|