@hjmds/design-contracts 1.27.2 → 1.28.1
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/dist/activity-chip.d.ts +1 -1
- package/dist/activity-chip.js +1 -1
- package/dist/activity-chip.js.map +1 -1
- package/dist/auth-screen.d.ts +77 -5
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +63 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/base-recipes.d.ts +1 -1
- package/dist/base-recipes.js +1 -1
- package/dist/base-recipes.js.map +1 -1
- package/dist/behaviors.d.ts +1 -1
- package/dist/breadcrumb.d.ts +1 -1
- package/dist/breadcrumb.js +1 -1
- package/dist/breadcrumb.js.map +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/calendar.js +2 -2
- package/dist/calendar.js.map +1 -1
- package/dist/catalog.d.ts +131 -87
- package/dist/catalog.d.ts.map +1 -1
- package/dist/component-contracts.d.ts +1 -1
- package/dist/component-contracts.js +1 -1
- package/dist/component-contracts.js.map +1 -1
- package/dist/component-recipes.d.ts +85 -57
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +98 -60
- package/dist/component-recipes.js.map +1 -1
- package/dist/conversation-row.d.ts +2 -2
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +6 -4
- package/dist/conversation-row.js.map +1 -1
- package/dist/counter-badge-recipe.d.ts +1 -1
- package/dist/counter-badge-recipe.js +1 -1
- package/dist/counter-badge-recipe.js.map +1 -1
- package/dist/data-table.d.ts +4 -4
- package/dist/data-table.js +4 -4
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +1 -1
- package/dist/design-profile.d.ts.map +1 -1
- package/dist/design-profile.js +19 -16
- package/dist/design-profile.js.map +1 -1
- package/dist/file-picker.d.ts +1 -1
- package/dist/file-picker.js +1 -1
- package/dist/file-picker.js.map +1 -1
- package/dist/foundations.d.ts +18 -14
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +18 -13
- package/dist/foundations.js.map +1 -1
- package/dist/heading.d.ts +13 -13
- package/dist/heading.js.map +1 -1
- package/dist/number-field.d.ts +1 -1
- package/dist/otp-field.d.ts +1 -1
- package/dist/password-field.d.ts +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/result.js +2 -2
- package/dist/result.js.map +1 -1
- package/dist/screen-patterns.d.ts +36 -0
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +20 -0
- package/dist/screen-patterns.js.map +1 -1
- package/dist/semantic-colors.d.ts +10 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +6 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/sheet.d.ts +7 -4
- package/dist/sheet.d.ts.map +1 -1
- package/dist/sheet.js +8 -5
- package/dist/sheet.js.map +1 -1
- package/dist/side-panel.d.ts +1 -1
- package/dist/side-panel.js +2 -2
- package/dist/side-panel.js.map +1 -1
- package/dist/steps.d.ts +1 -1
- package/dist/steps.js +1 -1
- package/dist/steps.js.map +1 -1
- package/dist/tag.d.ts +1 -1
- package/dist/tag.js +1 -1
- package/dist/tag.js.map +1 -1
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/timeline.js.map +1 -1
- package/dist/top.d.ts +4 -4
- package/dist/top.d.ts.map +1 -1
- package/dist/top.js +3 -1
- package/dist/top.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/activity-chip.md +5 -1
- package/docs/auth-screen.md +2 -0
- package/docs/design-profile.md +14 -10
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +9 -5
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/heading.md +14 -9
- package/docs/sheet.md +2 -0
- package/docs/top.md +4 -3
- package/docs/usage/README.md +1 -1
- package/docs/usage/SELECTION.md +1 -0
- package/docs/usage/components/auth-screen-layout.md +44 -11
- package/docs/usage/components/bottom-cta.md +6 -2
- package/docs/usage/components/breadcrumb.md +5 -1
- package/docs/usage/components/chat-screen.md +1 -0
- package/docs/usage/components/checkbox-group.md +5 -1
- package/docs/usage/components/collapsible.md +1 -0
- package/docs/usage/components/comment-thread-screen.md +2 -0
- package/docs/usage/components/dialog.md +1 -0
- package/docs/usage/components/editor-screen.md +1 -0
- package/docs/usage/components/effect-surface.md +2 -2
- package/docs/usage/components/field.md +1 -0
- package/docs/usage/components/heading.md +14 -12
- package/docs/usage/components/list-detail-screen.md +1 -0
- package/docs/usage/components/list-row.md +7 -3
- package/docs/usage/components/list.md +25 -4
- package/docs/usage/components/notification-item.md +5 -1
- package/docs/usage/components/onboarding-screen.md +1 -0
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/screen-layout.md +28 -3
- package/docs/usage/components/search-field.md +1 -0
- package/docs/usage/components/search-screen.md +4 -2
- package/docs/usage/components/section.md +5 -1
- package/docs/usage/components/segmented-control.md +1 -1
- package/docs/usage/components/sheet.md +18 -13
- package/docs/usage/components/skeleton.md +42 -15
- package/docs/usage/components/statistic.md +5 -1
- package/docs/usage/components/switch.md +3 -2
- package/docs/usage/components/tag.md +5 -1
- package/docs/usage/components/text-area.md +1 -0
- package/docs/usage/components/text.md +6 -4
- package/docs/usage/components/top.md +1 -1
- package/docs/usage/compositions/adaptive-content.md +5 -1
- package/docs/usage/compositions/compound-controls.md +2 -1
- package/docs/usage/compositions/design-profile-comparison.md +1 -1
- package/docs/usage/compositions/family-drawer.md +5 -3
- package/docs/usage/compositions/input-sheet.md +1 -1
- package/docs/usage/compositions/interaction-flow-apply.md +3 -3
- package/docs/usage/compositions/photo-source.md +3 -3
- package/docs/usage/compositions/selection-motion.md +5 -1
- package/docs/usage/compositions/visual-foundations.md +5 -4
- package/docs/usage/screens/common-login.md +9 -7
- package/docs/usage/screens/landing.md +15 -14
- package/docs/usage/screens/notification-settings.md +4 -2
- package/docs/usage/tokens/typography.md +21 -13
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-colors.js","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC;IACxB,OAAO,EAAE;QACP,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC;QAC9B,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC;QAChC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC;QACxB,KAAK,EAAE,UAAU,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"semantic-colors.js","sourceRoot":"","sources":["../src/semantic-colors.ts"],"names":[],"mappings":"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","sourcesContent":["import { accentColor, themeColor } from \"./color-references.js\";\n\n/**\n * Stable role aliases above the legacy ThemeColors key set. New recipes should\n * prefer these roles so the underlying theme shape can evolve independently.\n */\nexport const semanticColors = {\n canvas: themeColor(\"bg\"),\n surface: {\n default: themeColor(\"surface\"),\n sunken: themeColor(\"surfaceAlt\"),\n raised: themeColor(\"bg\"),\n brand: themeColor(\"surfaceAccent\"),\n /**\n * Loading placeholder fill (Skeleton, ListRow `loading`). A translucent text tint instead of an opaque\n * neutral: it darkens/lightens whatever it sits on, so it stays visible on canvas, Card surfaces and\n * profile palettes whose `surfaceAlt` is as light as `bg` (2026-10-17 Skeleton review, skeleton.md 근거).\n */\n placeholder: themeColor(\"text\", 0.12),\n },\n content: {\n primary: themeColor(\"text\"),\n body: themeColor(\"textBody\"),\n secondary: themeColor(\"textMuted\"),\n tertiary: themeColor(\"textSub\"),\n decorative: themeColor(\"textWeak\"),\n brand: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n inverse: themeColor(\"onPrimary\"),\n },\n border: {\n subtle: themeColor(\"border\", 0.7),\n default: themeColor(\"border\"),\n /** Resting outline of an interactive control. */\n control: themeColor(\"borderControl\"),\n strong: themeColor(\"textWeak\"),\n focus: themeColor(\"contentBrand\"),\n danger: themeColor(\"danger\"),\n },\n action: {\n brand: {\n background: themeColor(\"primary\"),\n content: themeColor(\"onPrimary\"),\n },\n neutral: {\n background: themeColor(\"bg\"),\n content: themeColor(\"text\"),\n // A control outline is a border role. `textSub` made a resting neutral\n // action read heavier than the selected control beside it.\n border: themeColor(\"borderControl\"),\n },\n danger: {\n background: themeColor(\"dangerFill\"),\n content: themeColor(\"onDanger\"),\n },\n },\n feedback: {\n info: {\n foreground: accentColor(\"info\"),\n background: accentColor(\"info\", 0.1),\n badgeBackground: accentColor(\"info\", 0.1),\n border: accentColor(\"info\", 0.3),\n },\n success: {\n foreground: accentColor(\"success\"),\n background: accentColor(\"success\", 0.1),\n badgeBackground: accentColor(\"success\", 0.1),\n border: accentColor(\"success\", 0.3),\n },\n warning: {\n foreground: accentColor(\"warning\"),\n background: accentColor(\"warning\", 0.1),\n badgeBackground: accentColor(\"warning\", 0.1),\n border: accentColor(\"warning\", 0.3),\n },\n attention: {\n foreground: accentColor(\"attention\"),\n background: accentColor(\"attention\", 0.1),\n badgeBackground: accentColor(\"attention\", 0.1),\n border: accentColor(\"attention\", 0.3),\n },\n danger: {\n foreground: themeColor(\"danger\"),\n background: themeColor(\"danger\", 0.1),\n badgeBackground: themeColor(\"danger\", 0.1),\n border: themeColor(\"danger\", 0.3),\n },\n },\n interaction: {\n hover: themeColor(\"text\", 0.06),\n focus: themeColor(\"contentBrand\", 0.08),\n pressed: themeColor(\"text\", 0.1),\n selected: themeColor(\"primary\", 0.1),\n /**\n * Lower edge of a raised object, composited over its own fill so one shade\n * darkens any brand color (BottomNavigation raised tile, Spint 2026-10-13).\n */\n depth: themeColor(\"text\", 0.45),\n },\n} as const;\n"]}
|
package/dist/sheet.d.ts
CHANGED
|
@@ -32,11 +32,14 @@ export declare const sheetBehaviorDefaults: {
|
|
|
32
32
|
readonly swipeDismiss: true;
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Every placement closes on a backdrop press (2026-10-17 user decision; bottom sheets had been drag-only
|
|
36
|
+
* since 2026-10-16). Bottom sheets still show no × by default: drag, backdrop, Escape/system back and the
|
|
37
|
+
* handle's close action cover pointer, keyboard, switch and screen-reader users. `outsideDismiss: false`
|
|
38
|
+
* (with `swipeDismiss: false`) is only for flows where an accidental close loses work, such as payment
|
|
39
|
+
* confirmation; unsaved-input confirmation belongs in the controlled owner's `onOpenChange`, which every
|
|
40
|
+
* reason, `outside` included, passes through. `placement` stays in the signature for published callers.
|
|
38
41
|
*/
|
|
39
|
-
export declare function resolveSheetDismissPolicy(
|
|
42
|
+
export declare function resolveSheetDismissPolicy(_placement: string, override?: Partial<SheetDismissPolicy>): SheetDismissPolicy;
|
|
40
43
|
export declare function resolveSheetCloseButton(placement: string, showClose?: boolean): boolean;
|
|
41
44
|
/** Release thresholds for the drag-to-dismiss gesture shared by both renderers. */
|
|
42
45
|
export declare const sheetDragRecipe: {
|
package/dist/sheet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACnE,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,sBAAsB,KAAK,IAAI,CAAC;CACxE,CAAC,CAAC;AAEP,eAAO,MAAM,qBAAqB;;;;;;CAOK,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,cAAc,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;CACxC,CAAC,CAAC;AAEH,iFAAiF;AACjF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACnE,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,sBAAsB,KAAK,IAAI,CAAC;CACxE,CAAC,CAAC;AAEP,eAAO,MAAM,qBAAqB;;;;;;CAOK,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,kBAAkB,CAE5H;AACD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAEvF;AAED,mFAAmF;AACnF,eAAO,MAAM,eAAe;;;IAG1B,8EAA8E;;CAEtE,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,kBAA0C,GACjD,OAAO,CAUT;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,IAAI,IAAI,MAAM,CAAC;IACf,YAAY,IAAI,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CACV,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IACX,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,gBAAgB,UAAQ,GACvB,cAAc,CAgChB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtD,eAAO,MAAM,gBAAgB,sCAAwE,CAAC;AAEtG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAsB1E;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,QAAQ,GAAG,UAAU,GAC/B,WAAW,GAAG,IAAI,CASpB"}
|
package/dist/sheet.js
CHANGED
|
@@ -7,12 +7,15 @@ export const sheetBehaviorDefaults = {
|
|
|
7
7
|
swipeDismiss: true,
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Every placement closes on a backdrop press (2026-10-17 user decision; bottom sheets had been drag-only
|
|
11
|
+
* since 2026-10-16). Bottom sheets still show no × by default: drag, backdrop, Escape/system back and the
|
|
12
|
+
* handle's close action cover pointer, keyboard, switch and screen-reader users. `outsideDismiss: false`
|
|
13
|
+
* (with `swipeDismiss: false`) is only for flows where an accidental close loses work, such as payment
|
|
14
|
+
* confirmation; unsaved-input confirmation belongs in the controlled owner's `onOpenChange`, which every
|
|
15
|
+
* reason, `outside` included, passes through. `placement` stays in the signature for published callers.
|
|
13
16
|
*/
|
|
14
|
-
export function resolveSheetDismissPolicy(
|
|
15
|
-
return { ...sheetBehaviorDefaults, ...
|
|
17
|
+
export function resolveSheetDismissPolicy(_placement, override = {}) {
|
|
18
|
+
return { ...sheetBehaviorDefaults, ...override };
|
|
16
19
|
}
|
|
17
20
|
export function resolveSheetCloseButton(placement, showClose) {
|
|
18
21
|
return showClose ?? placement !== "bottom";
|
package/dist/sheet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.js","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"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","sourcesContent":["/** Why an open Sheet is being asked to close. */\nexport type SheetDismissReason =\n | \"close-action\"\n | \"escape\"\n | \"back\"\n | \"outside\"\n | \"swipe\"\n | \"programmatic\";\n\n/**\n * `trigger` only opens a Sheet. Every close reports the concrete dismiss reason\n * so renderers and product analytics do not have to infer it from platform events.\n */\nexport type SheetOpenChangeDetails = Readonly<{\n reason: \"trigger\" | SheetDismissReason;\n}>;\n\n/** User-dismiss policy. A controlled owner may always close programmatically. */\nexport type SheetDismissPolicy = Readonly<{\n dismissible: boolean;\n dismissWhileBusy: boolean;\n outsideDismiss: boolean;\n escapeOrBackDismiss: boolean;\n swipeDismiss: boolean;\n}>;\n\nexport type SheetOpenState =\n | Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: SheetOpenChangeDetails): void;\n }>\n | Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: SheetOpenChangeDetails) => void;\n }>;\n\nexport const sheetBehaviorDefaults = {\n dismissible: true,\n dismissWhileBusy: false,\n outsideDismiss: true,\n escapeOrBackDismiss: true,\n // 2026-10-16 user decision: dragging a bottom sheet down is a required way to close it.\n swipeDismiss: true,\n} as const satisfies SheetDismissPolicy;\n\n/**\n * Bottom sheets close by dragging only (2026-10-16 user decision): no backdrop tap and no visible ×\n * by default. Escape/system back and the handle's close action stay so keyboard, switch and\n * screen-reader users can still dismiss without a drag gesture. Other placements keep the old defaults.\n */\nexport function resolveSheetDismissPolicy(placement: string, override: Partial<SheetDismissPolicy> = {}): SheetDismissPolicy {\n return { ...sheetBehaviorDefaults, ...(placement === \"bottom\" ? { outsideDismiss: false } : {}), ...override };\n}\nexport function resolveSheetCloseButton(placement: string, showClose?: boolean): boolean {\n return showClose ?? placement !== \"bottom\";\n}\n\n/** Release thresholds for the drag-to-dismiss gesture shared by both renderers. */\nexport const sheetDragRecipe = {\n activationDistance: 6,\n dismissDistanceRatio: 0.25,\n /** px per millisecond: a quick flick closes before the distance threshold. */\n dismissVelocity: 0.5,\n} as const;\n\n/**\n * Resolves an attempted close without platform knowledge. Programmatic close is\n * intentionally an owner override: a controlled `open={false}` cannot be vetoed\n * by a renderer, even while the Sheet is busy or not user-dismissible.\n */\nexport function canDismissSheet(\n reason: SheetDismissReason,\n busy: boolean,\n policy: SheetDismissPolicy = sheetBehaviorDefaults,\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.outsideDismiss;\n if (reason === \"escape\" || reason === \"back\") {\n return policy.escapeOrBackDismiss;\n }\n if (reason === \"swipe\") return policy.swipeDismiss;\n return true;\n}\n\nexport type SheetLifecycle = Readonly<{\n open(): number;\n beginDismiss(): number | null;\n requestClose(\n reason: SheetDismissReason,\n busy: boolean,\n policy?: SheetDismissPolicy,\n ): boolean;\n completeDismiss(cycle: number): boolean;\n}>;\n\n/**\n * Makes persistent native Modal renderers settle each visible cycle once.\n * Platform adapters still decide when native dismissal has actually completed.\n */\nexport function createSheetLifecycle(\n initiallyVisible = false,\n): SheetLifecycle {\n let sequence = initiallyVisible ? 1 : 0;\n let activeCycle: number | null = initiallyVisible ? sequence : null;\n let closeRequested = false;\n const dismissingCycles = new Set<number>();\n\n return {\n open() {\n if (activeCycle !== null) return activeCycle;\n sequence += 1;\n activeCycle = sequence;\n closeRequested = false;\n return activeCycle;\n },\n beginDismiss() {\n if (activeCycle === null) return null;\n const cycle = activeCycle;\n activeCycle = null;\n closeRequested = false;\n dismissingCycles.add(cycle);\n return cycle;\n },\n requestClose(reason, busy, policy = sheetBehaviorDefaults) {\n if (activeCycle === null || closeRequested) return false;\n if (!canDismissSheet(reason, busy, policy)) return false;\n closeRequested = true;\n return true;\n },\n completeDismiss(cycle) {\n return dismissingCycles.delete(cycle);\n },\n };\n}\n\n/**\n * 사용자가 시트 높이를 바꾸는 축이다. `sheetRecipe.sizes`는 제품이 여는 높이를 정하고,\n * 이것은 열린 뒤 **사용자가** 단계를 옮기는 문제다 — 지도 위 목록처럼 \"조금 보기 ↔ 크게\n * 보기\"가 필요한 화면이 그 자리다.\n *\n * `auto`는 단계에 들어가지 않는다. 내용이 정하는 높이라 \"다음 단계\"가 정의되지 않는다.\n *\n * **드래그 물리는 여기 없다.** 이 계약은 단계 목록과 \"다음/이전 단계가 무엇인가\"만 갖고,\n * 손가락 속도·관성·고무줄은 넣지 않았다 — 실제 제품 화면 없이 그 값을 정하면 기기에서\n * 다시 다 고치게 된다. 대신 **핸들이 접근 가능한 컨트롤**이라 키보드·보조기기에서도\n * 단계를 옮길 수 있다. 제스처는 제품이 얹고, 결과를 같은 `onDetentChange`로 보낸다.\n */\nexport type SheetDetent = \"medium\" | \"large\" | \"full\";\n\nexport const sheetDetentOrder = [\"medium\", \"large\", \"full\"] as const satisfies readonly SheetDetent[];\n\nexport function validateSheetDetents(detents: readonly SheetDetent[]): void {\n if (detents.length === 0) {\n throw new RangeError(\"Sheet detents must not be empty\");\n }\n const seen = new Set<SheetDetent>();\n for (const detent of detents) {\n if (!sheetDetentOrder.includes(detent)) {\n throw new TypeError(`Unsupported Sheet detent: ${String(detent)}`);\n }\n if (seen.has(detent)) throw new TypeError(`Duplicate Sheet detent: ${detent}`);\n seen.add(detent);\n }\n const ordered = [...detents].sort(\n (left, right) => sheetDetentOrder.indexOf(left) - sheetDetentOrder.indexOf(right),\n );\n if (ordered.some((detent, index) => detent !== detents[index])) {\n /*\n 정렬을 대신 해 주지 않는 이유: 순서가 곧 \"다음 단계\"의 의미라서, 제품이 적은 순서와\n 실제 이동 순서가 다르면 디버깅할 수 없는 화면이 된다.\n */\n throw new TypeError(\"Sheet detents must be listed from smallest to largest\");\n }\n}\n\nexport function resolveNextSheetDetent(\n detents: readonly SheetDetent[],\n current: SheetDetent,\n direction: \"expand\" | \"collapse\",\n): SheetDetent | null {\n validateSheetDetents(detents);\n const index = detents.indexOf(current);\n if (index === -1) {\n throw new RangeError(`Sheet current detent is not in the list: ${current}`);\n }\n const next = direction === \"expand\" ? index + 1 : index - 1;\n /** 끝에서는 `null`이다 — renderer가 \"더 갈 수 없다\"를 컨트롤 상태로 표현한다. */\n return next < 0 || next >= detents.length ? null : detents[next]!;\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"sheet.js","sourceRoot":"","sources":["../src/sheet.ts"],"names":[],"mappings":"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","sourcesContent":["/** Why an open Sheet is being asked to close. */\nexport type SheetDismissReason =\n | \"close-action\"\n | \"escape\"\n | \"back\"\n | \"outside\"\n | \"swipe\"\n | \"programmatic\";\n\n/**\n * `trigger` only opens a Sheet. Every close reports the concrete dismiss reason\n * so renderers and product analytics do not have to infer it from platform events.\n */\nexport type SheetOpenChangeDetails = Readonly<{\n reason: \"trigger\" | SheetDismissReason;\n}>;\n\n/** User-dismiss policy. A controlled owner may always close programmatically. */\nexport type SheetDismissPolicy = Readonly<{\n dismissible: boolean;\n dismissWhileBusy: boolean;\n outsideDismiss: boolean;\n escapeOrBackDismiss: boolean;\n swipeDismiss: boolean;\n}>;\n\nexport type SheetOpenState =\n | Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: SheetOpenChangeDetails): void;\n }>\n | Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: SheetOpenChangeDetails) => void;\n }>;\n\nexport const sheetBehaviorDefaults = {\n dismissible: true,\n dismissWhileBusy: false,\n outsideDismiss: true,\n escapeOrBackDismiss: true,\n // 2026-10-16 user decision: dragging a bottom sheet down is a required way to close it.\n swipeDismiss: true,\n} as const satisfies SheetDismissPolicy;\n\n/**\n * Every placement closes on a backdrop press (2026-10-17 user decision; bottom sheets had been drag-only\n * since 2026-10-16). Bottom sheets still show no × by default: drag, backdrop, Escape/system back and the\n * handle's close action cover pointer, keyboard, switch and screen-reader users. `outsideDismiss: false`\n * (with `swipeDismiss: false`) is only for flows where an accidental close loses work, such as payment\n * confirmation; unsaved-input confirmation belongs in the controlled owner's `onOpenChange`, which every\n * reason, `outside` included, passes through. `placement` stays in the signature for published callers.\n */\nexport function resolveSheetDismissPolicy(_placement: string, override: Partial<SheetDismissPolicy> = {}): SheetDismissPolicy {\n return { ...sheetBehaviorDefaults, ...override };\n}\nexport function resolveSheetCloseButton(placement: string, showClose?: boolean): boolean {\n return showClose ?? placement !== \"bottom\";\n}\n\n/** Release thresholds for the drag-to-dismiss gesture shared by both renderers. */\nexport const sheetDragRecipe = {\n activationDistance: 6,\n dismissDistanceRatio: 0.25,\n /** px per millisecond: a quick flick closes before the distance threshold. */\n dismissVelocity: 0.5,\n} as const;\n\n/**\n * Resolves an attempted close without platform knowledge. Programmatic close is\n * intentionally an owner override: a controlled `open={false}` cannot be vetoed\n * by a renderer, even while the Sheet is busy or not user-dismissible.\n */\nexport function canDismissSheet(\n reason: SheetDismissReason,\n busy: boolean,\n policy: SheetDismissPolicy = sheetBehaviorDefaults,\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.outsideDismiss;\n if (reason === \"escape\" || reason === \"back\") {\n return policy.escapeOrBackDismiss;\n }\n if (reason === \"swipe\") return policy.swipeDismiss;\n return true;\n}\n\nexport type SheetLifecycle = Readonly<{\n open(): number;\n beginDismiss(): number | null;\n requestClose(\n reason: SheetDismissReason,\n busy: boolean,\n policy?: SheetDismissPolicy,\n ): boolean;\n completeDismiss(cycle: number): boolean;\n}>;\n\n/**\n * Makes persistent native Modal renderers settle each visible cycle once.\n * Platform adapters still decide when native dismissal has actually completed.\n */\nexport function createSheetLifecycle(\n initiallyVisible = false,\n): SheetLifecycle {\n let sequence = initiallyVisible ? 1 : 0;\n let activeCycle: number | null = initiallyVisible ? sequence : null;\n let closeRequested = false;\n const dismissingCycles = new Set<number>();\n\n return {\n open() {\n if (activeCycle !== null) return activeCycle;\n sequence += 1;\n activeCycle = sequence;\n closeRequested = false;\n return activeCycle;\n },\n beginDismiss() {\n if (activeCycle === null) return null;\n const cycle = activeCycle;\n activeCycle = null;\n closeRequested = false;\n dismissingCycles.add(cycle);\n return cycle;\n },\n requestClose(reason, busy, policy = sheetBehaviorDefaults) {\n if (activeCycle === null || closeRequested) return false;\n if (!canDismissSheet(reason, busy, policy)) return false;\n closeRequested = true;\n return true;\n },\n completeDismiss(cycle) {\n return dismissingCycles.delete(cycle);\n },\n };\n}\n\n/**\n * 사용자가 시트 높이를 바꾸는 축이다. `sheetRecipe.sizes`는 제품이 여는 높이를 정하고,\n * 이것은 열린 뒤 **사용자가** 단계를 옮기는 문제다 — 지도 위 목록처럼 \"조금 보기 ↔ 크게\n * 보기\"가 필요한 화면이 그 자리다.\n *\n * `auto`는 단계에 들어가지 않는다. 내용이 정하는 높이라 \"다음 단계\"가 정의되지 않는다.\n *\n * **드래그 물리는 여기 없다.** 이 계약은 단계 목록과 \"다음/이전 단계가 무엇인가\"만 갖고,\n * 손가락 속도·관성·고무줄은 넣지 않았다 — 실제 제품 화면 없이 그 값을 정하면 기기에서\n * 다시 다 고치게 된다. 대신 **핸들이 접근 가능한 컨트롤**이라 키보드·보조기기에서도\n * 단계를 옮길 수 있다. 제스처는 제품이 얹고, 결과를 같은 `onDetentChange`로 보낸다.\n */\nexport type SheetDetent = \"medium\" | \"large\" | \"full\";\n\nexport const sheetDetentOrder = [\"medium\", \"large\", \"full\"] as const satisfies readonly SheetDetent[];\n\nexport function validateSheetDetents(detents: readonly SheetDetent[]): void {\n if (detents.length === 0) {\n throw new RangeError(\"Sheet detents must not be empty\");\n }\n const seen = new Set<SheetDetent>();\n for (const detent of detents) {\n if (!sheetDetentOrder.includes(detent)) {\n throw new TypeError(`Unsupported Sheet detent: ${String(detent)}`);\n }\n if (seen.has(detent)) throw new TypeError(`Duplicate Sheet detent: ${detent}`);\n seen.add(detent);\n }\n const ordered = [...detents].sort(\n (left, right) => sheetDetentOrder.indexOf(left) - sheetDetentOrder.indexOf(right),\n );\n if (ordered.some((detent, index) => detent !== detents[index])) {\n /*\n 정렬을 대신 해 주지 않는 이유: 순서가 곧 \"다음 단계\"의 의미라서, 제품이 적은 순서와\n 실제 이동 순서가 다르면 디버깅할 수 없는 화면이 된다.\n */\n throw new TypeError(\"Sheet detents must be listed from smallest to largest\");\n }\n}\n\nexport function resolveNextSheetDetent(\n detents: readonly SheetDetent[],\n current: SheetDetent,\n direction: \"expand\" | \"collapse\",\n): SheetDetent | null {\n validateSheetDetents(detents);\n const index = detents.indexOf(current);\n if (index === -1) {\n throw new RangeError(`Sheet current detent is not in the list: ${current}`);\n }\n const next = direction === \"expand\" ? index + 1 : index - 1;\n /** 끝에서는 `null`이다 — renderer가 \"더 갈 수 없다\"를 컨트롤 상태로 표현한다. */\n return next < 0 || next >= detents.length ? null : detents[next]!;\n}\n\n"]}
|
package/dist/side-panel.d.ts
CHANGED
package/dist/side-panel.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { floatingSurfaceContract, focusIndicatorContract } from "./component-contracts.js";
|
|
2
|
-
import { backdrop, control,
|
|
2
|
+
import { backdrop, control, motionPreset, shadow, spacing, typography, } from "./foundations.js";
|
|
3
3
|
import { semanticColors } from "./semantic-colors.js";
|
|
4
4
|
export const sidePanelBehaviorDefaults = {
|
|
5
5
|
modal: true,
|
|
@@ -55,7 +55,7 @@ export const sidePanelRecipe = {
|
|
|
55
55
|
title: {
|
|
56
56
|
color: semanticColors.content.primary,
|
|
57
57
|
textVariant: "title",
|
|
58
|
-
fontWeight: fontWeight
|
|
58
|
+
fontWeight: typography.title.fontWeight,
|
|
59
59
|
},
|
|
60
60
|
body: {
|
|
61
61
|
color: semanticColors.content.body,
|
package/dist/side-panel.js.map
CHANGED
|
@@ -1 +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"]}
|
|
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 motionPreset,\n shadow,\n spacing,\n typography,\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: typography.title.fontWeight,\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"]}
|
package/dist/steps.d.ts
CHANGED
package/dist/steps.js
CHANGED
package/dist/steps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { fontWeight, glyph, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Ant Design Steps' full surface (navigation/clickable jump, dot type, custom\n * per-item icon, vertical orientation) is intentionally not reproduced. Both\n * known consumers (Yajalal onboarding, BurnTok sign-up) are single-cursor,\n * horizontal, back-button-driven wizards; see docs/steps.md for the per-axis\n * rationale.\n */\nexport type StepStatus = \"pending\" | \"current\" | \"complete\" | \"error\";\n\n/** Only the cursor step can be \"current\" or \"error\"; every other step is derived. */\n// \"complete\" marks a finished flow; see docs/steps.md.\nexport type StepCursorStatus = Extract<StepStatus, \"current\" | \"error\" | \"complete\">;\n\nexport type StepItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n description?: string;\n}>;\n\n/**\n * A single stable cursor (`currentStepId`) is the source of truth instead of a\n * per-item status array, so \"two current steps\" or \"a completed step past an\n * unresolved error\" cannot be represented. Clickable jump is out of scope, so\n * the flow is always linear relative to the cursor.\n */\nexport type StepsDescriptor<Id extends string = string> = Readonly<{\n steps: readonly StepItemDescriptor<Id>[];\n currentStepId: Id;\n currentStepStatus?: StepCursorStatus;\n}>;\n\nexport const stepsDefaults = {\n currentStepStatus: \"current\",\n} as const satisfies Readonly<{ currentStepStatus: StepCursorStatus }>;\n\nexport type StepsStatusLabels = Readonly<{\n pending: string;\n current: string;\n complete: string;\n error: string;\n}>;\n\nexport type StepsAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar (Korean\n * \"3단계 중 2단계\" does not translate structurally to English \"step 2 of 3\"),\n * so the resolver invokes this composer instead of assembling copy itself.\n */\nexport type ComposeStepsAccessibleName = (\n info: StepsAccessibleNameInfo,\n) => string;\n\nexport type ResolvedStepDescriptor<Id extends string = string> =\n StepItemDescriptor<Id> &\n Readonly<{\n status: StepStatus;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Primary accessible name: order + label. Never says status by itself. */\n accessibleName: string;\n /** Supplementary status announcement (Web aria-describedby text / RN accessibilityHint). */\n statusLabel: string;\n }>;\n\nexport type ResolveStepsOptions = Readonly<{\n statusLabels: StepsStatusLabels;\n composeAccessibleName: ComposeStepsAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Steps ${field} must not be empty`);\n }\n}\n\nexport function validateStepItemDescriptor<Id extends string>(\n step: StepItemDescriptor<Id>,\n): void {\n assertNonEmpty(step.id, \"item id\");\n if (step.id !== step.id.trim()) {\n throw new TypeError(\"Steps item id must not start or end with whitespace\");\n }\n assertNonEmpty(step.label, \"item label\");\n if (step.description !== undefined) {\n assertNonEmpty(step.description, \"item description\");\n }\n}\n\nexport function validateStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n): void {\n if (descriptor.steps.length < 2) {\n throw new RangeError(\"Steps must contain at least two steps\");\n }\n const ids = new Set<Id>();\n for (const step of descriptor.steps) {\n validateStepItemDescriptor(step);\n if (ids.has(step.id)) {\n throw new TypeError(`Duplicate Steps item id: ${step.id}`);\n }\n ids.add(step.id);\n }\n if (!ids.has(descriptor.currentStepId)) {\n throw new RangeError(\n `Steps currentStepId does not match any step id: ${String(descriptor.currentStepId)}`,\n );\n }\n if (\n descriptor.currentStepStatus !== undefined &&\n descriptor.currentStepStatus !== \"current\" &&\n descriptor.currentStepStatus !== \"error\" &&\n descriptor.currentStepStatus !== \"complete\"\n ) {\n throw new TypeError(\n `Unsupported Steps currentStepStatus: ${String(descriptor.currentStepStatus)}`,\n );\n }\n}\n\nexport function validateStepsStatusLabels(labels: StepsStatusLabels): void {\n if (labels === null || typeof labels !== \"object\") {\n throw new TypeError(\"Steps statusLabels must be an object\");\n }\n for (const key of [\"pending\", \"current\", \"complete\", \"error\"] as const) {\n const value = labels[key];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Steps statusLabels.${key} must not be empty`);\n }\n }\n}\n\n/** A step is visually and semantically \"reached\" once it is no longer waiting. */\nexport function isStepReached(status: StepStatus): boolean {\n return status !== \"pending\";\n}\n\n/**\n * Derives every step's status from its position relative to the single\n * cursor, then attaches the order-preserving accessible name and the\n * supplementary status announcement. Throws on a malformed descriptor,\n * labels bag, or composer instead of silently rendering an inconsistent flow.\n */\nexport function resolveStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n options: ResolveStepsOptions,\n): readonly ResolvedStepDescriptor<Id>[] {\n validateStepsDescriptor(descriptor);\n validateStepsStatusLabels(options.statusLabels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Steps composeAccessibleName must be a function\");\n }\n\n const currentStepStatus =\n descriptor.currentStepStatus ?? stepsDefaults.currentStepStatus;\n const total = descriptor.steps.length;\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n return descriptor.steps.map((step, index) => {\n const status: StepStatus =\n index < cursorIndex\n ? \"complete\"\n : index > cursorIndex\n ? \"pending\"\n : currentStepStatus;\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: step.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Steps composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...step,\n status,\n position,\n total,\n accessibleName,\n statusLabel: options.statusLabels[status],\n };\n });\n}\n\nexport const stepsRecipe = {\n slots: [\n \"root\",\n \"step\",\n \"indicator\",\n \"marker\",\n \"connector\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.xs,\n indicator: {\n size: glyph.md,\n borderWidth: stroke.default,\n activeBorderWidth: stroke.strong,\n /** null means the renderer shows the step's own position number instead of a glyph. */\n marks: {\n pending: null,\n current: null,\n complete: \"check\",\n error: \"error\",\n },\n border: {\n pending: semanticColors.border.default,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.border,\n error: semanticColors.feedback.danger.border,\n },\n background: {\n pending: null,\n current: null,\n complete: semanticColors.feedback.success.badgeBackground,\n error: semanticColors.feedback.danger.badgeBackground,\n },\n content: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.foreground,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n connector: {\n height: stroke.default,\n tone: {\n reached: semanticColors.content.brand,\n unreached: semanticColors.border.default,\n },\n },\n label: {\n textVariant: \"label\",\n fontWeight: fontWeight.semibold,\n color: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.primary,\n complete: semanticColors.content.primary,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n description: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n indicator: {\n size: number;\n borderWidth: number;\n activeBorderWidth: number;\n marks: Record<StepStatus, SemanticIconName | null>;\n border: Record<StepStatus, ColorReference>;\n background: Record<StepStatus, ColorReference | null>;\n content: Record<StepStatus, ColorReference>;\n };\n connector: {\n height: number;\n tone: Record<\"reached\" | \"unreached\", ColorReference>;\n };\n label: {\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n color: Record<StepStatus, ColorReference>;\n };\n description: {\n textVariant: TextVariant;\n color: ColorReference;\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { fontWeight, glyph, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Ant Design Steps' full surface (navigation/clickable jump, dot type, custom\n * per-item icon, vertical orientation) is intentionally not reproduced. Both\n * known consumers (Yajalal onboarding, BurnTok sign-up) are single-cursor,\n * horizontal, back-button-driven wizards; see docs/steps.md for the per-axis\n * rationale.\n */\nexport type StepStatus = \"pending\" | \"current\" | \"complete\" | \"error\";\n\n/** Only the cursor step can be \"current\" or \"error\"; every other step is derived. */\n// \"complete\" marks a finished flow; see docs/steps.md.\nexport type StepCursorStatus = Extract<StepStatus, \"current\" | \"error\" | \"complete\">;\n\nexport type StepItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n description?: string;\n}>;\n\n/**\n * A single stable cursor (`currentStepId`) is the source of truth instead of a\n * per-item status array, so \"two current steps\" or \"a completed step past an\n * unresolved error\" cannot be represented. Clickable jump is out of scope, so\n * the flow is always linear relative to the cursor.\n */\nexport type StepsDescriptor<Id extends string = string> = Readonly<{\n steps: readonly StepItemDescriptor<Id>[];\n currentStepId: Id;\n currentStepStatus?: StepCursorStatus;\n}>;\n\nexport const stepsDefaults = {\n currentStepStatus: \"current\",\n} as const satisfies Readonly<{ currentStepStatus: StepCursorStatus }>;\n\nexport type StepsStatusLabels = Readonly<{\n pending: string;\n current: string;\n complete: string;\n error: string;\n}>;\n\nexport type StepsAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar (Korean\n * \"3단계 중 2단계\" does not translate structurally to English \"step 2 of 3\"),\n * so the resolver invokes this composer instead of assembling copy itself.\n */\nexport type ComposeStepsAccessibleName = (\n info: StepsAccessibleNameInfo,\n) => string;\n\nexport type ResolvedStepDescriptor<Id extends string = string> =\n StepItemDescriptor<Id> &\n Readonly<{\n status: StepStatus;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Primary accessible name: order + label. Never says status by itself. */\n accessibleName: string;\n /** Supplementary status announcement (Web aria-describedby text / RN accessibilityHint). */\n statusLabel: string;\n }>;\n\nexport type ResolveStepsOptions = Readonly<{\n statusLabels: StepsStatusLabels;\n composeAccessibleName: ComposeStepsAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Steps ${field} must not be empty`);\n }\n}\n\nexport function validateStepItemDescriptor<Id extends string>(\n step: StepItemDescriptor<Id>,\n): void {\n assertNonEmpty(step.id, \"item id\");\n if (step.id !== step.id.trim()) {\n throw new TypeError(\"Steps item id must not start or end with whitespace\");\n }\n assertNonEmpty(step.label, \"item label\");\n if (step.description !== undefined) {\n assertNonEmpty(step.description, \"item description\");\n }\n}\n\nexport function validateStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n): void {\n if (descriptor.steps.length < 2) {\n throw new RangeError(\"Steps must contain at least two steps\");\n }\n const ids = new Set<Id>();\n for (const step of descriptor.steps) {\n validateStepItemDescriptor(step);\n if (ids.has(step.id)) {\n throw new TypeError(`Duplicate Steps item id: ${step.id}`);\n }\n ids.add(step.id);\n }\n if (!ids.has(descriptor.currentStepId)) {\n throw new RangeError(\n `Steps currentStepId does not match any step id: ${String(descriptor.currentStepId)}`,\n );\n }\n if (\n descriptor.currentStepStatus !== undefined &&\n descriptor.currentStepStatus !== \"current\" &&\n descriptor.currentStepStatus !== \"error\" &&\n descriptor.currentStepStatus !== \"complete\"\n ) {\n throw new TypeError(\n `Unsupported Steps currentStepStatus: ${String(descriptor.currentStepStatus)}`,\n );\n }\n}\n\nexport function validateStepsStatusLabels(labels: StepsStatusLabels): void {\n if (labels === null || typeof labels !== \"object\") {\n throw new TypeError(\"Steps statusLabels must be an object\");\n }\n for (const key of [\"pending\", \"current\", \"complete\", \"error\"] as const) {\n const value = labels[key];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Steps statusLabels.${key} must not be empty`);\n }\n }\n}\n\n/** A step is visually and semantically \"reached\" once it is no longer waiting. */\nexport function isStepReached(status: StepStatus): boolean {\n return status !== \"pending\";\n}\n\n/**\n * Derives every step's status from its position relative to the single\n * cursor, then attaches the order-preserving accessible name and the\n * supplementary status announcement. Throws on a malformed descriptor,\n * labels bag, or composer instead of silently rendering an inconsistent flow.\n */\nexport function resolveStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n options: ResolveStepsOptions,\n): readonly ResolvedStepDescriptor<Id>[] {\n validateStepsDescriptor(descriptor);\n validateStepsStatusLabels(options.statusLabels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Steps composeAccessibleName must be a function\");\n }\n\n const currentStepStatus =\n descriptor.currentStepStatus ?? stepsDefaults.currentStepStatus;\n const total = descriptor.steps.length;\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n return descriptor.steps.map((step, index) => {\n const status: StepStatus =\n index < cursorIndex\n ? \"complete\"\n : index > cursorIndex\n ? \"pending\"\n : currentStepStatus;\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: step.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Steps composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...step,\n status,\n position,\n total,\n accessibleName,\n statusLabel: options.statusLabels[status],\n };\n });\n}\n\nexport const stepsRecipe = {\n slots: [\n \"root\",\n \"step\",\n \"indicator\",\n \"marker\",\n \"connector\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.xs,\n indicator: {\n size: glyph.md,\n borderWidth: stroke.default,\n activeBorderWidth: stroke.strong,\n /** null means the renderer shows the step's own position number instead of a glyph. */\n marks: {\n pending: null,\n current: null,\n complete: \"check\",\n error: \"error\",\n },\n border: {\n pending: semanticColors.border.default,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.border,\n error: semanticColors.feedback.danger.border,\n },\n background: {\n pending: null,\n current: null,\n complete: semanticColors.feedback.success.badgeBackground,\n error: semanticColors.feedback.danger.badgeBackground,\n },\n content: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.foreground,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n connector: {\n height: stroke.default,\n tone: {\n reached: semanticColors.content.brand,\n unreached: semanticColors.border.default,\n },\n },\n label: {\n textVariant: \"label\",\n fontWeight: fontWeight.medium,\n color: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.primary,\n complete: semanticColors.content.primary,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n description: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n indicator: {\n size: number;\n borderWidth: number;\n activeBorderWidth: number;\n marks: Record<StepStatus, SemanticIconName | null>;\n border: Record<StepStatus, ColorReference>;\n background: Record<StepStatus, ColorReference | null>;\n content: Record<StepStatus, ColorReference>;\n };\n connector: {\n height: number;\n tone: Record<\"reached\" | \"unreached\", ColorReference>;\n };\n label: {\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n color: Record<StepStatus, ColorReference>;\n };\n description: {\n textVariant: TextVariant;\n color: ColorReference;\n };\n};\n"]}
|
package/dist/tag.d.ts
CHANGED
|
@@ -121,7 +121,7 @@ export declare const tagRecipe: {
|
|
|
121
121
|
readonly paddingHorizontal: 4;
|
|
122
122
|
readonly gap: 4;
|
|
123
123
|
readonly textVariant: "caption";
|
|
124
|
-
readonly fontWeight: "
|
|
124
|
+
readonly fontWeight: "500";
|
|
125
125
|
};
|
|
126
126
|
readonly radius: "sm";
|
|
127
127
|
readonly borderWidth: 1;
|
package/dist/tag.js
CHANGED
package/dist/tag.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveColorReference,\n type ColorReferencePalette,\n} from \"./color-references.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\n\nexport type TagTone = \"neutral\" | \"info\" | \"success\" | \"attention\" | \"brand\";\n\nexport const tagDefaults = {\n tone: \"neutral\",\n} as const satisfies Readonly<{ tone: TagTone }>;\n\n/**\n * A single piece of static metadata (a position, a grade, a season). Tag has\n * no interaction axis at all — no selected, no pressed, no closable. Removing\n * or toggling a tag is deselection, which belongs to `Chip`.\n */\nexport type TagDescriptor = Readonly<{\n label: string;\n tone?: TagTone;\n}>;\n\nexport type ResolvedTagDescriptor = Readonly<{\n label: string;\n tone: TagTone;\n}>;\n\nfunction assertCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Tag ${field} must not be empty`);\n }\n}\n\nconst supportedTones: readonly TagTone[] = [\n \"neutral\",\n \"info\",\n \"success\",\n \"attention\",\n \"brand\",\n];\n\nexport function validateTagDescriptor(descriptor: TagDescriptor): void {\n assertCopy(descriptor.label, \"label\");\n if (\n descriptor.tone !== undefined &&\n !supportedTones.includes(descriptor.tone)\n ) {\n throw new TypeError(`Unsupported Tag tone: ${String(descriptor.tone)}`);\n }\n}\n\nexport function resolveTagDescriptor(\n descriptor: TagDescriptor,\n): ResolvedTagDescriptor {\n validateTagDescriptor(descriptor);\n return {\n label: descriptor.label,\n tone: descriptor.tone ?? tagDefaults.tone,\n };\n}\n\n/**\n * Rectangular, not the pill `radius.full` that `chipRecipe` and `badgeRecipe`\n * use, so a static Tag never reads as a pressable Chip or a status Badge at a\n * glance. No `states` and no `focus` contract: nothing here is ever pressed\n * or focused.\n */\nexport const tagRecipe = {\n slots: [\"root\", \"label\"] as const,\n defaults: tagDefaults,\n tones: {\n // Same as badgeRecipe: a canvas plate without a border vanished on the canvas (2026-10-17 review E3).\n neutral: {\n background: semanticColors.canvas,\n content: semanticColors.content.secondary,\n border: semanticColors.border.default,\n },\n brand: {\n background: semanticColors.canvas,\n content: semanticColors.content.brand,\n border: semanticColors.border.default,\n },\n info: {\n background: semanticColors.canvas,\n content: semanticColors.feedback.info.foreground,\n border: semanticColors.feedback.info.border,\n },\n success: {\n background: semanticColors.feedback.success.badgeBackground,\n content: semanticColors.feedback.success.foreground,\n border: semanticColors.feedback.success.border,\n },\n attention: {\n background: semanticColors.feedback.attention.badgeBackground,\n content: semanticColors.feedback.attention.foreground,\n border: semanticColors.feedback.attention.border,\n },\n },\n size: {\n // Static metadata still participates in Dynamic Type. A fixed 20px frame\n // clips the label at accessibility sizes; minHeight preserves the compact\n // base shape while allowing the intrinsic line box to grow.\n minHeight: 20,\n paddingHorizontal: spacing.xxs,\n gap: spacing.xxs,\n textVariant: \"caption\",\n fontWeight: fontWeight.medium,\n },\n radius: \"sm\",\n borderWidth: stroke.default,\n} as const;\n\nexport type ResolvedTagPresentation = Readonly<{\n background: string;\n content: string;\n border: string | null;\n}>;\n\n/** Resolve the same Tag recipe for non-CSS renderers. */\nexport function resolveTagPresentation(\n tone: TagTone,\n palette: ColorReferencePalette,\n): ResolvedTagPresentation {\n const contract = tagRecipe.tones[tone];\n return {\n background: resolveColorReference(contract.background, palette),\n content: resolveColorReference(contract.content, palette),\n border:\n contract.border === null\n ? null\n : resolveColorReference(contract.border, palette),\n };\n}\n"]}
|
package/dist/timeline.d.ts
CHANGED
package/dist/timeline.js
CHANGED
package/dist/timeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Timeline is a record of what already happened, not a position in a flow —\n * that is Steps' job (see docs/steps.md and the boundary note in\n * docs/timeline.md). Because there is no cursor, Timeline never derives a\n * status from position: every item is simply \"happened\", and the connector\n * between items carries no reached/unreached meaning. Ant Design's\n * alternating left/right layout is intentionally not reproduced — it is a\n * Web-only decoration with no equivalent in a single-direction native list;\n * see docs/timeline.md for the rationale.\n */\nexport type TimelineItemTone = \"neutral\" | \"info\" | \"success\" | \"attention\";\n\nexport type TimelineItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n /** Product-formatted \"when\" copy, e.g. \"3회 초\" or \"2024-01-15\". Timeline never formats a clock, inning, or date itself. */\n timestamp?: string;\n /** Product-formatted supplementary detail, e.g. \"안타로 1루 진출\". */\n description?: string;\n tone?: TimelineItemTone;\n}>;\n\nexport type TimelineDescriptor<Id extends string = string> = Readonly<{\n items: readonly TimelineItemDescriptor<Id>[];\n}>;\n\nexport const timelineDefaults = {\n itemTone: \"neutral\",\n} as const satisfies Readonly<{ itemTone: TimelineItemTone }>;\n\nexport type TimelineAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar, same reason\n * Steps does not assemble its own accessible name (see docs/steps.md).\n */\nexport type ComposeTimelineAccessibleName = (\n info: TimelineAccessibleNameInfo,\n) => string;\n\nexport type ResolvedTimelineItemDescriptor<Id extends string = string> = Omit<\n TimelineItemDescriptor<Id>,\n \"tone\"\n> &\n Readonly<{\n tone: TimelineItemTone;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Order + label, for platforms without built-in list-position semantics. */\n accessibleName: string;\n }>;\n\nexport type ResolveTimelineOptions = Readonly<{\n composeAccessibleName: ComposeTimelineAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Timeline ${field} must not be empty`);\n }\n}\n\nexport function validateTimelineItemDescriptor<Id extends string>(\n item: TimelineItemDescriptor<Id>,\n): void {\n assertNonEmpty(item.id, \"item id\");\n if (item.id !== item.id.trim()) {\n throw new TypeError(\"Timeline item id must not start or end with whitespace\");\n }\n assertNonEmpty(item.label, \"item label\");\n if (item.timestamp !== undefined) {\n assertNonEmpty(item.timestamp, \"item timestamp\");\n }\n if (item.description !== undefined) {\n assertNonEmpty(item.description, \"item description\");\n }\n if (\n item.tone !== undefined &&\n item.tone !== \"neutral\" &&\n item.tone !== \"info\" &&\n item.tone !== \"success\" &&\n item.tone !== \"attention\"\n ) {\n throw new TypeError(`Unsupported Timeline item tone: ${String(item.tone)}`);\n }\n}\n\nexport function validateTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n): void {\n if (descriptor.items.length === 0) {\n throw new RangeError(\"Timeline must contain at least one item\");\n }\n const ids = new Set<Id>();\n for (const item of descriptor.items) {\n validateTimelineItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate Timeline item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n}\n\n/**\n * Attaches order-preserving accessible names to an already-ordered item list.\n * Unlike Steps, no status is derived from position — every item already\n * happened, so this only adds order and the resolved tone default.\n */\nexport function resolveTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n options: ResolveTimelineOptions,\n): readonly ResolvedTimelineItemDescriptor<Id>[] {\n validateTimelineDescriptor(descriptor);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Timeline composeAccessibleName must be a function\");\n }\n\n const total = descriptor.items.length;\n return descriptor.items.map((item, index) => {\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: item.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Timeline composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...item,\n tone: item.tone ?? timelineDefaults.itemTone,\n position,\n total,\n accessibleName,\n };\n });\n}\n\nexport const timelineRecipe = {\n slots: [\n \"root\",\n \"item\",\n \"dot\",\n \"connector\",\n \"content\",\n \"timestamp\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.md,\n dot: {\n diameter: 10,\n borderWidth: stroke.default,\n tones: {\n neutral: { border: null, fill: semanticColors.content.secondary },\n info: {\n border: semanticColors.feedback.info.border,\n fill: semanticColors.feedback.info.foreground,\n },\n success: {\n border: semanticColors.feedback.success.border,\n fill: semanticColors.feedback.success.foreground,\n },\n attention: {\n border: semanticColors.feedback.attention.border,\n fill: semanticColors.feedback.attention.foreground,\n },\n },\n },\n /**\n * A single tone, unlike stepsRecipe.connector's reached/unreached pair —\n * Timeline has no cursor, so no segment is \"not yet reached\".\n */\n connector: {\n width: stroke.default,\n tone: semanticColors.border.default,\n },\n timestamp: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n label: {\n textVariant: \"body\",\n fontWeight: fontWeight.semibold,\n color: semanticColors.content.primary,\n },\n description: {\n textVariant: \"body\",\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n dot: {\n diameter: number;\n borderWidth: number;\n tones: Record<\n TimelineItemTone,\n { border: ColorReference | null; fill: ColorReference }\n >;\n };\n connector: {\n width: number;\n tone: ColorReference;\n };\n timestamp: { textVariant: TextVariant; color: ColorReference };\n label: { textVariant: TextVariant; fontWeight: FontWeightValue; color: ColorReference };\n description: { textVariant: TextVariant; color: ColorReference };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Timeline is a record of what already happened, not a position in a flow —\n * that is Steps' job (see docs/steps.md and the boundary note in\n * docs/timeline.md). Because there is no cursor, Timeline never derives a\n * status from position: every item is simply \"happened\", and the connector\n * between items carries no reached/unreached meaning. Ant Design's\n * alternating left/right layout is intentionally not reproduced — it is a\n * Web-only decoration with no equivalent in a single-direction native list;\n * see docs/timeline.md for the rationale.\n */\nexport type TimelineItemTone = \"neutral\" | \"info\" | \"success\" | \"attention\";\n\nexport type TimelineItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n /** Product-formatted \"when\" copy, e.g. \"3회 초\" or \"2024-01-15\". Timeline never formats a clock, inning, or date itself. */\n timestamp?: string;\n /** Product-formatted supplementary detail, e.g. \"안타로 1루 진출\". */\n description?: string;\n tone?: TimelineItemTone;\n}>;\n\nexport type TimelineDescriptor<Id extends string = string> = Readonly<{\n items: readonly TimelineItemDescriptor<Id>[];\n}>;\n\nexport const timelineDefaults = {\n itemTone: \"neutral\",\n} as const satisfies Readonly<{ itemTone: TimelineItemTone }>;\n\nexport type TimelineAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar, same reason\n * Steps does not assemble its own accessible name (see docs/steps.md).\n */\nexport type ComposeTimelineAccessibleName = (\n info: TimelineAccessibleNameInfo,\n) => string;\n\nexport type ResolvedTimelineItemDescriptor<Id extends string = string> = Omit<\n TimelineItemDescriptor<Id>,\n \"tone\"\n> &\n Readonly<{\n tone: TimelineItemTone;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Order + label, for platforms without built-in list-position semantics. */\n accessibleName: string;\n }>;\n\nexport type ResolveTimelineOptions = Readonly<{\n composeAccessibleName: ComposeTimelineAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Timeline ${field} must not be empty`);\n }\n}\n\nexport function validateTimelineItemDescriptor<Id extends string>(\n item: TimelineItemDescriptor<Id>,\n): void {\n assertNonEmpty(item.id, \"item id\");\n if (item.id !== item.id.trim()) {\n throw new TypeError(\"Timeline item id must not start or end with whitespace\");\n }\n assertNonEmpty(item.label, \"item label\");\n if (item.timestamp !== undefined) {\n assertNonEmpty(item.timestamp, \"item timestamp\");\n }\n if (item.description !== undefined) {\n assertNonEmpty(item.description, \"item description\");\n }\n if (\n item.tone !== undefined &&\n item.tone !== \"neutral\" &&\n item.tone !== \"info\" &&\n item.tone !== \"success\" &&\n item.tone !== \"attention\"\n ) {\n throw new TypeError(`Unsupported Timeline item tone: ${String(item.tone)}`);\n }\n}\n\nexport function validateTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n): void {\n if (descriptor.items.length === 0) {\n throw new RangeError(\"Timeline must contain at least one item\");\n }\n const ids = new Set<Id>();\n for (const item of descriptor.items) {\n validateTimelineItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate Timeline item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n}\n\n/**\n * Attaches order-preserving accessible names to an already-ordered item list.\n * Unlike Steps, no status is derived from position — every item already\n * happened, so this only adds order and the resolved tone default.\n */\nexport function resolveTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n options: ResolveTimelineOptions,\n): readonly ResolvedTimelineItemDescriptor<Id>[] {\n validateTimelineDescriptor(descriptor);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Timeline composeAccessibleName must be a function\");\n }\n\n const total = descriptor.items.length;\n return descriptor.items.map((item, index) => {\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: item.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Timeline composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...item,\n tone: item.tone ?? timelineDefaults.itemTone,\n position,\n total,\n accessibleName,\n };\n });\n}\n\nexport const timelineRecipe = {\n slots: [\n \"root\",\n \"item\",\n \"dot\",\n \"connector\",\n \"content\",\n \"timestamp\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.md,\n dot: {\n diameter: 10,\n borderWidth: stroke.default,\n tones: {\n neutral: { border: null, fill: semanticColors.content.secondary },\n info: {\n border: semanticColors.feedback.info.border,\n fill: semanticColors.feedback.info.foreground,\n },\n success: {\n border: semanticColors.feedback.success.border,\n fill: semanticColors.feedback.success.foreground,\n },\n attention: {\n border: semanticColors.feedback.attention.border,\n fill: semanticColors.feedback.attention.foreground,\n },\n },\n },\n /**\n * A single tone, unlike stepsRecipe.connector's reached/unreached pair —\n * Timeline has no cursor, so no segment is \"not yet reached\".\n */\n connector: {\n width: stroke.default,\n tone: semanticColors.border.default,\n },\n timestamp: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n label: {\n textVariant: \"body\",\n fontWeight: fontWeight.medium,\n color: semanticColors.content.primary,\n },\n description: {\n textVariant: \"body\",\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n dot: {\n diameter: number;\n borderWidth: number;\n tones: Record<\n TimelineItemTone,\n { border: ColorReference | null; fill: ColorReference }\n >;\n };\n connector: {\n width: number;\n tone: ColorReference;\n };\n timestamp: { textVariant: TextVariant; color: ColorReference };\n label: { textVariant: TextVariant; fontWeight: FontWeightValue; color: ColorReference };\n description: { textVariant: TextVariant; color: ColorReference };\n};\n"]}
|
package/dist/top.d.ts
CHANGED
|
@@ -47,16 +47,16 @@ export declare const topRecipe: {
|
|
|
47
47
|
readonly title: {
|
|
48
48
|
readonly fontSize: 20;
|
|
49
49
|
readonly lineHeight: 28;
|
|
50
|
-
readonly fontWeight: "
|
|
50
|
+
readonly fontWeight: "500";
|
|
51
51
|
};
|
|
52
52
|
readonly paddingTop: 16;
|
|
53
53
|
readonly paddingBottom: 12;
|
|
54
54
|
};
|
|
55
55
|
readonly large: {
|
|
56
56
|
readonly title: {
|
|
57
|
-
readonly fontSize:
|
|
58
|
-
readonly lineHeight:
|
|
59
|
-
readonly fontWeight: "
|
|
57
|
+
readonly fontSize: 24;
|
|
58
|
+
readonly lineHeight: 32;
|
|
59
|
+
readonly fontWeight: "500";
|
|
60
60
|
};
|
|
61
61
|
readonly paddingTop: 24;
|
|
62
62
|
readonly paddingBottom: 16;
|
package/dist/top.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../src/top.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzC,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW;;;CAGiD,CAAC;AAQ1E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,CAkBrE;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"top.d.ts","sourceRoot":"","sources":["../src/top.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzC,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC1B,CAAC,CAAC;AAEH,eAAO,MAAM,WAAW;;;CAGiD,CAAC;AAQ1E,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI,CAkBrE;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoBpB,uDAAuD;;;;;CAexD,CAAC;AAEF,eAAO,MAAM,WAAW;IACtB,6CAA6C;;;;;;;;;;;;;;;CAaV,CAAC"}
|
package/dist/top.js
CHANGED
|
@@ -37,7 +37,9 @@ export const topRecipe = {
|
|
|
37
37
|
defaults: { size: topDefaults.size },
|
|
38
38
|
sizes: {
|
|
39
39
|
medium: { title: typography.titleLarge, paddingTop: spacing.md, paddingBottom: spacing.sm },
|
|
40
|
-
|
|
40
|
+
// The screen's first title uses the HJM ceiling, `heading.level3` (24). It was `heading.level2` (32) until the
|
|
41
|
+
// display tier was removed (2026-10-17 user decision, "글자 너무 커").
|
|
42
|
+
large: { title: heading.level3, paddingTop: spacing.xl, paddingBottom: spacing.md },
|
|
41
43
|
},
|
|
42
44
|
eyebrow: {
|
|
43
45
|
color: semanticColors.content.brand,
|
package/dist/top.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"top.js","sourceRoot":"","sources":["../src/top.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { heading, spacing, typography } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 화면 본문의 **첫 블록**이다. `TopBar`(상단 내비게이션 바)와 다른 자리이고, 그 차이가\n * 이 계약의 존재 이유다.\n *\n * - `TopBar`는 화면에 고정된 크롬이다 — 뒤로가기, 화면 이름, 액션. 스크롤과 무관하게\n * 붙어 있고 safe area를 다룬다.\n * - `Top`은 본문이다 — 스크롤과 함께 올라가고, 사용자가 \"이 화면이 무엇을 묻는지\"\n * 읽는 문장이 여기 있다. 토스 TDS가 \"Top과 ListRow로 화면 대부분을 만든다\"고\n * 말하는 그 Top이고, 지금까지 BurnTok `AppScreenHeader`·Taground `screen-shell`·\n * Diairy `DetailPanel`이 각자 다시 만들던 블록이다.\n *\n * `Section`과도 다르다. Section은 본문 **중간**의 묶음 제목이라 heading level이 화면\n * 구조에 종속되지만, Top은 화면당 하나뿐인 첫 제목이라 기본이 `h1`이다. 그래서 두\n * 계약을 합치지 않았다 — 합치면 \"이 Section이 화면의 h1인가\"를 매번 물어야 한다.\n */\nexport type TopSize = \"medium\" | \"large\";\n\nexport type TopDescriptor = Readonly<{\n title: string;\n /** 제목 위 한 줄. 카테고리·단계처럼 제목을 한정하는 짧은 말. */\n eyebrow?: string;\n /** 제목 아래 보조 문장. 길면 줄바꿈되며 잘리지 않는다. */\n description?: string;\n size?: TopSize;\n /**\n * 문서 구조상의 제목 단계. 화면당 하나인 Top은 `1`이 기본이지만, 한 라우트가\n * 여러 화면을 담는 경우(시트 안의 화면 등) 제품이 낮출 수 있다.\n */\n headingLevel?: 1 | 2 | 3;\n}>;\n\nexport const topDefaults = {\n size: \"large\",\n headingLevel: 1,\n} as const satisfies Readonly<{ size: TopSize; headingLevel: 1 | 2 | 3 }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Top ${field} must not be empty`);\n }\n}\n\nexport function validateTopDescriptor(descriptor: TopDescriptor): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"Top descriptor must be an object\");\n }\n assertNonEmpty(descriptor.title, \"title\");\n if (descriptor.eyebrow !== undefined) assertNonEmpty(descriptor.eyebrow, \"eyebrow\");\n if (descriptor.description !== undefined) {\n assertNonEmpty(descriptor.description, \"description\");\n }\n if (descriptor.size !== undefined && descriptor.size !== \"medium\" && descriptor.size !== \"large\") {\n throw new TypeError(`Unsupported Top size: ${String(descriptor.size)}`);\n }\n if (\n descriptor.headingLevel !== undefined &&\n ![1, 2, 3].includes(descriptor.headingLevel)\n ) {\n throw new TypeError(`Unsupported Top headingLevel: ${String(descriptor.headingLevel)}`);\n }\n}\n\n/**\n * `large`는 화면의 첫 제목, `medium`은 시트·모달 안의 첫 제목이다. 두 단계뿐인 이유는\n * 세 번째가 필요해지는 자리가 곧 `Section`이기 때문이다 — 크기를 더 늘리는 대신 다른\n * 컴포넌트를 쓰라는 신호로 둔다.\n */\nexport const topRecipe = {\n slots: [\"root\", \"eyebrow\", \"title\", \"description\", \"trailing\"] as const,\n defaults: { size: topDefaults.size },\n sizes: {\n medium: { title: typography.titleLarge, paddingTop: spacing.md, paddingBottom: spacing.sm },\n large: { title: heading.
|
|
1
|
+
{"version":3,"file":"top.js","sourceRoot":"","sources":["../src/top.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport type { ColorReference } from \"./color-references.js\";\nimport { heading, spacing, typography } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * 화면 본문의 **첫 블록**이다. `TopBar`(상단 내비게이션 바)와 다른 자리이고, 그 차이가\n * 이 계약의 존재 이유다.\n *\n * - `TopBar`는 화면에 고정된 크롬이다 — 뒤로가기, 화면 이름, 액션. 스크롤과 무관하게\n * 붙어 있고 safe area를 다룬다.\n * - `Top`은 본문이다 — 스크롤과 함께 올라가고, 사용자가 \"이 화면이 무엇을 묻는지\"\n * 읽는 문장이 여기 있다. 토스 TDS가 \"Top과 ListRow로 화면 대부분을 만든다\"고\n * 말하는 그 Top이고, 지금까지 BurnTok `AppScreenHeader`·Taground `screen-shell`·\n * Diairy `DetailPanel`이 각자 다시 만들던 블록이다.\n *\n * `Section`과도 다르다. Section은 본문 **중간**의 묶음 제목이라 heading level이 화면\n * 구조에 종속되지만, Top은 화면당 하나뿐인 첫 제목이라 기본이 `h1`이다. 그래서 두\n * 계약을 합치지 않았다 — 합치면 \"이 Section이 화면의 h1인가\"를 매번 물어야 한다.\n */\nexport type TopSize = \"medium\" | \"large\";\n\nexport type TopDescriptor = Readonly<{\n title: string;\n /** 제목 위 한 줄. 카테고리·단계처럼 제목을 한정하는 짧은 말. */\n eyebrow?: string;\n /** 제목 아래 보조 문장. 길면 줄바꿈되며 잘리지 않는다. */\n description?: string;\n size?: TopSize;\n /**\n * 문서 구조상의 제목 단계. 화면당 하나인 Top은 `1`이 기본이지만, 한 라우트가\n * 여러 화면을 담는 경우(시트 안의 화면 등) 제품이 낮출 수 있다.\n */\n headingLevel?: 1 | 2 | 3;\n}>;\n\nexport const topDefaults = {\n size: \"large\",\n headingLevel: 1,\n} as const satisfies Readonly<{ size: TopSize; headingLevel: 1 | 2 | 3 }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Top ${field} must not be empty`);\n }\n}\n\nexport function validateTopDescriptor(descriptor: TopDescriptor): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"Top descriptor must be an object\");\n }\n assertNonEmpty(descriptor.title, \"title\");\n if (descriptor.eyebrow !== undefined) assertNonEmpty(descriptor.eyebrow, \"eyebrow\");\n if (descriptor.description !== undefined) {\n assertNonEmpty(descriptor.description, \"description\");\n }\n if (descriptor.size !== undefined && descriptor.size !== \"medium\" && descriptor.size !== \"large\") {\n throw new TypeError(`Unsupported Top size: ${String(descriptor.size)}`);\n }\n if (\n descriptor.headingLevel !== undefined &&\n ![1, 2, 3].includes(descriptor.headingLevel)\n ) {\n throw new TypeError(`Unsupported Top headingLevel: ${String(descriptor.headingLevel)}`);\n }\n}\n\n/**\n * `large`는 화면의 첫 제목, `medium`은 시트·모달 안의 첫 제목이다. 두 단계뿐인 이유는\n * 세 번째가 필요해지는 자리가 곧 `Section`이기 때문이다 — 크기를 더 늘리는 대신 다른\n * 컴포넌트를 쓰라는 신호로 둔다.\n */\nexport const topRecipe = {\n slots: [\"root\", \"eyebrow\", \"title\", \"description\", \"trailing\"] as const,\n defaults: { size: topDefaults.size },\n sizes: {\n medium: { title: typography.titleLarge, paddingTop: spacing.md, paddingBottom: spacing.sm },\n // The screen's first title uses the HJM ceiling, `heading.level3` (24). It was `heading.level2` (32) until the\n // display tier was removed (2026-10-17 user decision, \"글자 너무 커\").\n large: { title: heading.level3, paddingTop: spacing.xl, paddingBottom: spacing.md },\n },\n eyebrow: {\n color: semanticColors.content.brand,\n textVariant: \"label\" as const,\n marginBottom: spacing.xxs,\n },\n title: { color: semanticColors.content.primary },\n description: {\n color: semanticColors.content.body,\n textVariant: \"body\" as const,\n marginTop: spacing.xs,\n },\n /** 제목 줄 오른쪽 보조 행동. 본문이므로 TopBar처럼 아이콘 전용을 강제하지 않는다. */\n trailing: { gap: spacing.xs },\n gap: spacing.xs,\n} as const satisfies {\n slots: readonly [\"root\", \"eyebrow\", \"title\", \"description\", \"trailing\"];\n defaults: { size: TopSize };\n sizes: Readonly<{\n medium: { title: typeof typography.titleLarge; paddingTop: number; paddingBottom: number };\n large: { title: typeof heading.level3; paddingTop: number; paddingBottom: number };\n }>;\n eyebrow: { color: ColorReference; textVariant: \"label\"; marginBottom: number };\n title: { color: ColorReference };\n description: { color: ColorReference; textVariant: \"body\"; marginTop: number };\n trailing: { gap: number };\n gap: number;\n};\n\nexport const topBehavior = {\n /** 상태 축이 없다 — 화면이 제목을 바꾸면 그냥 다른 문자열을 넘긴다. */\n controlled: [],\n inputs: [\"title\", \"eyebrow\", \"description\", \"size\", \"headingLevel\"],\n stateAxes: {},\n web: { roles: [\"heading\"], keyboard: [], focus: \"none\" },\n native: { roles: [\"header\"], states: [], actions: [] },\n scenarios: [\n \"the-title-is-a-real-heading-element-at-the-declared-level-not-styled-text\",\n \"top-is-body-content-that-scrolls-away-while-topbar-is-fixed-chrome\",\n \"description-wraps-instead-of-truncating-so-large-text-never-hides-the-question\",\n \"a-trailing-action-shares-the-title-row-and-drops-below-it-when-space-runs-out\",\n \"eyebrow-never-carries-information-the-title-does-not-repeat-in-some-form\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.28.1\" as const;\n"]}
|
package/docs/activity-chip.md
CHANGED
|
@@ -19,7 +19,7 @@ Chip의 안정성을 상속하거나 독립적인 stable 승격을 뜻하지 않
|
|
|
19
19
|
- `emphasis`: `default`(surface/text/border) · `emphasis`(brand action fill/content, 결과가 사용자를 기다릴 때).
|
|
20
20
|
두 쌍 모두 light/dark에서 4.5:1을 contract test가 지킨다.
|
|
21
21
|
- `activityChipRecipe`: 최소 높이 44(`control.minTouchTarget`), 최대 폭 180, 모서리 full, 미디어 28, 간격 `spacing.xxs`,
|
|
22
|
-
시작 inset `spacing.xxs`(미디어가 있을 때)·끝 inset `spacing.sm`, 라벨 `label` variant
|
|
22
|
+
시작 inset `spacing.xxs`(미디어가 있을 때)·끝 inset `spacing.sm`, 라벨 `label` variant medium(500) 한 줄.
|
|
23
23
|
- 작업 상태·문구·미디어·패널 내용은 제품 소유다. HJM은 트리거 모양, 펼침 의미, 패널(Toast) 수명주기를 소유한다.
|
|
24
24
|
|
|
25
25
|
## Renderer
|
|
@@ -33,3 +33,7 @@ Chip의 안정성을 상속하거나 독립적인 stable 승격을 뜻하지 않
|
|
|
33
33
|
|
|
34
34
|
검증: contracts `test/activity-chip.test.ts`, Web `test/activity-chip.browser.test.tsx`, Native `test/activity-chip.test.tsx`.
|
|
35
35
|
기기 시각·VoiceOver/TalkBack 확인은 아직 하지 않았다.
|
|
36
|
+
|
|
37
|
+
## 근거
|
|
38
|
+
|
|
39
|
+
2026-10-17 사용자 결정("굵기 좀더 줄여"): 모든 글자 굵기를 한 단계 낮췄다. 큰 숫자·선택 상태 600(큰 제목 단계는 같은 날 폐지), 제목·강조 500, label 500, 본문 400이며 정본은 [타이포그래피](usage/tokens/typography.md)다.
|
package/docs/auth-screen.md
CHANGED
|
@@ -75,6 +75,8 @@ hero와 footer는 진행 중에도 유지한다. 버튼별 busy spinner와 함
|
|
|
75
75
|
| footer 간격 | `xl`(compact `md`) | **최소값**이다. 화면이 길면 그 이상 벌어진다 |
|
|
76
76
|
| 제공자 버튼 높이 | `authProviderButtonRecipe.minHeight` | 여기서 다시 정하지 않는다. 두 계약이 각자 값을 들면 한쪽만 바뀌어 버튼 줄기가 어긋난다 |
|
|
77
77
|
| 정책 링크 터치 | `control.minTouchTarget` | 글자 높이만 누를 수 있으면 44pt에 못 미친다 |
|
|
78
|
+
| 히어로 태그라인 | `typography.heading` 24/32·500(compact `titleLarge` 20/28) | 2026-10-17 "굵기랑 크기 줄여": 화면 첫 제목(display 32/40)을 빌리면 402 폭에서 두 줄이 된다. `AuthScreenHero`가 그린다 |
|
|
79
|
+
| 정책 링크 간격 | `linkGap` 8 + `·` + 8 | 2026-10-17 "간격 줄여": 버튼 안쪽 여백이 더해지면 40~42가 된다. 터치 영역은 보이지 않게 넓힌다. `AuthScreenFooter`가 그린다 |
|
|
78
80
|
|
|
79
81
|
## 이 계약에 없는 것
|
|
80
82
|
|