inkbridge 0.1.0-beta.2 → 0.1.0-beta.21
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/README.md +108 -25
- package/bin/inkbridge.mjs +354 -83
- package/code.js +40 -11802
- package/manifest.json +1 -0
- package/package.json +74 -23
- package/scanner/adapter-utils-regression.ts +159 -0
- package/scanner/aspect-percent-position-regression.ts +237 -0
- package/scanner/aspect-ratio-regression.ts +90 -0
- package/scanner/blob-placement-regression.ts +2 -2
- package/scanner/block-cache-regression.ts +195 -0
- package/scanner/bundle-size-regression.ts +50 -0
- package/scanner/child-sizing-matrix-regression.ts +303 -0
- package/scanner/cli.ts +342 -13
- package/scanner/component-scanner.ts +2108 -174
- package/scanner/component-sections-regression.ts +198 -0
- package/scanner/compound-classes-lookup-regression.ts +163 -0
- package/scanner/css-token-reader-regression.ts +7 -6
- package/scanner/css-token-reader.ts +152 -31
- package/scanner/cva-jsx-child-fallback-regression.ts +98 -0
- package/scanner/cva-master-icon-regression.ts +315 -0
- package/scanner/data-attr-prop-alias-regression.ts +129 -0
- package/scanner/explicit-size-root-regression.ts +102 -0
- package/scanner/font-family-extract-regression.ts +113 -0
- package/scanner/font-style-resolver-regression.ts +1 -1
- package/scanner/framework-adapter-shadcn-regression.ts +480 -0
- package/scanner/full-width-matrix-regression.ts +338 -0
- package/scanner/grid-cols-extraction-regression.ts +110 -0
- package/scanner/image-src-collector-regression.ts +204 -0
- package/scanner/inline-flex-regression.ts +235 -0
- package/scanner/input-range-regression.ts +217 -0
- package/scanner/instance-rendering-regression.ts +224 -0
- package/scanner/jsx-prop-unresolved-regression.ts +178 -0
- package/scanner/jsx-text-regression.ts +178 -0
- package/scanner/layout-alignment-regression.ts +108 -0
- package/scanner/layout-flex-regression.ts +90 -0
- package/scanner/layout-mode-regression.ts +71 -0
- package/scanner/layout-sizing-regression.ts +227 -0
- package/scanner/layout-spacing-regression.ts +135 -0
- package/scanner/local-const-className-regression.ts +331 -0
- package/scanner/percent-position-regression.ts +105 -0
- package/scanner/provider-cascade-regression.ts +224 -0
- package/scanner/provider-flatten-regression.ts +235 -0
- package/scanner/radial-gradient-regression.ts +1 -1
- package/scanner/render-prop-parser-regression.ts +161 -0
- package/scanner/ring-utility-regression.ts +153 -0
- package/scanner/sandbox-spread-regression.ts +125 -0
- package/scanner/selection-pressed-regression.ts +241 -0
- package/scanner/size-full-normalization-regression.ts +127 -0
- package/scanner/state-classification-regression.ts +175 -0
- package/scanner/story-diagnostics-regression.ts +216 -0
- package/scanner/story-dimensioning-regression.ts +298 -0
- package/scanner/story-render-strategy-regression.ts +205 -0
- package/scanner/stretch-to-parent-width-regression.ts +147 -0
- package/scanner/svg-fill-parent-regression.ts +98 -0
- package/scanner/svg-group-inheritance-regression.ts +166 -0
- package/scanner/svg-marker-inline-regression.ts +211 -0
- package/scanner/svg-marker-regression.ts +116 -0
- package/scanner/tailwind-parser.ts +46 -4
- package/scanner/text-resize-matrix-regression.ts +173 -0
- package/scanner/transform-math-regression.ts +1 -1
- package/scanner/types.ts +26 -2
- package/src/cache/frame-cache.ts +150 -0
- package/src/cache/index.ts +2 -0
- package/src/{component-defs.ts → components/component-defs.ts} +25 -10
- package/src/{component-gen.ts → components/component-gen.ts} +43 -116
- package/src/components/component-instance.ts +386 -0
- package/src/components/component-library.ts +44 -0
- package/src/components/component-lookup.ts +161 -0
- package/src/components/index.ts +7 -0
- package/src/components/scanner-types.ts +39 -0
- package/src/components/symbol-instance-policy.ts +312 -0
- package/src/design-system/block-cache.ts +130 -0
- package/src/design-system/component-sections.ts +107 -0
- package/src/design-system/cva-inference.ts +187 -0
- package/src/design-system/cva-master.ts +427 -0
- package/src/design-system/cva-utils.ts +29 -0
- package/src/design-system/design-system.ts +334 -0
- package/src/design-system/frame-stabilizers.ts +191 -0
- package/src/design-system/frame-utils.ts +46 -0
- package/src/design-system/generated-node.ts +84 -0
- package/src/design-system/icon-rendering.ts +229 -0
- package/src/design-system/index.ts +13 -0
- package/src/design-system/instance-rendering.ts +307 -0
- package/src/design-system/master-shared.ts +133 -0
- package/src/design-system/node-helpers.ts +237 -0
- package/src/design-system/node-variants.ts +196 -0
- package/src/design-system/non-cva-master.ts +104 -0
- package/src/design-system/portal-handling.ts +138 -0
- package/src/design-system/preview-builder.ts +738 -0
- package/src/{render-context.ts → design-system/render-context.ts} +32 -6
- package/src/design-system/render-prop-parser.ts +50 -0
- package/src/design-system/responsive-resolver.ts +180 -0
- package/src/design-system/selectable-state.ts +157 -0
- package/src/design-system/state-master.ts +267 -0
- package/src/design-system/state-utils.ts +15 -0
- package/src/design-system/story-builder-context.ts +40 -0
- package/src/design-system/story-builder.ts +1322 -0
- package/src/design-system/story-diagnostics.ts +80 -0
- package/src/design-system/story-dimensioning.ts +272 -0
- package/src/design-system/story-frames.ts +400 -0
- package/src/design-system/story-instance.ts +333 -0
- package/src/{story-layout.ts → design-system/story-layout.ts} +2 -2
- package/src/design-system/story-render-strategy.ts +150 -0
- package/src/design-system/story-tree-search.ts +110 -0
- package/src/design-system/symbol-fallback.ts +89 -0
- package/src/design-system/symbol-source.ts +172 -0
- package/src/design-system/table-helpers.ts +56 -0
- package/src/design-system/tag-predicates.ts +99 -0
- package/src/design-system/theme-context.ts +52 -0
- package/src/design-system/typography.ts +100 -0
- package/src/design-system/ui-builder.ts +2676 -0
- package/src/{clip-path-decorative.ts → effects/clip-path-decorative.ts} +11 -11
- package/src/effects/icon-builder.ts +1074 -0
- package/src/effects/index.ts +5 -0
- package/src/effects/portal-panel.ts +369 -0
- package/src/{radial-gradient.ts → effects/radial-gradient.ts} +1 -1
- package/src/framework-adapters/index.ts +47 -0
- package/src/framework-adapters/shadcn.ts +541 -0
- package/src/{github.ts → github/github.ts} +46 -21
- package/src/github/index.ts +1 -0
- package/src/layout/deferred-layout.ts +1556 -0
- package/src/layout/index.ts +24 -0
- package/src/layout/layout-parser.ts +375 -0
- package/src/{layout-utils.ts → layout/layout-utils.ts} +23 -17
- package/src/layout/parser/alignment.ts +54 -0
- package/src/layout/parser/flex.ts +59 -0
- package/src/layout/parser/index.ts +65 -0
- package/src/layout/parser/ir.ts +80 -0
- package/src/layout/parser/layout-mode.ts +57 -0
- package/src/layout/parser/sizing.ts +241 -0
- package/src/layout/parser/spacing-scale.ts +78 -0
- package/src/layout/parser/spacing.ts +134 -0
- package/src/layout/ring-utils.ts +120 -0
- package/src/layout/size-utils.ts +143 -0
- package/src/layout/text-resize-decision.ts +51 -0
- package/src/{width-solver.ts → layout/width-solver.ts} +168 -37
- package/src/main.ts +444 -162
- package/src/{config.ts → plugin/config.ts} +12 -12
- package/src/{dev-server.ts → plugin/dev-server.ts} +3 -3
- package/src/plugin/image-src-collector.ts +52 -0
- package/src/plugin/index.ts +3 -0
- package/src/plugin/packs/index.ts +2 -0
- package/src/{pack-provider.ts → plugin/packs/pack-provider.ts} +12 -12
- package/src/{packs.ts → plugin/packs/packs.ts} +22 -17
- package/src/render-engine-version.ts +2 -0
- package/src/tailwind/adapter-utils.ts +137 -0
- package/src/{class-utils.ts → tailwind/class-utils.ts} +33 -6
- package/src/tailwind/index.ts +8 -0
- package/src/tailwind/jsx-utils.ts +319 -0
- package/src/{node-ir.ts → tailwind/node-ir.ts} +208 -19
- package/src/{responsive-analyzer.ts → tailwind/responsive-analyzer.ts} +32 -2
- package/src/{state-analyzer.ts → tailwind/state-analyzer.ts} +71 -5
- package/src/{tailwind.ts → tailwind/tailwind.ts} +423 -674
- package/src/{utility-resolver.ts → tailwind/utility-resolver.ts} +27 -6
- package/src/{font-style-resolver.ts → text/font-style-resolver.ts} +0 -2
- package/src/text/index.ts +4 -0
- package/src/{inline-text.ts → text/inline-text.ts} +13 -13
- package/src/{text-builder.ts → text/text-builder.ts} +24 -7
- package/src/{text-line.ts → text/text-line.ts} +2 -2
- package/src/{change-detection.ts → tokens/change-detection.ts} +12 -12
- package/src/{color-resolver.ts → tokens/color-resolver.ts} +1 -6
- package/src/{colors.ts → tokens/colors.ts} +13 -6
- package/src/tokens/index.ts +6 -0
- package/src/{token-source.ts → tokens/token-source.ts} +4 -1
- package/src/{tokens.ts → tokens/tokens.ts} +116 -20
- package/src/{variables.ts → tokens/variables.ts} +447 -102
- package/templates/patch-tokens-route.ts +25 -6
- package/templates/scan-components-route.ts +26 -5
- package/ui.html +485 -37
- package/src/component-lookup.ts +0 -82
- package/src/design-system.ts +0 -59
- package/src/icon-builder.ts +0 -607
- package/src/layout-parser.ts +0 -667
- package/src/story-builder.ts +0 -1706
- package/src/ui-builder.ts +0 -1996
- /package/src/{image-cache.ts → cache/image-cache.ts} +0 -0
- /package/src/{blob-placement.ts → effects/blob-placement.ts} +0 -0
- /package/src/{transform-math.ts → tailwind/transform-math.ts} +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ComponentDef, ComponentStory, LayoutInfo } from '../components';
|
|
2
|
+
import { RENDER_ENGINE_VERSION } from '../render-engine-version';
|
|
3
|
+
import { hasNodeChildren } from './frame-utils';
|
|
4
|
+
import { normalizeLayoutClasses } from './story-layout';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Per-story inspection metadata written to the Story Layout frame as Figma
|
|
8
|
+
* plugin data. Surfaced by the `Debug Selection` plugin command — read from
|
|
9
|
+
* `inkbridge:story-scan` (scanner output snapshot) and `inkbridge:story-render`
|
|
10
|
+
* (render-time decisions) so you can answer "why did this story render the
|
|
11
|
+
* way it did?" without reattaching a debugger.
|
|
12
|
+
*
|
|
13
|
+
* Pure side-effect: writes string-encoded summaries; tolerates `setPluginData`
|
|
14
|
+
* unavailability or throws (the diagnostics path must never crash a render).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function getStoryTreeRootTag(story: ComponentStory): string {
|
|
18
|
+
const root = story && story.jsxTree;
|
|
19
|
+
if (!root || root.type !== 'element') return '';
|
|
20
|
+
return String(root.tagName || '');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getStoryTreeRootChildCount(story: ComponentStory): number {
|
|
24
|
+
const root = story && story.jsxTree;
|
|
25
|
+
if (!root || !Array.isArray(root.children)) return 0;
|
|
26
|
+
return root.children.length;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function summarizeLayoutClasses(classes: string[] | undefined, maxLength: number): string {
|
|
30
|
+
const joined = Array.isArray(classes) ? classes.join(' ') : '';
|
|
31
|
+
if (joined.length <= maxLength) return joined;
|
|
32
|
+
return joined.slice(0, maxLength);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function setStoryRenderDiagnostics(
|
|
36
|
+
layout: LayoutInfo,
|
|
37
|
+
def: ComponentDef,
|
|
38
|
+
story: ComponentStory,
|
|
39
|
+
mode: string,
|
|
40
|
+
added: number,
|
|
41
|
+
usedStoryTree: boolean
|
|
42
|
+
): void {
|
|
43
|
+
if (!layout || typeof layout.setPluginData !== 'function') return;
|
|
44
|
+
try {
|
|
45
|
+
const storyName = String(story && story.name ? story.name : '');
|
|
46
|
+
const defName = String(def && def.name ? def.name : '');
|
|
47
|
+
const instances = Array.isArray(story && story.instances) ? story.instances.length : 0;
|
|
48
|
+
const layoutClasses = normalizeLayoutClasses(story && story.layoutClasses);
|
|
49
|
+
const scanSummary = [
|
|
50
|
+
'jsx=' + (story && story.jsxTree ? '1' : '0'),
|
|
51
|
+
'root=' + getStoryTreeRootTag(story),
|
|
52
|
+
'rootChildren=' + String(getStoryTreeRootChildCount(story)),
|
|
53
|
+
'instances=' + String(instances),
|
|
54
|
+
'layout=' + summarizeLayoutClasses(layoutClasses, 120),
|
|
55
|
+
].join(';');
|
|
56
|
+
const renderSummary = [
|
|
57
|
+
'mode=' + mode,
|
|
58
|
+
'added=' + String(added),
|
|
59
|
+
'treeSelected=' + (usedStoryTree ? '1' : '0'),
|
|
60
|
+
'children=' + (hasNodeChildren(layout) ? String(layout.children.length) : '0'),
|
|
61
|
+
'engine=' + RENDER_ENGINE_VERSION,
|
|
62
|
+
].join(';');
|
|
63
|
+
|
|
64
|
+
layout.setPluginData('inkbridge:story-name', storyName);
|
|
65
|
+
layout.setPluginData('inkbridge:story-def', defName);
|
|
66
|
+
layout.setPluginData('inkbridge:story-scan', scanSummary);
|
|
67
|
+
layout.setPluginData('inkbridge:story-render', renderSummary);
|
|
68
|
+
} catch (_err) {
|
|
69
|
+
// Ignore diagnostics write failures.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Exported for the regression fixture so the pure helpers can be tested in
|
|
74
|
+
// isolation. Not used outside this module's `setStoryRenderDiagnostics` in
|
|
75
|
+
// runtime code; the test boundary is the only consumer.
|
|
76
|
+
export const _internal = {
|
|
77
|
+
getStoryTreeRootTag,
|
|
78
|
+
getStoryTreeRootChildCount,
|
|
79
|
+
summarizeLayoutClasses,
|
|
80
|
+
};
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractLeadingContainerMaxWidthFromTree,
|
|
3
|
+
splitClassName,
|
|
4
|
+
treeHasFullWidth,
|
|
5
|
+
type JsxElement,
|
|
6
|
+
type JsxNode,
|
|
7
|
+
} from '../tailwind';
|
|
8
|
+
import type { ComponentStory, LayoutInfo } from '../components';
|
|
9
|
+
import {
|
|
10
|
+
extractFixedWidth,
|
|
11
|
+
extractGridBreakpointWidth,
|
|
12
|
+
extractGridColumns,
|
|
13
|
+
extractMaxWidth,
|
|
14
|
+
} from '../layout';
|
|
15
|
+
import {
|
|
16
|
+
defaultGridWidth,
|
|
17
|
+
extractGridBreakpointWidthFromTree,
|
|
18
|
+
extractGridColumnsFromTree,
|
|
19
|
+
} from './story-layout';
|
|
20
|
+
import { resolvePortalAwareStoryTree } from './portal-handling';
|
|
21
|
+
import type { StoryBuilderContext } from './story-builder-context';
|
|
22
|
+
|
|
23
|
+
// Components whose root is `md:hidden` (or larger-breakpoint-hidden) are
|
|
24
|
+
// mobile-only designs. At the default ~900px width their flex/justify-between
|
|
25
|
+
// layout spreads content across an empty frame. Render such components at a
|
|
26
|
+
// mobile width so the design is legible.
|
|
27
|
+
export const MOBILE_ONLY_DEFAULT_WIDTH = 360;
|
|
28
|
+
|
|
29
|
+
// Viewport-anchored stories (Sheet drawers, full-page shells, dialogs) use
|
|
30
|
+
// `h-full` / `h-screen` to anchor against a viewport that doesn't exist in
|
|
31
|
+
// Figma auto-layout. Without a synthetic height the container hugs content
|
|
32
|
+
// and descendant `flex-1` siblings collapse to 0. Detect the pattern at the
|
|
33
|
+
// story root and pin a reasonable design height so the vertical grow cascade
|
|
34
|
+
// lights up. Two preset heights cover the common shapes.
|
|
35
|
+
export const VIEWPORT_HEIGHTS = {
|
|
36
|
+
DRAWER: 700,
|
|
37
|
+
FULL_PAGE: 900,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Walks the leading single-child spine of a JSX tree and collects classes so a
|
|
42
|
+
* decorator-wrapped component (common in Storybook stories) still exposes its
|
|
43
|
+
* intended root behaviour. Stops after 4 levels or at the first multi-child
|
|
44
|
+
* fan-out — those branches are the body, not the spine.
|
|
45
|
+
*/
|
|
46
|
+
export function collectLeadingSpineClasses(node: JsxNode, out: string[]): void {
|
|
47
|
+
let current: JsxNode | undefined = node;
|
|
48
|
+
let depth = 0;
|
|
49
|
+
while (current && current.type === 'element' && depth < 4) {
|
|
50
|
+
const el = current as JsxElement;
|
|
51
|
+
const cls = el.props && el.props.className;
|
|
52
|
+
if (typeof cls === 'string') {
|
|
53
|
+
const list = splitClassName(cls);
|
|
54
|
+
for (let i = 0; i < list.length; i++) out.push(list[i]);
|
|
55
|
+
}
|
|
56
|
+
const children = el.children || [];
|
|
57
|
+
if (children.length !== 1 || !children[0] || children[0].type !== 'element') break;
|
|
58
|
+
current = children[0];
|
|
59
|
+
depth++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Depth-limited descendant scan for a single Tailwind class. Used by the
|
|
65
|
+
* viewport-pattern detector to confirm a descendant `flex-1` exists before
|
|
66
|
+
* pinning a drawer height — without `flex-1` the height pin would just
|
|
67
|
+
* stretch a fixed-height container with no grow cascade to fill it.
|
|
68
|
+
*/
|
|
69
|
+
export function treeHasDescendantClass(node: JsxNode, cls: string, depth: number = 0): boolean {
|
|
70
|
+
if (!node || typeof node !== 'object' || depth > 6) return false;
|
|
71
|
+
if (node.type === 'element') {
|
|
72
|
+
const el = node as JsxElement;
|
|
73
|
+
if (el.props && typeof el.props.className === 'string') {
|
|
74
|
+
const list = splitClassName(el.props.className);
|
|
75
|
+
if (list.indexOf(cls) !== -1) return true;
|
|
76
|
+
}
|
|
77
|
+
const children = Array.isArray(el.children) ? el.children : [];
|
|
78
|
+
for (let i = 0; i < children.length; i++) {
|
|
79
|
+
if (treeHasDescendantClass(children[i], cls, depth + 1)) return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Detects portal-mounted nodes that anchor to viewport height. Required because
|
|
87
|
+
* Sheet/Drawer content is rendered into a portal subtree — its `h-full` /
|
|
88
|
+
* `inset-y-0` classes don't show up on the story's own spine.
|
|
89
|
+
*/
|
|
90
|
+
export function treeHasPortalWithFullHeight(node: JsxNode, depth: number = 0): boolean {
|
|
91
|
+
if (!node || typeof node !== 'object' || depth > 8) return false;
|
|
92
|
+
if (node.type === 'element') {
|
|
93
|
+
const el = node as JsxElement;
|
|
94
|
+
if (el.props && el.props.__fromPortal) {
|
|
95
|
+
const cls = el.props.className;
|
|
96
|
+
if (typeof cls === 'string') {
|
|
97
|
+
const list = splitClassName(cls);
|
|
98
|
+
if (list.indexOf('h-full') !== -1 || list.indexOf('h-screen') !== -1 || list.indexOf('inset-y-0') !== -1) {
|
|
99
|
+
if (treeHasDescendantClass(el, 'flex-1')) return true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const children = Array.isArray(el.children) ? el.children : [];
|
|
104
|
+
for (let i = 0; i < children.length; i++) {
|
|
105
|
+
if (treeHasPortalWithFullHeight(children[i], depth + 1)) return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns a synthetic viewport height when the story root needs a fixed
|
|
113
|
+
* primary axis to make `h-full` / `flex-1` children render correctly:
|
|
114
|
+
* - `h-screen` / `min-h-screen` on the spine → FULL_PAGE (900)
|
|
115
|
+
* - `h-full` + vertical flex + descendant `flex-1` on the spine → DRAWER (700)
|
|
116
|
+
* - A portal-mounted node with `h-full` (or `inset-y-0`) + descendant
|
|
117
|
+
* `flex-1` (Sheet / Drawer content) → DRAWER (700)
|
|
118
|
+
* Returns null when the story does not need viewport anchoring.
|
|
119
|
+
*/
|
|
120
|
+
export function detectViewportHeightPattern(story: ComponentStory, layoutClasses: string[]): number | null {
|
|
121
|
+
// Portal-filter the tree the same way the renderer does: closed stories drop
|
|
122
|
+
// the portal subtree, so a `Sheet` default (closed) shouldn't be detected as
|
|
123
|
+
// a drawer — only its `defaultOpen` sibling should.
|
|
124
|
+
const tree = story && story.jsxTree ? resolvePortalAwareStoryTree(story.jsxTree) : null;
|
|
125
|
+
|
|
126
|
+
const spineClasses: string[] = [];
|
|
127
|
+
if (tree) collectLeadingSpineClasses(tree, spineClasses);
|
|
128
|
+
for (let i = 0; i < layoutClasses.length; i++) spineClasses.push(layoutClasses[i]);
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < spineClasses.length; i++) {
|
|
131
|
+
const cls = spineClasses[i];
|
|
132
|
+
if (cls === 'h-screen' || cls === 'min-h-screen') return VIEWPORT_HEIGHTS.FULL_PAGE;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const spineHasHFull = spineClasses.indexOf('h-full') !== -1;
|
|
136
|
+
const spineHasFlexCol = spineClasses.indexOf('flex-col') !== -1;
|
|
137
|
+
const treeHasFlexOne = tree ? treeHasDescendantClass(tree, 'flex-1') : false;
|
|
138
|
+
if (spineHasHFull && spineHasFlexCol && treeHasFlexOne) return VIEWPORT_HEIGHTS.DRAWER;
|
|
139
|
+
|
|
140
|
+
if (tree && treeHasPortalWithFullHeight(tree)) return VIEWPORT_HEIGHTS.DRAWER;
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Returns the mobile width when the story root carries a `{bp}:hidden` class —
|
|
147
|
+
* the design is intended to render only at mobile width and would look broken
|
|
148
|
+
* at the default 900px.
|
|
149
|
+
*/
|
|
150
|
+
export function mobileOnlyRootWidth(story: ComponentStory, layoutClasses: string[]): number | null {
|
|
151
|
+
const rootClasses: string[] = [];
|
|
152
|
+
if (story && story.jsxTree) collectLeadingSpineClasses(story.jsxTree, rootClasses);
|
|
153
|
+
for (let i = 0; i < layoutClasses.length; i++) rootClasses.push(layoutClasses[i]);
|
|
154
|
+
for (let i = 0; i < rootClasses.length; i++) {
|
|
155
|
+
const cls = rootClasses[i];
|
|
156
|
+
// Match `sm:hidden`, `md:hidden`, `lg:hidden`, `xl:hidden`, `2xl:hidden`.
|
|
157
|
+
if (/^(sm|md|lg|xl|2xl):hidden$/.test(cls)) return MOBILE_ONLY_DEFAULT_WIDTH;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolves the desktop width of the Story Layout frame and applies it. Picks
|
|
164
|
+
* between an explicit fixed-width class, a tree-derived max-width, grid-cols
|
|
165
|
+
* minimums, mobile-only overrides, and the generic 900px fallback in priority
|
|
166
|
+
* order. The `viewportWidth` parameter overrides everything (used by
|
|
167
|
+
* responsive previews to size at a specific breakpoint).
|
|
168
|
+
*/
|
|
169
|
+
export function resolveStoryLayoutWidth(
|
|
170
|
+
story: ComponentStory,
|
|
171
|
+
layout: LayoutInfo,
|
|
172
|
+
layoutClasses: string[],
|
|
173
|
+
ctx: StoryBuilderContext,
|
|
174
|
+
viewportWidth?: number
|
|
175
|
+
): number {
|
|
176
|
+
const fixedWidth = extractFixedWidth(layoutClasses);
|
|
177
|
+
const maxWidthFromLayout = extractMaxWidth(layoutClasses);
|
|
178
|
+
const maxWidthFromTree = extractLeadingContainerMaxWidthFromTree(story.jsxTree);
|
|
179
|
+
const constrainedMaxWidth = (
|
|
180
|
+
maxWidthFromLayout != null && maxWidthFromTree != null
|
|
181
|
+
? Math.min(maxWidthFromLayout, maxWidthFromTree)
|
|
182
|
+
: (maxWidthFromTree != null ? maxWidthFromTree : maxWidthFromLayout)
|
|
183
|
+
);
|
|
184
|
+
const mobileOnlyWidth = mobileOnlyRootWidth(story, layoutClasses);
|
|
185
|
+
const fallbackWidth = !fixedWidth && story.jsxTree && treeHasFullWidth(story.jsxTree, null, {
|
|
186
|
+
getComponentDefByName: ctx.getComponentDefByName,
|
|
187
|
+
normalizeComponentDef: ctx.normalizeComponentDef,
|
|
188
|
+
hasWidthHintInClasses: ctx.hasWidthHintInClasses,
|
|
189
|
+
propsContainWidthHint: ctx.propsContainWidthHint,
|
|
190
|
+
})
|
|
191
|
+
? (constrainedMaxWidth || 900)
|
|
192
|
+
: null;
|
|
193
|
+
let effectiveWidth = fixedWidth || fallbackWidth;
|
|
194
|
+
const treeGridCols = story.jsxTree ? extractGridColumnsFromTree(story.jsxTree) : null;
|
|
195
|
+
const treeGridMinWidth = story.jsxTree ? extractGridBreakpointWidthFromTree(story.jsxTree) : null;
|
|
196
|
+
const layoutGridCols = extractGridColumns(layoutClasses);
|
|
197
|
+
const layoutGridMinWidth = extractGridBreakpointWidth(layoutClasses);
|
|
198
|
+
const gridCols = layoutGridCols || treeGridCols;
|
|
199
|
+
if (!effectiveWidth && gridCols) {
|
|
200
|
+
effectiveWidth = defaultGridWidth(gridCols);
|
|
201
|
+
} else if (effectiveWidth && layoutGridCols) {
|
|
202
|
+
const minGridWidth = defaultGridWidth(layoutGridCols);
|
|
203
|
+
if (effectiveWidth < minGridWidth) {
|
|
204
|
+
effectiveWidth = minGridWidth;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const gridMinWidth = Math.max(layoutGridMinWidth || 0, treeGridMinWidth || 0);
|
|
208
|
+
if (gridMinWidth > 0) {
|
|
209
|
+
if (!effectiveWidth || effectiveWidth < gridMinWidth) {
|
|
210
|
+
effectiveWidth = gridMinWidth;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (!effectiveWidth) {
|
|
214
|
+
effectiveWidth = mobileOnlyWidth != null ? mobileOnlyWidth : 900;
|
|
215
|
+
} else if (mobileOnlyWidth != null && !fixedWidth && constrainedMaxWidth == null && !gridCols && !gridMinWidth) {
|
|
216
|
+
// Mobile-only components with only `w-full` on a decorator would otherwise
|
|
217
|
+
// inherit the generic 900px fallback via treeHasFullWidth. Clamp them to
|
|
218
|
+
// the mobile default so the design renders at its intended width.
|
|
219
|
+
effectiveWidth = mobileOnlyWidth;
|
|
220
|
+
}
|
|
221
|
+
if (viewportWidth != null && Number.isFinite(viewportWidth) && viewportWidth > 0) {
|
|
222
|
+
effectiveWidth = viewportWidth;
|
|
223
|
+
}
|
|
224
|
+
if (effectiveWidth) {
|
|
225
|
+
layout.resize(effectiveWidth, layout.height);
|
|
226
|
+
if (layout.layoutMode === 'HORIZONTAL') {
|
|
227
|
+
layout.primaryAxisSizingMode = 'FIXED';
|
|
228
|
+
} else {
|
|
229
|
+
layout.counterAxisSizingMode = 'FIXED';
|
|
230
|
+
}
|
|
231
|
+
if (!ctx.hasExplicitHeight(layoutClasses)) {
|
|
232
|
+
// Keep natural content-driven height in responsive previews.
|
|
233
|
+
// Forcing a transient 1px height can leave complex portal content
|
|
234
|
+
// (e.g. Dialog panels) visually clipped after layout settles.
|
|
235
|
+
if (layout.layoutMode === 'HORIZONTAL') {
|
|
236
|
+
layout.counterAxisSizingMode = 'AUTO';
|
|
237
|
+
} else {
|
|
238
|
+
layout.primaryAxisSizingMode = 'AUTO';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return effectiveWidth;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Pin the Story Layout's primary-axis height for viewport-anchored stories so
|
|
247
|
+
* `h-full` / `flex-1` descendants render at their intended sizes. Mirror of
|
|
248
|
+
* `resolveStoryLayoutWidth` on the primary axis.
|
|
249
|
+
*/
|
|
250
|
+
export function resolveStoryLayoutHeight(
|
|
251
|
+
story: ComponentStory,
|
|
252
|
+
layout: LayoutInfo,
|
|
253
|
+
layoutClasses: string[],
|
|
254
|
+
ctx: StoryBuilderContext
|
|
255
|
+
): number | null {
|
|
256
|
+
if (ctx.hasExplicitHeight(layoutClasses)) return null;
|
|
257
|
+
const height = detectViewportHeightPattern(story, layoutClasses);
|
|
258
|
+
if (height == null) return null;
|
|
259
|
+
try {
|
|
260
|
+
layout.resize(layout.width, height);
|
|
261
|
+
if (layout.layoutMode === 'VERTICAL') {
|
|
262
|
+
layout.primaryAxisSizingMode = 'FIXED';
|
|
263
|
+
} else if (layout.layoutMode === 'HORIZONTAL') {
|
|
264
|
+
layout.counterAxisSizingMode = 'FIXED';
|
|
265
|
+
} else if ('primaryAxisSizingMode' in layout) {
|
|
266
|
+
layout.primaryAxisSizingMode = 'FIXED';
|
|
267
|
+
}
|
|
268
|
+
} catch (_err) {
|
|
269
|
+
// ignore resize errors
|
|
270
|
+
}
|
|
271
|
+
return height;
|
|
272
|
+
}
|