@hjmds/design-contracts 0.8.2
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 +208 -0
- package/dist/alert-dialog.d.ts +102 -0
- package/dist/alert-dialog.d.ts.map +1 -0
- package/dist/alert-dialog.js +136 -0
- package/dist/alert-dialog.js.map +1 -0
- package/dist/base-recipes.d.ts +184 -0
- package/dist/base-recipes.d.ts.map +1 -0
- package/dist/base-recipes.js +129 -0
- package/dist/base-recipes.js.map +1 -0
- package/dist/behaviors.d.ts +1254 -0
- package/dist/behaviors.d.ts.map +1 -0
- package/dist/behaviors.js +972 -0
- package/dist/behaviors.js.map +1 -0
- package/dist/bottom-navigation-defaults.d.ts +13 -0
- package/dist/bottom-navigation-defaults.d.ts.map +1 -0
- package/dist/bottom-navigation-defaults.js +10 -0
- package/dist/bottom-navigation-defaults.js.map +1 -0
- package/dist/bottom-navigation.d.ts +114 -0
- package/dist/bottom-navigation.d.ts.map +1 -0
- package/dist/bottom-navigation.js +223 -0
- package/dist/bottom-navigation.js.map +1 -0
- package/dist/breadcrumb.d.ts +97 -0
- package/dist/breadcrumb.d.ts.map +1 -0
- package/dist/breadcrumb.js +99 -0
- package/dist/breadcrumb.js.map +1 -0
- package/dist/calendar.d.ts +285 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +297 -0
- package/dist/calendar.js.map +1 -0
- package/dist/card.d.ts +39 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/card.js +50 -0
- package/dist/card.js.map +1 -0
- package/dist/carousel.d.ts +180 -0
- package/dist/carousel.d.ts.map +1 -0
- package/dist/carousel.js +172 -0
- package/dist/carousel.js.map +1 -0
- package/dist/catalog.d.ts +7158 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +220 -0
- package/dist/catalog.js.map +1 -0
- package/dist/collection.d.ts +123 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +211 -0
- package/dist/collection.js.map +1 -0
- package/dist/color-references.d.ts +46 -0
- package/dist/color-references.d.ts.map +1 -0
- package/dist/color-references.js +39 -0
- package/dist/color-references.js.map +1 -0
- package/dist/colors.d.ts +56 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +90 -0
- package/dist/colors.js.map +1 -0
- package/dist/command-palette.d.ts +240 -0
- package/dist/command-palette.d.ts.map +1 -0
- package/dist/command-palette.js +97 -0
- package/dist/command-palette.js.map +1 -0
- package/dist/component-contracts.d.ts +137 -0
- package/dist/component-contracts.d.ts.map +1 -0
- package/dist/component-contracts.js +53 -0
- package/dist/component-contracts.js.map +1 -0
- package/dist/component-definitions.d.ts +135 -0
- package/dist/component-definitions.d.ts.map +1 -0
- package/dist/component-definitions.js +142 -0
- package/dist/component-definitions.js.map +1 -0
- package/dist/component-recipes.d.ts +3583 -0
- package/dist/component-recipes.d.ts.map +1 -0
- package/dist/component-recipes.js +1503 -0
- package/dist/component-recipes.js.map +1 -0
- package/dist/component-references.d.ts +416 -0
- package/dist/component-references.d.ts.map +1 -0
- package/dist/component-references.js +140 -0
- package/dist/component-references.js.map +1 -0
- package/dist/content-state.d.ts +104 -0
- package/dist/content-state.d.ts.map +1 -0
- package/dist/content-state.js +116 -0
- package/dist/content-state.js.map +1 -0
- package/dist/counter-badge-recipe.d.ts +80 -0
- package/dist/counter-badge-recipe.d.ts.map +1 -0
- package/dist/counter-badge-recipe.js +44 -0
- package/dist/counter-badge-recipe.js.map +1 -0
- package/dist/counter-badge.d.ts +12 -0
- package/dist/counter-badge.d.ts.map +1 -0
- package/dist/counter-badge.js +21 -0
- package/dist/counter-badge.js.map +1 -0
- package/dist/data-table.d.ts +203 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +182 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-picker.d.ts +268 -0
- package/dist/date-picker.d.ts.map +1 -0
- package/dist/date-picker.js +168 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/description-list.d.ts +68 -0
- package/dist/description-list.d.ts.map +1 -0
- package/dist/description-list.js +85 -0
- package/dist/description-list.js.map +1 -0
- package/dist/design-system-provider.d.ts +80 -0
- package/dist/design-system-provider.d.ts.map +1 -0
- package/dist/design-system-provider.js +147 -0
- package/dist/design-system-provider.js.map +1 -0
- package/dist/evidence.d.ts +49 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +133 -0
- package/dist/evidence.js.map +1 -0
- package/dist/file-picker.d.ts +183 -0
- package/dist/file-picker.d.ts.map +1 -0
- package/dist/file-picker.js +224 -0
- package/dist/file-picker.js.map +1 -0
- package/dist/floating-action-button.d.ts +143 -0
- package/dist/floating-action-button.d.ts.map +1 -0
- package/dist/floating-action-button.js +149 -0
- package/dist/floating-action-button.js.map +1 -0
- package/dist/form.d.ts +143 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +206 -0
- package/dist/form.js.map +1 -0
- package/dist/foundations.d.ts +300 -0
- package/dist/foundations.d.ts.map +1 -0
- package/dist/foundations.js +238 -0
- package/dist/foundations.js.map +1 -0
- package/dist/grid.d.ts +75 -0
- package/dist/grid.d.ts.map +1 -0
- package/dist/grid.js +133 -0
- package/dist/grid.js.map +1 -0
- package/dist/icon-button-recipe.d.ts +99 -0
- package/dist/icon-button-recipe.d.ts.map +1 -0
- package/dist/icon-button-recipe.js +53 -0
- package/dist/icon-button-recipe.js.map +1 -0
- package/dist/icon.d.ts +41 -0
- package/dist/icon.d.ts.map +1 -0
- package/dist/icon.js +147 -0
- package/dist/icon.js.map +1 -0
- package/dist/image.d.ts +82 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +100 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +118 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +118 -0
- package/dist/layout.js.map +1 -0
- package/dist/link.d.ts +57 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +142 -0
- package/dist/link.js.map +1 -0
- package/dist/load-more.d.ts +64 -0
- package/dist/load-more.d.ts.map +1 -0
- package/dist/load-more.js +124 -0
- package/dist/load-more.js.map +1 -0
- package/dist/mentions.d.ts +67 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +107 -0
- package/dist/mentions.js.map +1 -0
- package/dist/number-field.d.ts +208 -0
- package/dist/number-field.d.ts.map +1 -0
- package/dist/number-field.js +247 -0
- package/dist/number-field.js.map +1 -0
- package/dist/otp-field.d.ts +152 -0
- package/dist/otp-field.d.ts.map +1 -0
- package/dist/otp-field.js +117 -0
- package/dist/otp-field.js.map +1 -0
- package/dist/pagination.d.ts +181 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +226 -0
- package/dist/pagination.js.map +1 -0
- package/dist/password-field.d.ts +187 -0
- package/dist/password-field.d.ts.map +1 -0
- package/dist/password-field.js +120 -0
- package/dist/password-field.js.map +1 -0
- package/dist/popover.d.ts +152 -0
- package/dist/popover.d.ts.map +1 -0
- package/dist/popover.js +137 -0
- package/dist/popover.js.map +1 -0
- package/dist/progress-recipe.d.ts +43 -0
- package/dist/progress-recipe.d.ts.map +1 -0
- package/dist/progress-recipe.js +16 -0
- package/dist/progress-recipe.js.map +1 -0
- package/dist/recipes.d.ts +31 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +46 -0
- package/dist/recipes.js.map +1 -0
- package/dist/responsive.d.ts +27 -0
- package/dist/responsive.d.ts.map +1 -0
- package/dist/responsive.js +66 -0
- package/dist/responsive.js.map +1 -0
- package/dist/result.d.ts +111 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +97 -0
- package/dist/result.js.map +1 -0
- package/dist/selection-helpers.d.ts +16 -0
- package/dist/selection-helpers.d.ts.map +1 -0
- package/dist/selection-helpers.js +52 -0
- package/dist/selection-helpers.js.map +1 -0
- package/dist/semantic-colors.d.ts +275 -0
- package/dist/semantic-colors.d.ts.map +1 -0
- package/dist/semantic-colors.js +84 -0
- package/dist/semantic-colors.js.map +1 -0
- package/dist/sheet.d.ts +51 -0
- package/dist/sheet.d.ts.map +1 -0
- package/dist/sheet.js +69 -0
- package/dist/sheet.js.map +1 -0
- package/dist/showcase.d.ts +153 -0
- package/dist/showcase.d.ts.map +1 -0
- package/dist/showcase.js +210 -0
- package/dist/showcase.js.map +1 -0
- package/dist/side-panel.d.ts +199 -0
- package/dist/side-panel.d.ts.map +1 -0
- package/dist/side-panel.js +111 -0
- package/dist/side-panel.js.map +1 -0
- package/dist/slider.d.ts +138 -0
- package/dist/slider.d.ts.map +1 -0
- package/dist/slider.js +150 -0
- package/dist/slider.js.map +1 -0
- package/dist/splitter.d.ts +113 -0
- package/dist/splitter.d.ts.map +1 -0
- package/dist/splitter.js +99 -0
- package/dist/splitter.js.map +1 -0
- package/dist/statistic.d.ts +41 -0
- package/dist/statistic.d.ts.map +1 -0
- package/dist/statistic.js +76 -0
- package/dist/statistic.js.map +1 -0
- package/dist/steps.d.ts +196 -0
- package/dist/steps.d.ts.map +1 -0
- package/dist/steps.js +160 -0
- package/dist/steps.js.map +1 -0
- package/dist/tag.d.ts +128 -0
- package/dist/tag.d.ts.map +1 -0
- package/dist/tag.js +93 -0
- package/dist/tag.js.map +1 -0
- package/dist/timeline.d.ts +147 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +127 -0
- package/dist/timeline.js.map +1 -0
- package/dist/toast.d.ts +164 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +529 -0
- package/dist/toast.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +9 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tooltip.d.ts +44 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +88 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/tour.d.ts +218 -0
- package/dist/tour.d.ts.map +1 -0
- package/dist/tour.js +211 -0
- package/dist/tour.js.map +1 -0
- package/dist/transfer-list.d.ts +207 -0
- package/dist/transfer-list.d.ts.map +1 -0
- package/dist/transfer-list.js +193 -0
- package/dist/transfer-list.js.map +1 -0
- package/dist/tree-select.d.ts +78 -0
- package/dist/tree-select.d.ts.map +1 -0
- package/dist/tree-select.js +132 -0
- package/dist/tree-select.js.map +1 -0
- package/dist/tree.d.ts +206 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +223 -0
- package/dist/tree.js.map +1 -0
- package/dist/upload-item.d.ts +173 -0
- package/dist/upload-item.d.ts.map +1 -0
- package/dist/upload-item.js +156 -0
- package/dist/upload-item.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/docs/affix.md +63 -0
- package/docs/anchor.md +59 -0
- package/docs/ant-design-coverage.md +119 -0
- package/docs/app-provider.md +50 -0
- package/docs/app-rn-adoption.md +227 -0
- package/docs/architecture.md +320 -0
- package/docs/authoring-brief.md +87 -0
- package/docs/border-beam.md +66 -0
- package/docs/bottom-navigation.md +127 -0
- package/docs/breadcrumb.md +82 -0
- package/docs/calendar.md +154 -0
- package/docs/carousel.md +130 -0
- package/docs/cascader.md +93 -0
- package/docs/catalog-decision-status.md +306 -0
- package/docs/color-picker.md +72 -0
- package/docs/command-palette.md +116 -0
- package/docs/confirm-popover.md +93 -0
- package/docs/consistency-audit.md +383 -0
- package/docs/consumer-release-gate.md +90 -0
- package/docs/content-state.md +156 -0
- package/docs/context-panel.md +88 -0
- package/docs/cross-platform-core-normalization.md +118 -0
- package/docs/data-table.md +79 -0
- package/docs/date-picker.md +84 -0
- package/docs/description-list.md +75 -0
- package/docs/design-system-provider.md +139 -0
- package/docs/dropdown.md +78 -0
- package/docs/expansion-roadmap.md +285 -0
- package/docs/file-picker.md +59 -0
- package/docs/floating-action-button.md +111 -0
- package/docs/form.md +134 -0
- package/docs/generated/component-maturity.md +103 -0
- package/docs/generated/renderer-evidence.json +5597 -0
- package/docs/generated/renderer-evidence.md +132 -0
- package/docs/generated/showcase-manifest.json +3732 -0
- package/docs/icon.md +22 -0
- package/docs/identity.md +121 -0
- package/docs/image.md +84 -0
- package/docs/implementation-0.5.md +85 -0
- package/docs/layout-primitives.md +107 -0
- package/docs/layout.md +83 -0
- package/docs/library-reference-decisions.md +123 -0
- package/docs/link.md +67 -0
- package/docs/load-more.md +31 -0
- package/docs/mentions.md +82 -0
- package/docs/migration-0.2.md +108 -0
- package/docs/migration-0.3.md +72 -0
- package/docs/migration-0.5.md +82 -0
- package/docs/migration-0.6.md +197 -0
- package/docs/notification.md +54 -0
- package/docs/number-field.md +82 -0
- package/docs/otp-field.md +103 -0
- package/docs/pagination.md +139 -0
- package/docs/password-field.md +98 -0
- package/docs/popover.md +124 -0
- package/docs/promotion-candidates.md +169 -0
- package/docs/qr-code.md +69 -0
- package/docs/rating.md +58 -0
- package/docs/responsive-grid.md +90 -0
- package/docs/result.md +96 -0
- package/docs/showcase.md +97 -0
- package/docs/side-panel.md +71 -0
- package/docs/slider.md +79 -0
- package/docs/splitter.md +55 -0
- package/docs/statistic.md +16 -0
- package/docs/steps.md +115 -0
- package/docs/tag.md +57 -0
- package/docs/time-picker.md +88 -0
- package/docs/timeline.md +146 -0
- package/docs/toast.md +127 -0
- package/docs/tooltip.md +58 -0
- package/docs/tour.md +136 -0
- package/docs/transfer-list.md +94 -0
- package/docs/tree-select.md +101 -0
- package/docs/tree.md +123 -0
- package/docs/upload-item.md +68 -0
- package/docs/utility.md +53 -0
- package/docs/virtual-list.md +70 -0
- package/docs/watermark.md +58 -0
- package/package.json +402 -0
package/dist/showcase.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { componentCatalog, getComponentSurfaceStatus, } from "./catalog.js";
|
|
2
|
+
/**
|
|
3
|
+
* A small, deliberate environment matrix catches the high-risk differences
|
|
4
|
+
* without multiplying every story into an unreadable Cartesian product.
|
|
5
|
+
*/
|
|
6
|
+
export const showcaseEnvironmentMatrix = [
|
|
7
|
+
{
|
|
8
|
+
id: "default",
|
|
9
|
+
label: "Light · LTR · 100%",
|
|
10
|
+
theme: "light",
|
|
11
|
+
direction: "ltr",
|
|
12
|
+
textScale: 1,
|
|
13
|
+
motion: "full",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "dark",
|
|
17
|
+
label: "Dark · LTR · 100%",
|
|
18
|
+
theme: "dark",
|
|
19
|
+
direction: "ltr",
|
|
20
|
+
textScale: 1,
|
|
21
|
+
motion: "full",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "large-text",
|
|
25
|
+
label: "Light · LTR · 200%",
|
|
26
|
+
theme: "light",
|
|
27
|
+
direction: "ltr",
|
|
28
|
+
textScale: 2,
|
|
29
|
+
motion: "full",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "rtl",
|
|
33
|
+
label: "Light · RTL · 100%",
|
|
34
|
+
theme: "light",
|
|
35
|
+
direction: "rtl",
|
|
36
|
+
textScale: 1,
|
|
37
|
+
motion: "full",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "reduced-motion",
|
|
41
|
+
label: "Light · LTR · Reduced motion",
|
|
42
|
+
theme: "light",
|
|
43
|
+
direction: "ltr",
|
|
44
|
+
textScale: 1,
|
|
45
|
+
motion: "reduced",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
/** Bridges documentation fixtures to the same environment contract products consume. */
|
|
49
|
+
export function getShowcaseEnvironmentInput(environment) {
|
|
50
|
+
const textScale = environment.textScale;
|
|
51
|
+
return {
|
|
52
|
+
theme: environment.theme,
|
|
53
|
+
direction: environment.direction,
|
|
54
|
+
textScale,
|
|
55
|
+
reducedMotion: environment.motion === "reduced",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export const showcaseScenarios = [
|
|
59
|
+
{
|
|
60
|
+
id: "contract",
|
|
61
|
+
label: "Contract",
|
|
62
|
+
description: "Anatomy, defaults, supported axes, platform and maturity are visible.",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "default",
|
|
66
|
+
label: "Default",
|
|
67
|
+
description: "The normal state renders using recipe defaults without product overrides.",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "dark",
|
|
71
|
+
label: "Dark",
|
|
72
|
+
description: "Semantic colors remain legible on the dark theme.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "long-copy",
|
|
76
|
+
label: "Long copy",
|
|
77
|
+
description: "Long Korean and English copy wraps without clipping or hiding meaning.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "large-text",
|
|
81
|
+
label: "200% text",
|
|
82
|
+
description: "Text can grow to 200% without truncating required information.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "rtl",
|
|
86
|
+
label: "RTL",
|
|
87
|
+
description: "Logical start/end layout and directional icons mirror correctly.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "reduced-motion",
|
|
91
|
+
label: "Reduced motion",
|
|
92
|
+
description: "Motion follows the recipe reduced-motion fallback.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "accessibility",
|
|
96
|
+
label: "Accessibility",
|
|
97
|
+
description: "Names, states, relationships, contrast and touch targets are inspectable.",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "keyboard",
|
|
101
|
+
label: "Keyboard",
|
|
102
|
+
description: "Focus order and documented keyboard behavior run as interaction tests.",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "platform-parity",
|
|
106
|
+
label: "Web / Native parity",
|
|
107
|
+
description: "Adaptive renderers preserve intent while using platform-native behavior.",
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
const plannedRequirements = ["contract"];
|
|
111
|
+
const rendererRequirements = [
|
|
112
|
+
"default",
|
|
113
|
+
"dark",
|
|
114
|
+
"long-copy",
|
|
115
|
+
"large-text",
|
|
116
|
+
"rtl",
|
|
117
|
+
"reduced-motion",
|
|
118
|
+
"accessibility",
|
|
119
|
+
];
|
|
120
|
+
export function getShowcaseStoryId(entry) {
|
|
121
|
+
const slug = entry.name
|
|
122
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
123
|
+
.replace(/[^a-zA-Z0-9]+/g, "-")
|
|
124
|
+
.replace(/^-|-$/g, "")
|
|
125
|
+
.toLowerCase();
|
|
126
|
+
return `${entry.category}/${slug}`;
|
|
127
|
+
}
|
|
128
|
+
export function getRequiredShowcaseScenarios(entry) {
|
|
129
|
+
const activeSurfaces = ["web", "native"].filter((surface) => isRendererMaturity(getComponentSurfaceStatus(entry, surface)));
|
|
130
|
+
if (activeSurfaces.length === 0)
|
|
131
|
+
return plannedRequirements;
|
|
132
|
+
return ["contract", ...getRendererShowcaseScenarios(entry, activeSurfaces)];
|
|
133
|
+
}
|
|
134
|
+
function isRendererMaturity(status) {
|
|
135
|
+
return status === "stable" || status === "beta";
|
|
136
|
+
}
|
|
137
|
+
function getRendererShowcaseScenarios(entry, activeSurfaces) {
|
|
138
|
+
const requirements = [...rendererRequirements];
|
|
139
|
+
if (entry.behavior) {
|
|
140
|
+
requirements.push("keyboard");
|
|
141
|
+
}
|
|
142
|
+
if (entry.platform === "adaptive" &&
|
|
143
|
+
activeSurfaces.includes("web") &&
|
|
144
|
+
activeSurfaces.includes("native")) {
|
|
145
|
+
requirements.push("platform-parity");
|
|
146
|
+
}
|
|
147
|
+
return requirements;
|
|
148
|
+
}
|
|
149
|
+
export function getRequiredShowcaseSurfaces(entry) {
|
|
150
|
+
return getRequiredShowcaseEvidence(entry).map(({ surface }) => surface);
|
|
151
|
+
}
|
|
152
|
+
export function getRequiredShowcaseEvidence(entry) {
|
|
153
|
+
const requirements = [
|
|
154
|
+
{ surface: "contract", scenarios: ["contract"] },
|
|
155
|
+
];
|
|
156
|
+
const activeSurfaces = ["web", "native"].filter((surface) => isRendererMaturity(getComponentSurfaceStatus(entry, surface)));
|
|
157
|
+
const rendererScenarios = getRendererShowcaseScenarios(entry, activeSurfaces);
|
|
158
|
+
for (const surface of activeSurfaces) {
|
|
159
|
+
requirements.push({ surface, scenarios: rendererScenarios });
|
|
160
|
+
}
|
|
161
|
+
return requirements;
|
|
162
|
+
}
|
|
163
|
+
export function createShowcaseManifest(entries = componentCatalog) {
|
|
164
|
+
return entries.map((component) => ({
|
|
165
|
+
storyId: getShowcaseStoryId(component),
|
|
166
|
+
component,
|
|
167
|
+
surfaceMaturity: {
|
|
168
|
+
web: getComponentSurfaceStatus(component, "web"),
|
|
169
|
+
native: getComponentSurfaceStatus(component, "native"),
|
|
170
|
+
},
|
|
171
|
+
requirements: getRequiredShowcaseEvidence(component),
|
|
172
|
+
requiredScenarios: getRequiredShowcaseScenarios(component),
|
|
173
|
+
requiredSurfaces: getRequiredShowcaseSurfaces(component),
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
export const showcaseManifest = createShowcaseManifest();
|
|
177
|
+
export function createShowcaseCoverage(evidence, manifest = showcaseManifest) {
|
|
178
|
+
return manifest.map(({ storyId, component, requirements }) => {
|
|
179
|
+
const componentEvidence = evidence.filter((entry) => entry.storyId === storyId);
|
|
180
|
+
const missingEvidence = requirements.flatMap(({ surface, scenarios }) => scenarios
|
|
181
|
+
.filter((scenario) => !componentEvidence.some((entry) => entry.surface === surface && entry.scenarios.includes(scenario)))
|
|
182
|
+
.map((scenario) => ({ surface, scenario })));
|
|
183
|
+
const missingSurfaces = [...new Set(missingEvidence.map(({ surface }) => surface))];
|
|
184
|
+
const missingScenarios = [...new Set(missingEvidence.map(({ scenario }) => scenario))];
|
|
185
|
+
return {
|
|
186
|
+
storyId,
|
|
187
|
+
component,
|
|
188
|
+
missingEvidence,
|
|
189
|
+
missingSurfaces,
|
|
190
|
+
missingScenarios,
|
|
191
|
+
complete: missingSurfaces.length === 0 && missingScenarios.length === 0,
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
export function assertShowcaseCoverage(evidence, manifest = showcaseManifest) {
|
|
196
|
+
const missing = createShowcaseCoverage(evidence, manifest).filter(({ complete }) => !complete);
|
|
197
|
+
if (missing.length === 0)
|
|
198
|
+
return;
|
|
199
|
+
const details = missing
|
|
200
|
+
.map(({ storyId, missingEvidence }) => `${storyId} (${missingEvidence.map(({ surface, scenario }) => `${surface}/${scenario}`).join(", ") || "none"})`)
|
|
201
|
+
.join("\n");
|
|
202
|
+
throw new Error(`Showcase evidence is incomplete:\n${details}`);
|
|
203
|
+
}
|
|
204
|
+
export function summarizeShowcaseMaturity(entries = componentCatalog) {
|
|
205
|
+
return entries.reduce((summary, entry) => {
|
|
206
|
+
summary[entry.status] += 1;
|
|
207
|
+
return summary;
|
|
208
|
+
}, { stable: 0, beta: 0, planned: 0, deprecated: 0 });
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=showcase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"showcase.js","sourceRoot":"","sources":["../src/showcase.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n componentCatalog,\n getComponentSurfaceStatus,\n type ComponentCatalogEntry,\n type ComponentStatus,\n type ComponentSurface,\n type ComponentSurfaceMaturity,\n type ComponentSurfaceStatus,\n} from \"./catalog.js\";\nimport type { ResolvedTheme } from \"./colors.js\";\nimport type {\n DesignSystemDirection,\n DesignSystemEnvironmentInput,\n DesignSystemTextScale,\n} from \"./design-system-provider.js\";\n\nexport type ShowcaseTheme = ResolvedTheme;\nexport type ShowcaseDirection = DesignSystemDirection;\n/** Closed fixture subset of the Provider's continuous text-scale signal. */\nexport type ShowcaseTextScale = 1 | 1.5 | 2;\nexport type ShowcaseMotionPreference = \"full\" | \"reduced\";\n\nexport type ShowcaseEnvironment = Readonly<{\n id: \"default\" | \"dark\" | \"large-text\" | \"rtl\" | \"reduced-motion\";\n label: string;\n theme: ShowcaseTheme;\n direction: ShowcaseDirection;\n textScale: ShowcaseTextScale;\n motion: ShowcaseMotionPreference;\n}>;\n\n/**\n * A small, deliberate environment matrix catches the high-risk differences\n * without multiplying every story into an unreadable Cartesian product.\n */\nexport const showcaseEnvironmentMatrix = [\n {\n id: \"default\",\n label: \"Light · LTR · 100%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"dark\",\n label: \"Dark · LTR · 100%\",\n theme: \"dark\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"large-text\",\n label: \"Light · LTR · 200%\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 2,\n motion: \"full\",\n },\n {\n id: \"rtl\",\n label: \"Light · RTL · 100%\",\n theme: \"light\",\n direction: \"rtl\",\n textScale: 1,\n motion: \"full\",\n },\n {\n id: \"reduced-motion\",\n label: \"Light · LTR · Reduced motion\",\n theme: \"light\",\n direction: \"ltr\",\n textScale: 1,\n motion: \"reduced\",\n },\n] as const satisfies readonly ShowcaseEnvironment[];\n\n/** Bridges documentation fixtures to the same environment contract products consume. */\nexport function getShowcaseEnvironmentInput(\n environment: ShowcaseEnvironment,\n): DesignSystemEnvironmentInput {\n const textScale: DesignSystemTextScale = environment.textScale;\n return {\n theme: environment.theme,\n direction: environment.direction,\n textScale,\n reducedMotion: environment.motion === \"reduced\",\n };\n}\n\nexport type ShowcaseScenarioId =\n | \"contract\"\n | \"default\"\n | \"dark\"\n | \"long-copy\"\n | \"large-text\"\n | \"rtl\"\n | \"reduced-motion\"\n | \"accessibility\"\n | \"keyboard\"\n | \"platform-parity\";\n\nexport type ShowcaseSurface = \"contract\" | \"web\" | \"native\";\n\nexport type ShowcaseScenario = Readonly<{\n id: ShowcaseScenarioId;\n label: string;\n description: string;\n}>;\n\nexport const showcaseScenarios = [\n {\n id: \"contract\",\n label: \"Contract\",\n description: \"Anatomy, defaults, supported axes, platform and maturity are visible.\",\n },\n {\n id: \"default\",\n label: \"Default\",\n description: \"The normal state renders using recipe defaults without product overrides.\",\n },\n {\n id: \"dark\",\n label: \"Dark\",\n description: \"Semantic colors remain legible on the dark theme.\",\n },\n {\n id: \"long-copy\",\n label: \"Long copy\",\n description: \"Long Korean and English copy wraps without clipping or hiding meaning.\",\n },\n {\n id: \"large-text\",\n label: \"200% text\",\n description: \"Text can grow to 200% without truncating required information.\",\n },\n {\n id: \"rtl\",\n label: \"RTL\",\n description: \"Logical start/end layout and directional icons mirror correctly.\",\n },\n {\n id: \"reduced-motion\",\n label: \"Reduced motion\",\n description: \"Motion follows the recipe reduced-motion fallback.\",\n },\n {\n id: \"accessibility\",\n label: \"Accessibility\",\n description: \"Names, states, relationships, contrast and touch targets are inspectable.\",\n },\n {\n id: \"keyboard\",\n label: \"Keyboard\",\n description: \"Focus order and documented keyboard behavior run as interaction tests.\",\n },\n {\n id: \"platform-parity\",\n label: \"Web / Native parity\",\n description: \"Adaptive renderers preserve intent while using platform-native behavior.\",\n },\n] as const satisfies readonly ShowcaseScenario[];\n\nconst plannedRequirements = [\"contract\"] as const satisfies readonly ShowcaseScenarioId[];\nconst rendererRequirements = [\n \"default\",\n \"dark\",\n \"long-copy\",\n \"large-text\",\n \"rtl\",\n \"reduced-motion\",\n \"accessibility\",\n] as const satisfies readonly Exclude<ShowcaseScenarioId, \"contract\">[];\n\nexport type ShowcaseComponentEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n /** Resolved Web/Native renderer maturity, including legacy catalog inputs. */\n surfaceMaturity: ComponentSurfaceMaturity;\n requirements: readonly ShowcaseEvidenceRequirement[];\n requiredScenarios: readonly ShowcaseScenarioId[];\n requiredSurfaces: readonly ShowcaseSurface[];\n}>;\n\nexport type ShowcaseEvidenceRequirement = Readonly<{\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseEvidenceEntry = Readonly<{\n storyId: string;\n surface: ShowcaseSurface;\n scenarios: readonly ShowcaseScenarioId[];\n}>;\n\nexport type ShowcaseCoverageEntry = Readonly<{\n storyId: string;\n component: ComponentCatalogEntry;\n missingEvidence: readonly Readonly<{\n surface: ShowcaseSurface;\n scenario: ShowcaseScenarioId;\n }>[];\n missingSurfaces: readonly ShowcaseSurface[];\n missingScenarios: readonly ShowcaseScenarioId[];\n complete: boolean;\n}>;\n\nexport function getShowcaseStoryId(entry: ComponentCatalogEntry): string {\n const slug = entry.name\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .toLowerCase();\n return `${entry.category}/${slug}`;\n}\n\nexport function getRequiredShowcaseScenarios(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseScenarioId[] {\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n if (activeSurfaces.length === 0) return plannedRequirements;\n\n return [\"contract\", ...getRendererShowcaseScenarios(entry, activeSurfaces)];\n}\n\nfunction isRendererMaturity(\n status: ComponentSurfaceStatus,\n): status is Extract<ComponentStatus, \"stable\" | \"beta\"> {\n return status === \"stable\" || status === \"beta\";\n}\n\nfunction getRendererShowcaseScenarios(\n entry: ComponentCatalogEntry,\n activeSurfaces: readonly ComponentSurface[],\n): readonly Exclude<ShowcaseScenarioId, \"contract\">[] {\n const requirements: Exclude<ShowcaseScenarioId, \"contract\">[] = [...rendererRequirements];\n if (entry.behavior) {\n requirements.push(\"keyboard\");\n }\n if (\n entry.platform === \"adaptive\" &&\n activeSurfaces.includes(\"web\") &&\n activeSurfaces.includes(\"native\")\n ) {\n requirements.push(\"platform-parity\");\n }\n return requirements;\n}\n\nexport function getRequiredShowcaseSurfaces(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseSurface[] {\n return getRequiredShowcaseEvidence(entry).map(({ surface }) => surface);\n}\n\nexport function getRequiredShowcaseEvidence(\n entry: ComponentCatalogEntry,\n): readonly ShowcaseEvidenceRequirement[] {\n const requirements: ShowcaseEvidenceRequirement[] = [\n { surface: \"contract\", scenarios: [\"contract\"] },\n ];\n const activeSurfaces = ([\"web\", \"native\"] as const).filter((surface) =>\n isRendererMaturity(getComponentSurfaceStatus(entry, surface)),\n );\n const rendererScenarios = getRendererShowcaseScenarios(entry, activeSurfaces);\n for (const surface of activeSurfaces) {\n requirements.push({ surface, scenarios: rendererScenarios });\n }\n return requirements;\n}\n\nexport function createShowcaseManifest(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): readonly ShowcaseComponentEntry[] {\n return entries.map((component) => ({\n storyId: getShowcaseStoryId(component),\n component,\n surfaceMaturity: {\n web: getComponentSurfaceStatus(component, \"web\"),\n native: getComponentSurfaceStatus(component, \"native\"),\n },\n requirements: getRequiredShowcaseEvidence(component),\n requiredScenarios: getRequiredShowcaseScenarios(component),\n requiredSurfaces: getRequiredShowcaseSurfaces(component),\n }));\n}\n\nexport const showcaseManifest = createShowcaseManifest();\n\nexport function createShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): readonly ShowcaseCoverageEntry[] {\n return manifest.map(({ storyId, component, requirements }) => {\n const componentEvidence = evidence.filter((entry) => entry.storyId === storyId);\n const missingEvidence = requirements.flatMap(({ surface, scenarios }) =>\n scenarios\n .filter(\n (scenario) =>\n !componentEvidence.some(\n (entry) => entry.surface === surface && entry.scenarios.includes(scenario),\n ),\n )\n .map((scenario) => ({ surface, scenario })),\n );\n const missingSurfaces = [...new Set(missingEvidence.map(({ surface }) => surface))];\n const missingScenarios = [...new Set(missingEvidence.map(({ scenario }) => scenario))];\n return {\n storyId,\n component,\n missingEvidence,\n missingSurfaces,\n missingScenarios,\n complete: missingSurfaces.length === 0 && missingScenarios.length === 0,\n };\n });\n}\n\nexport function assertShowcaseCoverage(\n evidence: readonly ShowcaseEvidenceEntry[],\n manifest: readonly ShowcaseComponentEntry[] = showcaseManifest,\n): void {\n const missing = createShowcaseCoverage(evidence, manifest).filter(({ complete }) => !complete);\n if (missing.length === 0) return;\n const details = missing\n .map(({ storyId, missingEvidence }) =>\n `${storyId} (${missingEvidence.map(({ surface, scenario }) => `${surface}/${scenario}`).join(\", \") || \"none\"})`,\n )\n .join(\"\\n\");\n throw new Error(`Showcase evidence is incomplete:\\n${details}`);\n}\n\nexport function summarizeShowcaseMaturity(\n entries: readonly ComponentCatalogEntry[] = componentCatalog,\n): Readonly<Record<ComponentStatus, number>> {\n return entries.reduce<Record<ComponentStatus, number>>(\n (summary, entry) => {\n summary[entry.status] += 1;\n return summary;\n },\n { stable: 0, beta: 0, planned: 0, deprecated: 0 },\n );\n}\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** Logical, RTL-aware docking side. `top`/`bottom` stay Sheet's job — see docs/side-panel.md. */
|
|
2
|
+
export type SidePanelEdge = "start" | "end";
|
|
3
|
+
/**
|
|
4
|
+
* No `"back"`/`"swipe"`: SidePanel is Web-only (no hardware back, no
|
|
5
|
+
* measured demand for a swipe-to-dismiss drawer). Everything else mirrors
|
|
6
|
+
* `SheetDismissReason` (`src/sheet.ts`) because the *reasons* a panel closes
|
|
7
|
+
* are the same on Web; only which reasons are *permitted* differs by `modal`.
|
|
8
|
+
*/
|
|
9
|
+
export type SidePanelDismissReason = "close-action" | "escape" | "outside" | "programmatic";
|
|
10
|
+
export type SidePanelOpenChangeDetails = Readonly<{
|
|
11
|
+
reason: "trigger" | SidePanelDismissReason;
|
|
12
|
+
}>;
|
|
13
|
+
export type SidePanelOpenState = Readonly<{
|
|
14
|
+
open: boolean;
|
|
15
|
+
defaultOpen?: never;
|
|
16
|
+
onOpenChange(open: boolean, detail: SidePanelOpenChangeDetails): void;
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
open?: never;
|
|
19
|
+
defaultOpen?: boolean;
|
|
20
|
+
onOpenChange?: (open: boolean, detail: SidePanelOpenChangeDetails) => void;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* `modal` is the axis Sheet does not need (Sheet is always modal). A
|
|
24
|
+
* non-modal panel has no backdrop to click "outside" of — the rest of the
|
|
25
|
+
* page stays live and interactive — so `outsideDismiss` is not merely
|
|
26
|
+
* defaulted to `false` there, the type forbids it from existing at all.
|
|
27
|
+
* This is the same "make the invalid combination unrepresentable" move as
|
|
28
|
+
* `SheetOpenState`'s controlled/uncontrolled split.
|
|
29
|
+
*/
|
|
30
|
+
export type SidePanelDismissPolicy = Readonly<{
|
|
31
|
+
modal: true;
|
|
32
|
+
dismissible: boolean;
|
|
33
|
+
dismissWhileBusy: boolean;
|
|
34
|
+
escapeDismiss: boolean;
|
|
35
|
+
outsideDismiss: boolean;
|
|
36
|
+
}> | Readonly<{
|
|
37
|
+
modal: false;
|
|
38
|
+
dismissible: boolean;
|
|
39
|
+
dismissWhileBusy: boolean;
|
|
40
|
+
escapeDismiss: boolean;
|
|
41
|
+
outsideDismiss?: never;
|
|
42
|
+
}>;
|
|
43
|
+
export declare const sidePanelBehaviorDefaults: {
|
|
44
|
+
readonly modal: true;
|
|
45
|
+
readonly dismissible: true;
|
|
46
|
+
readonly dismissWhileBusy: false;
|
|
47
|
+
readonly escapeDismiss: true;
|
|
48
|
+
readonly outsideDismiss: true;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Same shape and role as `canDismissSheet`, kept as a separate function
|
|
52
|
+
* because the two owning types are not generic over each other (`sheet.ts`
|
|
53
|
+
* is a shared file this module must not edit) and the `modal` branch is
|
|
54
|
+
* genuine new logic, not a copy.
|
|
55
|
+
*/
|
|
56
|
+
export declare function canDismissSidePanel(reason: SidePanelDismissReason, busy: boolean, policy?: SidePanelDismissPolicy): boolean;
|
|
57
|
+
export type SidePanelSize = "compact" | "regular" | "wide";
|
|
58
|
+
/**
|
|
59
|
+
* No `createSidePanelLifecycle` counterpart to `createSheetLifecycle`: that
|
|
60
|
+
* machinery exists solely to make Android's persistently-mounted native
|
|
61
|
+
* Modal settle one visible cycle at a time. SidePanel is Web-only, where a
|
|
62
|
+
* plain CSS `transitionend`/exit-callback (the same pattern `sheetRecipe`
|
|
63
|
+
* and `dialogRecipe` already assume for Web) is the whole story — adding a
|
|
64
|
+
* cycle counter here would solve a problem this platform does not have.
|
|
65
|
+
*/
|
|
66
|
+
export declare const sidePanelRecipe: {
|
|
67
|
+
readonly slots: readonly ["backdrop", "positioner", "content", "header", "title", "body", "footer", "close"];
|
|
68
|
+
readonly defaults: {
|
|
69
|
+
readonly edge: "end";
|
|
70
|
+
readonly size: "regular";
|
|
71
|
+
};
|
|
72
|
+
readonly sizes: {
|
|
73
|
+
readonly compact: 320;
|
|
74
|
+
readonly regular: 400;
|
|
75
|
+
readonly wide: 560;
|
|
76
|
+
};
|
|
77
|
+
readonly backdrop: {
|
|
78
|
+
readonly color: "#000000";
|
|
79
|
+
readonly opacity: 0.6;
|
|
80
|
+
};
|
|
81
|
+
readonly content: {
|
|
82
|
+
readonly background: Readonly<{
|
|
83
|
+
source: "theme";
|
|
84
|
+
key: "bg";
|
|
85
|
+
alpha?: number;
|
|
86
|
+
}>;
|
|
87
|
+
readonly border: Readonly<{
|
|
88
|
+
source: "theme";
|
|
89
|
+
key: "border";
|
|
90
|
+
alpha?: number;
|
|
91
|
+
}>;
|
|
92
|
+
readonly borderWidth: 1;
|
|
93
|
+
/** Flush to the viewport edge it docks to — unlike Sheet's floating `xl` radius. */
|
|
94
|
+
readonly radius: null;
|
|
95
|
+
readonly shadow: {
|
|
96
|
+
readonly color: "#000000";
|
|
97
|
+
readonly opacity: 0.16;
|
|
98
|
+
readonly radius: 24;
|
|
99
|
+
readonly offsetY: 8;
|
|
100
|
+
};
|
|
101
|
+
readonly paddingHorizontal: 20;
|
|
102
|
+
readonly paddingTop: 12;
|
|
103
|
+
readonly paddingBottom: 12;
|
|
104
|
+
};
|
|
105
|
+
readonly header: {
|
|
106
|
+
readonly minHeight: 44;
|
|
107
|
+
readonly gap: 12;
|
|
108
|
+
};
|
|
109
|
+
readonly title: {
|
|
110
|
+
readonly color: Readonly<{
|
|
111
|
+
source: "theme";
|
|
112
|
+
key: "text";
|
|
113
|
+
alpha?: number;
|
|
114
|
+
}>;
|
|
115
|
+
readonly textVariant: "title";
|
|
116
|
+
readonly fontWeight: "700";
|
|
117
|
+
};
|
|
118
|
+
readonly body: {
|
|
119
|
+
readonly color: Readonly<{
|
|
120
|
+
source: "theme";
|
|
121
|
+
key: "textBody";
|
|
122
|
+
alpha?: number;
|
|
123
|
+
}>;
|
|
124
|
+
readonly textVariant: "body";
|
|
125
|
+
readonly gap: 16;
|
|
126
|
+
};
|
|
127
|
+
readonly footer: {
|
|
128
|
+
readonly color: Readonly<{
|
|
129
|
+
source: "theme";
|
|
130
|
+
key: "textBody";
|
|
131
|
+
alpha?: number;
|
|
132
|
+
}>;
|
|
133
|
+
readonly textVariant: "body";
|
|
134
|
+
readonly gap: 12;
|
|
135
|
+
readonly paddingTop: 12;
|
|
136
|
+
};
|
|
137
|
+
readonly transition: {
|
|
138
|
+
readonly enter: {
|
|
139
|
+
readonly duration: 200;
|
|
140
|
+
readonly easing: "enter";
|
|
141
|
+
readonly reducedMotion: "opacity";
|
|
142
|
+
};
|
|
143
|
+
readonly exit: {
|
|
144
|
+
readonly duration: 120;
|
|
145
|
+
readonly easing: "exit";
|
|
146
|
+
readonly reducedMotion: "instant";
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
readonly states: {
|
|
150
|
+
readonly focus: {
|
|
151
|
+
readonly color: Readonly<{
|
|
152
|
+
source: "theme";
|
|
153
|
+
key: "contentBrand";
|
|
154
|
+
alpha?: number;
|
|
155
|
+
}>;
|
|
156
|
+
readonly width: 2;
|
|
157
|
+
readonly offset: 2;
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* `web.focus` can only hold one value, but the real behavior branches on
|
|
163
|
+
* `modal`: `"trap"` (default, modal) vs. no trap at all when `modal: false`.
|
|
164
|
+
* The field records the default configuration; the non-modal case is a
|
|
165
|
+
* documented exception in `docs/side-panel.md`, the same way this contract
|
|
166
|
+
* records one representative value elsewhere and pushes edge cases to
|
|
167
|
+
* `scenarios`.
|
|
168
|
+
*/
|
|
169
|
+
export declare const sidePanelBehavior: {
|
|
170
|
+
readonly controlled: readonly ["open", "defaultOpen", "onOpenChange", "dismissPolicy"];
|
|
171
|
+
readonly defaults: {
|
|
172
|
+
readonly modal: true;
|
|
173
|
+
readonly dismissible: true;
|
|
174
|
+
readonly dismissWhileBusy: false;
|
|
175
|
+
readonly escapeDismiss: true;
|
|
176
|
+
readonly outsideDismiss: true;
|
|
177
|
+
};
|
|
178
|
+
readonly configuration: {
|
|
179
|
+
readonly edge: readonly ["start", "end"];
|
|
180
|
+
readonly modal: readonly ["true", "false"];
|
|
181
|
+
};
|
|
182
|
+
readonly stateAxes: {
|
|
183
|
+
readonly availability: readonly ["enabled", "busy"];
|
|
184
|
+
readonly value: readonly ["open"];
|
|
185
|
+
};
|
|
186
|
+
readonly web: {
|
|
187
|
+
readonly roles: readonly ["dialog"];
|
|
188
|
+
readonly keyboard: readonly ["Tab", "Escape"];
|
|
189
|
+
readonly focus: "trap";
|
|
190
|
+
readonly dismiss: readonly ["escape", "outside"];
|
|
191
|
+
};
|
|
192
|
+
readonly native: {
|
|
193
|
+
readonly roles: readonly [];
|
|
194
|
+
readonly states: readonly [];
|
|
195
|
+
readonly actions: readonly [];
|
|
196
|
+
};
|
|
197
|
+
readonly scenarios: readonly ["edge-is-a-logical-start-or-end-direction-never-left-or-right", "modal-panels-trap-focus-and-lock-scroll-non-modal-panels-do-neither", "non-modal-panels-never-expose-outside-dismiss-the-type-forbids-it", "single-dismiss-callback-reports-the-concrete-reason", "busy-blocks-user-dismiss-programmatic-owner-close-is-always-allowed", "escape-dismisses-in-both-modal-and-non-modal-panels", "no-back-or-swipe-dismiss-reason-exists-on-this-web-only-platform", "reduced-motion-still-completes-the-exit-callback-once"];
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=side-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-panel.d.ts","sourceRoot":"","sources":["../src/side-panel.ts"],"names":[],"mappings":"AAeA,iGAAiG;AACjG,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,cAAc,CAAC;AAE5F,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,MAAM,EAAE,SAAS,GAAG,sBAAsB,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,CAAC;IACP,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,0BAA0B,GAAG,IAAI,CAAC;CACvE,CAAC,GACF,QAAQ,CAAC;IACP,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC5E,CAAC,CAAC;AAEP;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,CAAC;IACP,KAAK,EAAE,IAAI,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC,GACF,QAAQ,CAAC;IACP,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB,CAAC,CAAC;AAEP,eAAO,MAAM,yBAAyB;;;;;;CAM+B,CAAC;AAEtE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,sBAAsB,EAC9B,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,sBAAkD,GACzD,OAAO,CAOT;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;QASxB,oFAAoF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CvF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BO,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { floatingSurfaceContract, focusIndicatorContract } from "./component-contracts.js";
|
|
2
|
+
import { backdrop, control, fontWeight, motionPreset, shadow, spacing, } from "./foundations.js";
|
|
3
|
+
import { semanticColors } from "./semantic-colors.js";
|
|
4
|
+
export const sidePanelBehaviorDefaults = {
|
|
5
|
+
modal: true,
|
|
6
|
+
dismissible: true,
|
|
7
|
+
dismissWhileBusy: false,
|
|
8
|
+
escapeDismiss: true,
|
|
9
|
+
outsideDismiss: true,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Same shape and role as `canDismissSheet`, kept as a separate function
|
|
13
|
+
* because the two owning types are not generic over each other (`sheet.ts`
|
|
14
|
+
* is a shared file this module must not edit) and the `modal` branch is
|
|
15
|
+
* genuine new logic, not a copy.
|
|
16
|
+
*/
|
|
17
|
+
export function canDismissSidePanel(reason, busy, policy = sidePanelBehaviorDefaults) {
|
|
18
|
+
if (reason === "programmatic")
|
|
19
|
+
return true;
|
|
20
|
+
if (!policy.dismissible)
|
|
21
|
+
return false;
|
|
22
|
+
if (busy && !policy.dismissWhileBusy)
|
|
23
|
+
return false;
|
|
24
|
+
if (reason === "outside")
|
|
25
|
+
return policy.modal ? policy.outsideDismiss : false;
|
|
26
|
+
if (reason === "escape")
|
|
27
|
+
return policy.escapeDismiss;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* No `createSidePanelLifecycle` counterpart to `createSheetLifecycle`: that
|
|
32
|
+
* machinery exists solely to make Android's persistently-mounted native
|
|
33
|
+
* Modal settle one visible cycle at a time. SidePanel is Web-only, where a
|
|
34
|
+
* plain CSS `transitionend`/exit-callback (the same pattern `sheetRecipe`
|
|
35
|
+
* and `dialogRecipe` already assume for Web) is the whole story — adding a
|
|
36
|
+
* cycle counter here would solve a problem this platform does not have.
|
|
37
|
+
*/
|
|
38
|
+
export const sidePanelRecipe = {
|
|
39
|
+
slots: ["backdrop", "positioner", "content", "header", "title", "body", "footer", "close"],
|
|
40
|
+
defaults: { edge: "end", size: "regular" },
|
|
41
|
+
sizes: { compact: 320, regular: 400, wide: 560 },
|
|
42
|
+
backdrop: backdrop.modal,
|
|
43
|
+
content: {
|
|
44
|
+
background: semanticColors.canvas,
|
|
45
|
+
border: semanticColors.border.default,
|
|
46
|
+
borderWidth: floatingSurfaceContract.borderWidth,
|
|
47
|
+
/** Flush to the viewport edge it docks to — unlike Sheet's floating `xl` radius. */
|
|
48
|
+
radius: null,
|
|
49
|
+
shadow: shadow.overlay,
|
|
50
|
+
paddingHorizontal: spacing.lg,
|
|
51
|
+
paddingTop: spacing.sm,
|
|
52
|
+
paddingBottom: spacing.sm,
|
|
53
|
+
},
|
|
54
|
+
header: { minHeight: control.minTouchTarget, gap: spacing.sm },
|
|
55
|
+
title: {
|
|
56
|
+
color: semanticColors.content.primary,
|
|
57
|
+
textVariant: "title",
|
|
58
|
+
fontWeight: fontWeight.bold,
|
|
59
|
+
},
|
|
60
|
+
body: {
|
|
61
|
+
color: semanticColors.content.body,
|
|
62
|
+
textVariant: "body",
|
|
63
|
+
gap: spacing.md,
|
|
64
|
+
},
|
|
65
|
+
footer: {
|
|
66
|
+
color: semanticColors.content.body,
|
|
67
|
+
textVariant: "body",
|
|
68
|
+
gap: spacing.sm,
|
|
69
|
+
paddingTop: spacing.sm,
|
|
70
|
+
},
|
|
71
|
+
transition: { enter: motionPreset.enter, exit: motionPreset.exit },
|
|
72
|
+
states: { focus: focusIndicatorContract },
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* `web.focus` can only hold one value, but the real behavior branches on
|
|
76
|
+
* `modal`: `"trap"` (default, modal) vs. no trap at all when `modal: false`.
|
|
77
|
+
* The field records the default configuration; the non-modal case is a
|
|
78
|
+
* documented exception in `docs/side-panel.md`, the same way this contract
|
|
79
|
+
* records one representative value elsewhere and pushes edge cases to
|
|
80
|
+
* `scenarios`.
|
|
81
|
+
*/
|
|
82
|
+
export const sidePanelBehavior = {
|
|
83
|
+
controlled: ["open", "defaultOpen", "onOpenChange", "dismissPolicy"],
|
|
84
|
+
defaults: sidePanelBehaviorDefaults,
|
|
85
|
+
configuration: {
|
|
86
|
+
edge: ["start", "end"],
|
|
87
|
+
modal: ["true", "false"],
|
|
88
|
+
},
|
|
89
|
+
stateAxes: {
|
|
90
|
+
availability: ["enabled", "busy"],
|
|
91
|
+
value: ["open"],
|
|
92
|
+
},
|
|
93
|
+
web: {
|
|
94
|
+
roles: ["dialog"],
|
|
95
|
+
keyboard: ["Tab", "Escape"],
|
|
96
|
+
focus: "trap",
|
|
97
|
+
dismiss: ["escape", "outside"],
|
|
98
|
+
},
|
|
99
|
+
native: { roles: [], states: [], actions: [] },
|
|
100
|
+
scenarios: [
|
|
101
|
+
"edge-is-a-logical-start-or-end-direction-never-left-or-right",
|
|
102
|
+
"modal-panels-trap-focus-and-lock-scroll-non-modal-panels-do-neither",
|
|
103
|
+
"non-modal-panels-never-expose-outside-dismiss-the-type-forbids-it",
|
|
104
|
+
"single-dismiss-callback-reports-the-concrete-reason",
|
|
105
|
+
"busy-blocks-user-dismiss-programmatic-owner-close-is-always-allowed",
|
|
106
|
+
"escape-dismisses-in-both-modal-and-non-modal-panels",
|
|
107
|
+
"no-back-or-swipe-dismiss-reason-exists-on-this-web-only-platform",
|
|
108
|
+
"reduced-motion-still-completes-the-exit-callback-once",
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=side-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-panel.js","sourceRoot":"","sources":["../src/side-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { floatingSurfaceContract, focusIndicatorContract } from \"./component-contracts.js\";\nimport {\n backdrop,\n control,\n fontWeight,\n motionPreset,\n shadow,\n spacing,\n type FontWeightValue,\n type TextVariant,\n} from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/** Logical, RTL-aware docking side. `top`/`bottom` stay Sheet's job — see docs/side-panel.md. */\nexport type SidePanelEdge = \"start\" | \"end\";\n\n/**\n * No `\"back\"`/`\"swipe\"`: SidePanel is Web-only (no hardware back, no\n * measured demand for a swipe-to-dismiss drawer). Everything else mirrors\n * `SheetDismissReason` (`src/sheet.ts`) because the *reasons* a panel closes\n * are the same on Web; only which reasons are *permitted* differs by `modal`.\n */\nexport type SidePanelDismissReason = \"close-action\" | \"escape\" | \"outside\" | \"programmatic\";\n\nexport type SidePanelOpenChangeDetails = Readonly<{\n reason: \"trigger\" | SidePanelDismissReason;\n}>;\n\nexport type SidePanelOpenState =\n | Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: SidePanelOpenChangeDetails): void;\n }>\n | Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: SidePanelOpenChangeDetails) => void;\n }>;\n\n/**\n * `modal` is the axis Sheet does not need (Sheet is always modal). A\n * non-modal panel has no backdrop to click \"outside\" of — the rest of the\n * page stays live and interactive — so `outsideDismiss` is not merely\n * defaulted to `false` there, the type forbids it from existing at all.\n * This is the same \"make the invalid combination unrepresentable\" move as\n * `SheetOpenState`'s controlled/uncontrolled split.\n */\nexport type SidePanelDismissPolicy =\n | Readonly<{\n modal: true;\n dismissible: boolean;\n dismissWhileBusy: boolean;\n escapeDismiss: boolean;\n outsideDismiss: boolean;\n }>\n | Readonly<{\n modal: false;\n dismissible: boolean;\n dismissWhileBusy: boolean;\n escapeDismiss: boolean;\n outsideDismiss?: never;\n }>;\n\nexport const sidePanelBehaviorDefaults = {\n modal: true,\n dismissible: true,\n dismissWhileBusy: false,\n escapeDismiss: true,\n outsideDismiss: true,\n} as const satisfies Extract<SidePanelDismissPolicy, { modal: true }>;\n\n/**\n * Same shape and role as `canDismissSheet`, kept as a separate function\n * because the two owning types are not generic over each other (`sheet.ts`\n * is a shared file this module must not edit) and the `modal` branch is\n * genuine new logic, not a copy.\n */\nexport function canDismissSidePanel(\n reason: SidePanelDismissReason,\n busy: boolean,\n policy: SidePanelDismissPolicy = sidePanelBehaviorDefaults,\n): boolean {\n if (reason === \"programmatic\") return true;\n if (!policy.dismissible) return false;\n if (busy && !policy.dismissWhileBusy) return false;\n if (reason === \"outside\") return policy.modal ? policy.outsideDismiss : false;\n if (reason === \"escape\") return policy.escapeDismiss;\n return true;\n}\n\nexport type SidePanelSize = \"compact\" | \"regular\" | \"wide\";\n\n/**\n * No `createSidePanelLifecycle` counterpart to `createSheetLifecycle`: that\n * machinery exists solely to make Android's persistently-mounted native\n * Modal settle one visible cycle at a time. SidePanel is Web-only, where a\n * plain CSS `transitionend`/exit-callback (the same pattern `sheetRecipe`\n * and `dialogRecipe` already assume for Web) is the whole story — adding a\n * cycle counter here would solve a problem this platform does not have.\n */\nexport const sidePanelRecipe = {\n slots: [\"backdrop\", \"positioner\", \"content\", \"header\", \"title\", \"body\", \"footer\", \"close\"] as const,\n defaults: { edge: \"end\", size: \"regular\" },\n sizes: { compact: 320, regular: 400, wide: 560 },\n backdrop: backdrop.modal,\n content: {\n background: semanticColors.canvas,\n border: semanticColors.border.default,\n borderWidth: floatingSurfaceContract.borderWidth,\n /** Flush to the viewport edge it docks to — unlike Sheet's floating `xl` radius. */\n radius: null,\n shadow: shadow.overlay,\n paddingHorizontal: spacing.lg,\n paddingTop: spacing.sm,\n paddingBottom: spacing.sm,\n },\n header: { minHeight: control.minTouchTarget, gap: spacing.sm },\n title: {\n color: semanticColors.content.primary,\n textVariant: \"title\",\n fontWeight: fontWeight.bold,\n },\n body: {\n color: semanticColors.content.body,\n textVariant: \"body\",\n gap: spacing.md,\n },\n footer: {\n color: semanticColors.content.body,\n textVariant: \"body\",\n gap: spacing.sm,\n paddingTop: spacing.sm,\n },\n transition: { enter: motionPreset.enter, exit: motionPreset.exit },\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"backdrop\", \"positioner\", \"content\", \"header\", \"title\", \"body\", \"footer\", \"close\"];\n defaults: { edge: SidePanelEdge; size: SidePanelSize };\n sizes: Record<SidePanelSize, number>;\n backdrop: typeof backdrop.modal;\n content: {\n background: ColorReference;\n border: ColorReference;\n borderWidth: number;\n radius: null;\n shadow: typeof shadow.overlay;\n paddingHorizontal: number;\n paddingTop: number;\n paddingBottom: number;\n };\n header: { minHeight: number; gap: number };\n title: { color: ColorReference; textVariant: TextVariant; fontWeight: FontWeightValue };\n body: { color: ColorReference; textVariant: TextVariant; gap: number };\n footer: { color: ColorReference; textVariant: TextVariant; gap: number; paddingTop: number };\n transition: { enter: typeof motionPreset.enter; exit: typeof motionPreset.exit };\n states: { focus: typeof focusIndicatorContract };\n};\n\n/**\n * `web.focus` can only hold one value, but the real behavior branches on\n * `modal`: `\"trap\"` (default, modal) vs. no trap at all when `modal: false`.\n * The field records the default configuration; the non-modal case is a\n * documented exception in `docs/side-panel.md`, the same way this contract\n * records one representative value elsewhere and pushes edge cases to\n * `scenarios`.\n */\nexport const sidePanelBehavior = {\n controlled: [\"open\", \"defaultOpen\", \"onOpenChange\", \"dismissPolicy\"],\n defaults: sidePanelBehaviorDefaults,\n configuration: {\n edge: [\"start\", \"end\"],\n modal: [\"true\", \"false\"],\n },\n stateAxes: {\n availability: [\"enabled\", \"busy\"],\n value: [\"open\"],\n },\n web: {\n roles: [\"dialog\"],\n keyboard: [\"Tab\", \"Escape\"],\n focus: \"trap\",\n dismiss: [\"escape\", \"outside\"],\n },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"edge-is-a-logical-start-or-end-direction-never-left-or-right\",\n \"modal-panels-trap-focus-and-lock-scroll-non-modal-panels-do-neither\",\n \"non-modal-panels-never-expose-outside-dismiss-the-type-forbids-it\",\n \"single-dismiss-callback-reports-the-concrete-reason\",\n \"busy-blocks-user-dismiss-programmatic-owner-close-is-always-allowed\",\n \"escape-dismisses-in-both-modal-and-non-modal-panels\",\n \"no-back-or-swipe-dismiss-reason-exists-on-this-web-only-platform\",\n \"reduced-motion-still-completes-the-exit-callback-once\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|