@hjmds/design-contracts 1.9.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-session.d.ts +47 -0
- package/dist/action-session.d.ts.map +1 -0
- package/dist/action-session.js +60 -0
- package/dist/action-session.js.map +1 -0
- package/dist/activity-heatmap.d.ts +22 -0
- package/dist/activity-heatmap.d.ts.map +1 -0
- package/dist/activity-heatmap.js +29 -0
- package/dist/activity-heatmap.js.map +1 -0
- package/dist/auth-screen.d.ts +4 -0
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +2 -0
- package/dist/auth-screen.js.map +1 -1
- package/dist/avatar-fallback.d.ts +25 -0
- package/dist/avatar-fallback.d.ts.map +1 -0
- package/dist/avatar-fallback.js +17 -0
- package/dist/avatar-fallback.js.map +1 -0
- package/dist/behaviors.d.ts +2 -2
- package/dist/bottom-navigation-defaults.d.ts +1 -1
- package/dist/bottom-navigation-defaults.d.ts.map +1 -1
- package/dist/bottom-navigation-defaults.js.map +1 -1
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +5 -1
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/catalog.d.ts +67 -5
- package/dist/catalog.d.ts.map +1 -1
- package/dist/code-block.d.ts +23 -0
- package/dist/code-block.d.ts.map +1 -0
- package/dist/code-block.js +13 -0
- package/dist/code-block.js.map +1 -0
- package/dist/component-definitions.d.ts +2 -3
- package/dist/component-definitions.d.ts.map +1 -1
- package/dist/component-definitions.js +1 -2
- package/dist/component-definitions.js.map +1 -1
- package/dist/component-recipes.d.ts +63 -5
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +33 -6
- package/dist/component-recipes.js.map +1 -1
- package/dist/component-references.d.ts +0 -6
- package/dist/component-references.d.ts.map +1 -1
- package/dist/component-references.js +0 -5
- package/dist/component-references.js.map +1 -1
- package/dist/content-transition.d.ts +17 -0
- package/dist/content-transition.d.ts.map +1 -0
- package/dist/content-transition.js +15 -0
- package/dist/content-transition.js.map +1 -0
- package/dist/duration-field.d.ts +22 -0
- package/dist/duration-field.d.ts.map +1 -0
- package/dist/duration-field.js +20 -0
- package/dist/duration-field.js.map +1 -0
- package/dist/effect-surface.d.ts +29 -0
- package/dist/effect-surface.d.ts.map +1 -0
- package/dist/effect-surface.js +26 -0
- package/dist/effect-surface.js.map +1 -0
- package/dist/gooey-navigation.d.ts +13 -0
- package/dist/gooey-navigation.d.ts.map +1 -0
- package/dist/gooey-navigation.js +11 -0
- package/dist/gooey-navigation.js.map +1 -0
- package/dist/gravity-letters.d.ts +12 -0
- package/dist/gravity-letters.d.ts.map +1 -0
- package/dist/gravity-letters.js +11 -0
- package/dist/gravity-letters.js.map +1 -0
- package/dist/grid-reveal.d.ts +8 -0
- package/dist/grid-reveal.d.ts.map +1 -0
- package/dist/grid-reveal.js +5 -0
- package/dist/grid-reveal.js.map +1 -0
- package/dist/internal/object-validation.d.ts +3 -0
- package/dist/internal/object-validation.d.ts.map +1 -0
- package/dist/internal/object-validation.js +12 -0
- package/dist/internal/object-validation.js.map +1 -0
- package/dist/internal/thinking-orb/braid.d.ts.map +1 -1
- package/dist/internal/thinking-orb/braid.js +2 -9
- package/dist/internal/thinking-orb/braid.js.map +1 -1
- package/dist/internal/thinking-orb/core.d.ts +1 -0
- package/dist/internal/thinking-orb/core.d.ts.map +1 -1
- package/dist/internal/thinking-orb/core.js +12 -0
- package/dist/internal/thinking-orb/core.js.map +1 -1
- package/dist/internal/thinking-orb/presentation.d.ts +4 -0
- package/dist/internal/thinking-orb/presentation.d.ts.map +1 -0
- package/dist/internal/thinking-orb/presentation.js +28 -0
- package/dist/internal/thinking-orb/presentation.js.map +1 -0
- package/dist/internal/thinking-orb/ribbon.d.ts.map +1 -1
- package/dist/internal/thinking-orb/ribbon.js +2 -9
- package/dist/internal/thinking-orb/ribbon.js.map +1 -1
- package/dist/layout.d.ts +0 -5
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +0 -7
- package/dist/layout.js.map +1 -1
- package/dist/otp-field.d.ts +2 -0
- package/dist/otp-field.d.ts.map +1 -1
- package/dist/otp-field.js.map +1 -1
- package/dist/popover.d.ts.map +1 -1
- package/dist/popover.js +3 -12
- package/dist/popover.js.map +1 -1
- package/dist/reactions.d.ts +10 -0
- package/dist/reactions.d.ts.map +1 -0
- package/dist/reactions.js +20 -0
- package/dist/reactions.js.map +1 -0
- package/dist/scroll-progress.d.ts +8 -0
- package/dist/scroll-progress.d.ts.map +1 -0
- package/dist/scroll-progress.js +10 -0
- package/dist/scroll-progress.js.map +1 -0
- package/dist/task-list.d.ts +7 -0
- package/dist/task-list.d.ts.map +1 -0
- package/dist/task-list.js +4 -0
- package/dist/task-list.js.map +1 -0
- package/dist/theme-studio.d.ts +14 -0
- package/dist/theme-studio.d.ts.map +1 -0
- package/dist/theme-studio.js +16 -0
- package/dist/theme-studio.js.map +1 -0
- package/dist/thinking-orb.d.ts +4 -2
- package/dist/thinking-orb.d.ts.map +1 -1
- package/dist/thinking-orb.js +8 -3
- package/dist/thinking-orb.js.map +1 -1
- package/dist/toast-liquid.d.ts +4 -4
- package/dist/toast-liquid.d.ts.map +1 -1
- package/dist/toast-liquid.js +11 -10
- package/dist/toast-liquid.js.map +1 -1
- package/dist/toast.d.ts +4 -4
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +4 -3
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.d.ts.map +1 -1
- package/dist/tooltip.js +3 -12
- package/dist/tooltip.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/voice-note.d.ts +20 -0
- package/dist/voice-note.d.ts.map +1 -0
- package/dist/voice-note.js +16 -0
- package/dist/voice-note.js.map +1 -0
- package/docs/action-session.md +73 -0
- package/docs/activity-heatmap.md +32 -0
- package/docs/anchor.md +1 -1
- package/docs/auth-screen.md +44 -4
- package/docs/authoring-brief.md +5 -0
- package/docs/avatar-fallback.md +74 -0
- package/docs/bottom-navigation.md +16 -1
- package/docs/breadcrumb.md +1 -1
- package/docs/calendar.md +1 -1
- package/docs/cascader.md +1 -1
- package/docs/code-block.md +32 -0
- package/docs/command-palette.md +1 -1
- package/docs/compound-controls.md +83 -0
- package/docs/confirm-popover.md +1 -1
- package/docs/consumer-policy.md +30 -23
- package/docs/cross-platform-core-normalization.md +19 -21
- package/docs/data-table.md +8 -5
- package/docs/dialog.md +2 -0
- package/docs/effect-surface.md +50 -0
- package/docs/floating-action-button.md +1 -1
- package/docs/folder-preview.md +26 -0
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/gooey-navigation.md +36 -0
- package/docs/gravity-letters.md +26 -0
- package/docs/grid-reveal.md +11 -0
- package/docs/icon.md +26 -0
- package/docs/image.md +2 -2
- package/docs/layout.md +1 -2
- package/docs/migration-0.5.md +4 -1
- package/docs/migration-native-legacy-removal.md +59 -0
- package/docs/navigation-bar.md +28 -0
- package/docs/optional-adapters.md +24 -1
- package/docs/otp-field.md +15 -0
- package/docs/pagination.md +1 -1
- package/docs/popover.md +2 -2
- package/docs/product-adoption-1.4.md +7 -0
- package/docs/product-audit-2026-09-15.md +1 -1
- package/docs/provider-button.md +6 -3
- package/docs/rating.md +1 -1
- package/docs/react-native-completion.md +4 -4
- package/docs/screen-chrome.md +1 -1
- package/docs/scroll-progress.md +29 -0
- package/docs/sheet.md +2 -0
- package/docs/sidebar.md +6 -0
- package/docs/splitter.md +1 -1
- package/docs/step-player.md +16 -0
- package/docs/task-list.md +32 -0
- package/docs/theme-studio.md +13 -0
- package/docs/thinking-orb.md +9 -2
- package/docs/time-picker.md +1 -1
- package/docs/toast.md +27 -8
- package/docs/tour.md +1 -1
- package/docs/tree-select.md +2 -2
- package/docs/voice-note.md +19 -0
- package/package.json +91 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Optional behavior adapter: no React, networking, storage or animation dependency. */
|
|
2
|
+
export type ActionSnapshot<T> = Readonly<{
|
|
3
|
+
status: "idle";
|
|
4
|
+
value: T;
|
|
5
|
+
}> | Readonly<{
|
|
6
|
+
status: "pending" | "success";
|
|
7
|
+
value: T;
|
|
8
|
+
operationId: number;
|
|
9
|
+
}> | Readonly<{
|
|
10
|
+
status: "error";
|
|
11
|
+
value: T;
|
|
12
|
+
operationId: number;
|
|
13
|
+
error: unknown;
|
|
14
|
+
}>;
|
|
15
|
+
export type ActionTask<T> = (context: Readonly<{
|
|
16
|
+
operationId: number;
|
|
17
|
+
}>) => T | Promise<T>;
|
|
18
|
+
export type ActionOptions<T> = Readonly<{
|
|
19
|
+
optimisticValue?: T;
|
|
20
|
+
retryable?: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
export type ActionOutcome<T> = Readonly<{
|
|
23
|
+
status: "success";
|
|
24
|
+
value: T;
|
|
25
|
+
operationId: number;
|
|
26
|
+
}> | Readonly<{
|
|
27
|
+
status: "error";
|
|
28
|
+
error: unknown;
|
|
29
|
+
operationId: number;
|
|
30
|
+
}> | Readonly<{
|
|
31
|
+
status: "blocked" | "ignored";
|
|
32
|
+
}>;
|
|
33
|
+
export type ActionSession<T> = Readonly<{
|
|
34
|
+
getSnapshot(): ActionSnapshot<T>;
|
|
35
|
+
subscribe(listener: () => void): () => void;
|
|
36
|
+
run(task: ActionTask<T>, options?: ActionOptions<T>): Promise<ActionOutcome<T>>;
|
|
37
|
+
retry(): Promise<ActionOutcome<T>>;
|
|
38
|
+
/** Detach stale results; this does NOT cancel work already accepted by a server. */
|
|
39
|
+
reset(value: T): void;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* One session per editable entity/action. Serialize mutations instead of guessing
|
|
43
|
+
* how overlapping server writes should merge; applications own reconciliation.
|
|
44
|
+
* See docs/action-session.md for retry, persistence and ownership boundaries.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createActionSession<T>(initialValue: T): ActionSession<T>;
|
|
47
|
+
//# sourceMappingURL=action-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-session.d.ts","sourceRoot":"","sources":["../src/action-session.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,MAAM,cAAc,CAAC,CAAC,IACxB,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,GACtC,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1E,QAAQ,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACjF,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAC3F,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,QAAQ,CAAC;IAAE,eAAe,CAAC,EAAE,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACtF,MAAM,MAAM,aAAa,CAAC,CAAC,IACvB,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9D,QAAQ,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,GAClE,QAAQ,CAAC;IAAE,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC,CAAC;AAChD,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,QAAQ,CAAC;IACtC,WAAW,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC;IACjC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,oFAAoF;IACpF,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACvB,CAAC,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CA8CxE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One session per editable entity/action. Serialize mutations instead of guessing
|
|
3
|
+
* how overlapping server writes should merge; applications own reconciliation.
|
|
4
|
+
* See docs/action-session.md for retry, persistence and ownership boundaries.
|
|
5
|
+
*/
|
|
6
|
+
export function createActionSession(initialValue) {
|
|
7
|
+
let snapshot = { status: "idle", value: initialValue };
|
|
8
|
+
let generation = 0;
|
|
9
|
+
let failedAttempt;
|
|
10
|
+
const listeners = new Set();
|
|
11
|
+
const publish = (next) => {
|
|
12
|
+
snapshot = next;
|
|
13
|
+
for (const listener of [...listeners])
|
|
14
|
+
listener();
|
|
15
|
+
};
|
|
16
|
+
const run = async (task, options = {}) => {
|
|
17
|
+
if (snapshot.status === "pending")
|
|
18
|
+
return { status: "blocked" };
|
|
19
|
+
const operationId = ++generation;
|
|
20
|
+
const previous = snapshot.value;
|
|
21
|
+
// Capture the proposal now so retry cannot pick up a caller-mutated options object.
|
|
22
|
+
const proposal = { ...options };
|
|
23
|
+
failedAttempt = undefined;
|
|
24
|
+
publish({ status: "pending", value: "optimisticValue" in proposal ? proposal.optimisticValue : previous, operationId });
|
|
25
|
+
if (generation !== operationId)
|
|
26
|
+
return { status: "ignored" };
|
|
27
|
+
let value;
|
|
28
|
+
try {
|
|
29
|
+
value = await task({ operationId });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (generation !== operationId)
|
|
33
|
+
return { status: "ignored" };
|
|
34
|
+
// Retrying a mutation must be authorized by the product adapter (idempotency).
|
|
35
|
+
failedAttempt = proposal.retryable ? { task, options: proposal } : undefined;
|
|
36
|
+
publish({ status: "error", value: previous, operationId, error });
|
|
37
|
+
return { status: "error", error, operationId };
|
|
38
|
+
}
|
|
39
|
+
if (generation !== operationId)
|
|
40
|
+
return { status: "ignored" };
|
|
41
|
+
publish({ status: "success", value, operationId });
|
|
42
|
+
return { status: "success", value, operationId };
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
getSnapshot: () => snapshot,
|
|
46
|
+
subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; },
|
|
47
|
+
run,
|
|
48
|
+
retry() {
|
|
49
|
+
if (snapshot.status !== "error" || !failedAttempt)
|
|
50
|
+
return Promise.resolve({ status: "blocked" });
|
|
51
|
+
return run(failedAttempt.task, failedAttempt.options);
|
|
52
|
+
},
|
|
53
|
+
reset(value) {
|
|
54
|
+
++generation;
|
|
55
|
+
failedAttempt = undefined;
|
|
56
|
+
publish({ status: "idle", value });
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=action-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-session.js","sourceRoot":"","sources":["../src/action-session.ts"],"names":[],"mappings":"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","sourcesContent":["/** Optional behavior adapter: no React, networking, storage or animation dependency. */\nexport type ActionSnapshot<T> =\n | Readonly<{ status: \"idle\"; value: T }>\n | Readonly<{ status: \"pending\" | \"success\"; value: T; operationId: number }>\n | Readonly<{ status: \"error\"; value: T; operationId: number; error: unknown }>;\nexport type ActionTask<T> = (context: Readonly<{ operationId: number }>) => T | Promise<T>;\nexport type ActionOptions<T> = Readonly<{ optimisticValue?: T; retryable?: boolean }>;\nexport type ActionOutcome<T> =\n | Readonly<{ status: \"success\"; value: T; operationId: number }>\n | Readonly<{ status: \"error\"; error: unknown; operationId: number }>\n | Readonly<{ status: \"blocked\" | \"ignored\" }>;\nexport type ActionSession<T> = Readonly<{\n getSnapshot(): ActionSnapshot<T>;\n subscribe(listener: () => void): () => void;\n run(task: ActionTask<T>, options?: ActionOptions<T>): Promise<ActionOutcome<T>>;\n retry(): Promise<ActionOutcome<T>>;\n /** Detach stale results; this does NOT cancel work already accepted by a server. */\n reset(value: T): void;\n}>;\n\n/**\n * One session per editable entity/action. Serialize mutations instead of guessing\n * how overlapping server writes should merge; applications own reconciliation.\n * See docs/action-session.md for retry, persistence and ownership boundaries.\n */\nexport function createActionSession<T>(initialValue: T): ActionSession<T> {\n let snapshot: ActionSnapshot<T> = { status: \"idle\", value: initialValue };\n let generation = 0;\n let failedAttempt: { task: ActionTask<T>; options: ActionOptions<T> } | undefined;\n const listeners = new Set<() => void>();\n const publish = (next: ActionSnapshot<T>) => {\n snapshot = next;\n for (const listener of [...listeners]) listener();\n };\n const run: ActionSession<T>[\"run\"] = async (task, options = {}) => {\n if (snapshot.status === \"pending\") return { status: \"blocked\" };\n const operationId = ++generation;\n const previous = snapshot.value;\n // Capture the proposal now so retry cannot pick up a caller-mutated options object.\n const proposal = { ...options };\n failedAttempt = undefined;\n publish({ status: \"pending\", value: \"optimisticValue\" in proposal ? proposal.optimisticValue as T : previous, operationId });\n if (generation !== operationId) return { status: \"ignored\" };\n let value: T;\n try {\n value = await task({ operationId });\n } catch (error) {\n if (generation !== operationId) return { status: \"ignored\" };\n // Retrying a mutation must be authorized by the product adapter (idempotency).\n failedAttempt = proposal.retryable ? { task, options: proposal } : undefined;\n publish({ status: \"error\", value: previous, operationId, error });\n return { status: \"error\", error, operationId };\n }\n if (generation !== operationId) return { status: \"ignored\" };\n publish({ status: \"success\", value, operationId });\n return { status: \"success\", value, operationId };\n };\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) { listeners.add(listener); return () => { listeners.delete(listener); }; },\n run,\n retry() {\n if (snapshot.status !== \"error\" || !failedAttempt) return Promise.resolve({ status: \"blocked\" });\n return run(failedAttempt.task, failedAttempt.options);\n },\n reset(value) {\n ++generation;\n failedAttempt = undefined;\n publish({ status: \"idle\", value });\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ActivityDay = Readonly<{
|
|
2
|
+
date: string;
|
|
3
|
+
value: number;
|
|
4
|
+
}>;
|
|
5
|
+
export type ActivityHeatmapDescriptor = Readonly<{
|
|
6
|
+
startDate: string;
|
|
7
|
+
endDate: string;
|
|
8
|
+
days: readonly ActivityDay[];
|
|
9
|
+
thresholds?: readonly [number, number, number];
|
|
10
|
+
weekStartsOn?: 0 | 1;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function resolveActivityHeatmap(input: ActivityHeatmapDescriptor): {
|
|
13
|
+
days: {
|
|
14
|
+
date: string;
|
|
15
|
+
value: number | null;
|
|
16
|
+
level: number;
|
|
17
|
+
row: number;
|
|
18
|
+
column: number;
|
|
19
|
+
}[];
|
|
20
|
+
columns: number;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=activity-heatmap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-heatmap.d.ts","sourceRoot":"","sources":["../src/activity-heatmap.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IAAC,IAAI,EAAC,MAAM,CAAC;IAAA,KAAK,EAAC,MAAM,CAAA;CAAC,CAAC,CAAC;AAC/D,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAAC,SAAS,EAAC,MAAM,CAAC;IAAA,OAAO,EAAC,MAAM,CAAC;IAAA,IAAI,EAAC,SAAS,WAAW,EAAE,CAAC;IAAA,UAAU,CAAC,EAAC,SAAS,CAAC,MAAM,EAAC,MAAM,EAAC,MAAM,CAAC,CAAC;IAAA,YAAY,CAAC,EAAC,CAAC,GAAC,CAAC,CAAA;CAAC,CAAC,CAAC;AAG9K,wBAAgB,sBAAsB,CAAC,KAAK,EAAC,yBAAyB;;;;;;;;;EAWrE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const dayMs = 86400000;
|
|
2
|
+
function dateValue(date) { if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
|
|
3
|
+
throw new TypeError('Use an ISO calendar date'); const time = Date.parse(`${date}T00:00:00Z`); if (!Number.isFinite(time) || new Date(time).toISOString().slice(0, 10) !== date)
|
|
4
|
+
throw new RangeError('Invalid calendar date'); return time; }
|
|
5
|
+
export function resolveActivityHeatmap(input) {
|
|
6
|
+
const start = dateValue(input.startDate), end = dateValue(input.endDate), count = (end - start) / dayMs + 1;
|
|
7
|
+
// A year-sized overview stays bounded for Native accessibility and layout; paginate longer histories in the product.
|
|
8
|
+
if (count < 1 || count > 366)
|
|
9
|
+
throw new RangeError('Activity overview must span 1–366 days');
|
|
10
|
+
const thresholds = input.thresholds ?? [1, 3, 7];
|
|
11
|
+
if (thresholds.length !== 3 || thresholds.some((n, i) => !Number.isFinite(n) || n <= 0 || (i > 0 && n <= thresholds[i - 1])))
|
|
12
|
+
throw new RangeError('Thresholds must be positive and increasing');
|
|
13
|
+
const first = input.weekStartsOn ?? 1;
|
|
14
|
+
if (first !== 0 && first !== 1)
|
|
15
|
+
throw new RangeError('Week starts on Sunday or Monday');
|
|
16
|
+
const values = new Map();
|
|
17
|
+
for (const day of input.days) {
|
|
18
|
+
const time = dateValue(day.date);
|
|
19
|
+
if (time < start || time > end || values.has(day.date))
|
|
20
|
+
throw new RangeError('Activity dates must be unique and within range');
|
|
21
|
+
if (!Number.isFinite(day.value) || day.value < 0)
|
|
22
|
+
throw new RangeError('Activity values must be finite and nonnegative');
|
|
23
|
+
values.set(day.date, day.value);
|
|
24
|
+
}
|
|
25
|
+
const offset = (new Date(start).getUTCDay() - first + 7) % 7;
|
|
26
|
+
const days = Array.from({ length: count }, (_, index) => { const date = new Date(start + index * dayMs).toISOString().slice(0, 10); const value = values.get(date) ?? null; const level = value === null || value === 0 ? 0 : 1 + thresholds.filter(n => value > n).length; return { date, value, level, row: (index + offset) % 7, column: Math.floor((index + offset) / 7) }; });
|
|
27
|
+
return { days, columns: Math.ceil((count + offset) / 7) };
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=activity-heatmap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-heatmap.js","sourceRoot":"","sources":["../src/activity-heatmap.ts"],"names":[],"mappings":"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","sourcesContent":["export type ActivityDay = Readonly<{date:string;value:number}>;\nexport type ActivityHeatmapDescriptor = Readonly<{startDate:string;endDate:string;days:readonly ActivityDay[];thresholds?:readonly [number,number,number];weekStartsOn?:0|1}>;\nconst dayMs=86400000;\nfunction dateValue(date:string){if(!/^\\d{4}-\\d{2}-\\d{2}$/.test(date))throw new TypeError('Use an ISO calendar date');const time=Date.parse(`${date}T00:00:00Z`);if(!Number.isFinite(time)||new Date(time).toISOString().slice(0,10)!==date)throw new RangeError('Invalid calendar date');return time;}\nexport function resolveActivityHeatmap(input:ActivityHeatmapDescriptor){\n const start=dateValue(input.startDate),end=dateValue(input.endDate),count=(end-start)/dayMs+1;\n // A year-sized overview stays bounded for Native accessibility and layout; paginate longer histories in the product.\n if(count<1||count>366)throw new RangeError('Activity overview must span 1–366 days');\n const thresholds=input.thresholds??[1,3,7];if(thresholds.length!==3||thresholds.some((n,i)=>!Number.isFinite(n)||n<=0||(i>0&&n<=thresholds[i-1]!)))throw new RangeError('Thresholds must be positive and increasing');\n const first=input.weekStartsOn??1;if(first!==0&&first!==1)throw new RangeError('Week starts on Sunday or Monday');\n const values=new Map<string,number>();\n for(const day of input.days){const time=dateValue(day.date);if(time<start||time>end||values.has(day.date))throw new RangeError('Activity dates must be unique and within range');if(!Number.isFinite(day.value)||day.value<0)throw new RangeError('Activity values must be finite and nonnegative');values.set(day.date,day.value);}\n const offset=(new Date(start).getUTCDay()-first+7)%7;\n const days=Array.from({length:count},(_,index)=>{const date=new Date(start+index*dayMs).toISOString().slice(0,10);const value=values.get(date)??null;const level=value===null||value===0?0:1+thresholds.filter(n=>value>n).length;return {date,value,level,row:(index+offset)%7,column:Math.floor((index+offset)/7)};});\n return {days,columns:Math.ceil((count+offset)/7)};\n}\n"]}
|
package/dist/auth-screen.d.ts
CHANGED
|
@@ -80,6 +80,10 @@ export declare const authScreenRecipe: {
|
|
|
80
80
|
};
|
|
81
81
|
/** 제공자 버튼 높이는 Provider 계약이 소유한다 — 여기서 다시 정하지 않는다. */
|
|
82
82
|
readonly providerMinHeight: 44;
|
|
83
|
+
readonly mainCard: {
|
|
84
|
+
readonly padding: 16;
|
|
85
|
+
readonly radius: 16;
|
|
86
|
+
};
|
|
83
87
|
readonly densities: {
|
|
84
88
|
readonly regular: {
|
|
85
89
|
readonly heroGap: 16;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-screen.d.ts","sourceRoot":"","sources":["../src/auth-screen.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;CAGoB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;IAI3B,yDAAyD;;;;;;;;;;;;;IAKzD,+CAA+C;;;;;;;IAG/C,qDAAqD
|
|
1
|
+
{"version":3,"file":"auth-screen.d.ts","sourceRoot":"","sources":["../src/auth-screen.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;CAGoB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;IAI3B,yDAAyD;;;;;;;;;;;;;IAKzD,+CAA+C;;;;;;;IAG/C,qDAAqD;;;;;;;;;;;;;;;;;;;;;;CAoB7C,CAAC;AAIX,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,oBAAoB,GAAG,IAAI,CAUnF;AAED,wBAAgB,2BAA2B,CACzC,UAAU,GAAE,oBAAyB,GACpC,4BAA4B,CAc9B;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;CAgBM,CAAC"}
|
package/dist/auth-screen.js
CHANGED
|
@@ -23,6 +23,8 @@ export const authScreenRecipe = {
|
|
|
23
23
|
footerText: typography.caption,
|
|
24
24
|
/** 제공자 버튼 높이는 Provider 계약이 소유한다 — 여기서 다시 정하지 않는다. */
|
|
25
25
|
providerMinHeight: authProviderButtonRecipe.minHeight,
|
|
26
|
+
// 2026-10-01: the action card stays mounted during login so its dimensions and background survive the loader.
|
|
27
|
+
mainCard: { padding: spacing.md, radius: radius.lg },
|
|
26
28
|
densities: {
|
|
27
29
|
regular: {
|
|
28
30
|
heroGap: spacing.md,
|
package/dist/auth-screen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-screen.js","sourceRoot":"","sources":["../src/auth-screen.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport { authProviderButtonRecipe } from \"./provider-button.js\";\nimport { control, layout, radius, spacing, typography } from \"./foundations.js\";\n\n/**\n * 로그인 화면의 골격이다. 제품이 화면을 다시 조립하지 않게 **배치만** 소유한다.\n *\n * 왜 골격인가: 계정이 있는 제품들이 같은 화면을 각자 만들었고, 같은 실수를 각자 했다.\n * 번뚝은 로고·태그라인·설명 두 줄·동의 고지·정책 링크를 위에서부터 쌓아 제공자 버튼을\n * 화면 밖으로 밀어냈고(2026-09-19 iPhone 17 Pro 실측), 그 배치를 따라한 다른 제품도 같은\n * 순서로 쌓였다. 간격과 최대 폭은 제품마다 다를 이유가 없는데 제품마다 다시 정해졌다.\n *\n * 그래서 **두 영역**만 강제한다. 위 영역(`hero` + `main`)은 남는 세로 공간을 전부 차지하고\n * 그 안에서 가운데 정렬한다. 아래 영역(`footer`)은 바닥에 붙는다. 세 영역 이상으로 나누지\n * 않는 이유는, 셋이 되는 순간 어느 것이 주 행동인지가 배치로 드러나지 않아서다.\n *\n * **HJM이 소유하는 것**: 영역 구분, 간격, 최대 폭, 가운데 정렬, 내용이 길어질 때의 스크롤 전환.\n * **제품이 소유하는 것**: 마크 자산, 모든 문구, 제공자 목록과 순서, 정책 링크의 목적지,\n * 그리고 `main`에 무엇을 넣을지(제공자 버튼만, 또는 가입 재개·심사자 입력 같은 제품 덩어리).\n *\n * 이 계약은 화면을 **그리지 않는다.** `AuthProviderButton`·`BottomInfo`·`Agreement`처럼 이미\n * 있는 컴포넌트를 제품이 슬롯에 넣는다. 화면 전체를 한 컴포넌트로 받으면 제품마다 다른\n * 부분(다에리의 중단 거래 복구, 번뚝의 심사자 폼)을 전부 슬롯으로 되돌려 받아야 해서,\n * 결국 이름만 붙은 Stack이 된다.\n */\nexport type AuthScreenDensity = \"compact\" | \"regular\";\n\nexport type AuthScreenDescriptor = Readonly<{\n /**\n * 세로 공간이 좁은 기기에서 간격을 줄인다. 제품이 화면 높이를 보고 고르며, HJM은\n * 기기 종류를 추측하지 않는다(같은 폭이라도 키보드가 올라오면 좁아진다).\n */\n density?: AuthScreenDensity;\n /** 아래 영역(동의 고지·정책 링크)을 둘지. 계정 생성이 없는 화면은 없을 수 있다. */\n hasFooter?: boolean;\n}>;\n\nexport type ResolvedAuthScreenDescriptor = Readonly<{\n density: AuthScreenDensity;\n hasFooter: boolean;\n /** 위 영역 안쪽 간격(마크→제목→설명→주 행동). */\n heroGap: number;\n /** 위 영역과 주 행동 사이. */\n mainGap: number;\n /** 아래 영역을 띄우는 최소 간격. 화면이 길면 그 이상 벌어진다. */\n footerGap: number;\n paddingInline: number;\n paddingBlock: number;\n maxWidth: number;\n}>;\n\nexport const authScreenDefaults = {\n density: \"regular\",\n hasFooter: true,\n} as const satisfies Required<AuthScreenDescriptor>;\n\n/**\n * 최대 폭 26rem(416)은 `Container`의 reading(720)보다 좁다. 제공자 버튼이 노트북 폭만큼\n * 길어지면 누를 곳이 아니라 띠로 보이기 때문이고, 이 화면의 내용은 버튼 한 줄기뿐이라\n * 읽기 폭을 쓸 이유가 없다.\n */\nexport const authScreenRecipe = {\n slots: [\"root\", \"hero\", \"mark\", \"title\", \"description\", \"main\", \"footer\"] as const,\n defaults: authScreenDefaults,\n maxWidth: 416,\n /** 마크는 아이콘(48)보다 크고 일러스트(120+)보다 작다 — 제품을 알아볼 최소 크기다. */\n markSize: 72,\n markRadius: radius.lg,\n title: typography.titleLarge,\n description: typography.body,\n /** 정책 링크는 글자 높이만큼만 누를 수 있으면 44pt 기준에 못 미친다. */\n footerMinTouchTarget: control.minTouchTarget,\n footerText: typography.caption,\n /** 제공자 버튼 높이는 Provider 계약이 소유한다 — 여기서 다시 정하지 않는다. */\n providerMinHeight: authProviderButtonRecipe.minHeight,\n densities: {\n regular: {\n heroGap: spacing.md,\n mainGap: spacing.xl,\n footerGap: spacing.xl,\n paddingInline: layout.pagePadding.regular,\n paddingBlock: spacing.xxxl,\n },\n compact: {\n heroGap: spacing.xs,\n mainGap: spacing.md,\n footerGap: spacing.md,\n paddingInline: layout.pagePadding.compact,\n paddingBlock: spacing.lg,\n },\n },\n} as const;\n\nconst densities = new Set<AuthScreenDensity>([\"compact\", \"regular\"]);\n\nexport function validateAuthScreenDescriptor(descriptor: AuthScreenDescriptor): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"AuthScreen descriptor must be an object\");\n }\n if (descriptor.density !== undefined && !densities.has(descriptor.density)) {\n throw new TypeError(`Unsupported AuthScreen density: ${String(descriptor.density)}`);\n }\n if (descriptor.hasFooter !== undefined && typeof descriptor.hasFooter !== \"boolean\") {\n throw new TypeError(\"AuthScreen hasFooter must be a boolean\");\n }\n}\n\nexport function resolveAuthScreenDescriptor(\n descriptor: AuthScreenDescriptor = {},\n): ResolvedAuthScreenDescriptor {\n validateAuthScreenDescriptor(descriptor);\n const density = descriptor.density ?? authScreenDefaults.density;\n const scale = authScreenRecipe.densities[density];\n return {\n density,\n hasFooter: descriptor.hasFooter ?? authScreenDefaults.hasFooter,\n heroGap: scale.heroGap,\n mainGap: scale.mainGap,\n footerGap: scale.footerGap,\n paddingInline: scale.paddingInline,\n paddingBlock: scale.paddingBlock,\n maxWidth: authScreenRecipe.maxWidth,\n };\n}\n\nexport const authScreenBehavior = {\n controlled: [],\n inputs: [\"density\", \"hasFooter\"],\n events: [],\n stateAxes: {},\n web: { roles: [\"main\"], keyboard: [], focus: \"none\" },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"the-hero-and-the-main-action-stay-one-vertically-centred-block\",\n \"the-footer-sits-at-the-bottom-and-never-overlaps-the-main-action\",\n \"content-taller-than-the-viewport-scrolls-instead-of-pushing-the-footer-off-screen\",\n \"the-product-owns-every-string-and-the-mark-asset-while-the-layout-stays-here\",\n \"provider-button-height-comes-from-the-provider-contract-not-from-this-one\",\n \"compact-density-shrinks-gaps-without-changing-the-two-region-structure\",\n \"policy-links-in-the-footer-keep-the-minimum-touch-target\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
1
|
+
{"version":3,"file":"auth-screen.js","sourceRoot":"","sources":["../src/auth-screen.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BehaviorContract } from \"./behaviors.js\";\nimport { authProviderButtonRecipe } from \"./provider-button.js\";\nimport { control, layout, radius, spacing, typography } from \"./foundations.js\";\n\n/**\n * 로그인 화면의 골격이다. 제품이 화면을 다시 조립하지 않게 **배치만** 소유한다.\n *\n * 왜 골격인가: 계정이 있는 제품들이 같은 화면을 각자 만들었고, 같은 실수를 각자 했다.\n * 번뚝은 로고·태그라인·설명 두 줄·동의 고지·정책 링크를 위에서부터 쌓아 제공자 버튼을\n * 화면 밖으로 밀어냈고(2026-09-19 iPhone 17 Pro 실측), 그 배치를 따라한 다른 제품도 같은\n * 순서로 쌓였다. 간격과 최대 폭은 제품마다 다를 이유가 없는데 제품마다 다시 정해졌다.\n *\n * 그래서 **두 영역**만 강제한다. 위 영역(`hero` + `main`)은 남는 세로 공간을 전부 차지하고\n * 그 안에서 가운데 정렬한다. 아래 영역(`footer`)은 바닥에 붙는다. 세 영역 이상으로 나누지\n * 않는 이유는, 셋이 되는 순간 어느 것이 주 행동인지가 배치로 드러나지 않아서다.\n *\n * **HJM이 소유하는 것**: 영역 구분, 간격, 최대 폭, 가운데 정렬, 내용이 길어질 때의 스크롤 전환.\n * **제품이 소유하는 것**: 마크 자산, 모든 문구, 제공자 목록과 순서, 정책 링크의 목적지,\n * 그리고 `main`에 무엇을 넣을지(제공자 버튼만, 또는 가입 재개·심사자 입력 같은 제품 덩어리).\n *\n * 이 계약은 화면을 **그리지 않는다.** `AuthProviderButton`·`BottomInfo`·`Agreement`처럼 이미\n * 있는 컴포넌트를 제품이 슬롯에 넣는다. 화면 전체를 한 컴포넌트로 받으면 제품마다 다른\n * 부분(다에리의 중단 거래 복구, 번뚝의 심사자 폼)을 전부 슬롯으로 되돌려 받아야 해서,\n * 결국 이름만 붙은 Stack이 된다.\n */\nexport type AuthScreenDensity = \"compact\" | \"regular\";\n\nexport type AuthScreenDescriptor = Readonly<{\n /**\n * 세로 공간이 좁은 기기에서 간격을 줄인다. 제품이 화면 높이를 보고 고르며, HJM은\n * 기기 종류를 추측하지 않는다(같은 폭이라도 키보드가 올라오면 좁아진다).\n */\n density?: AuthScreenDensity;\n /** 아래 영역(동의 고지·정책 링크)을 둘지. 계정 생성이 없는 화면은 없을 수 있다. */\n hasFooter?: boolean;\n}>;\n\nexport type ResolvedAuthScreenDescriptor = Readonly<{\n density: AuthScreenDensity;\n hasFooter: boolean;\n /** 위 영역 안쪽 간격(마크→제목→설명→주 행동). */\n heroGap: number;\n /** 위 영역과 주 행동 사이. */\n mainGap: number;\n /** 아래 영역을 띄우는 최소 간격. 화면이 길면 그 이상 벌어진다. */\n footerGap: number;\n paddingInline: number;\n paddingBlock: number;\n maxWidth: number;\n}>;\n\nexport const authScreenDefaults = {\n density: \"regular\",\n hasFooter: true,\n} as const satisfies Required<AuthScreenDescriptor>;\n\n/**\n * 최대 폭 26rem(416)은 `Container`의 reading(720)보다 좁다. 제공자 버튼이 노트북 폭만큼\n * 길어지면 누를 곳이 아니라 띠로 보이기 때문이고, 이 화면의 내용은 버튼 한 줄기뿐이라\n * 읽기 폭을 쓸 이유가 없다.\n */\nexport const authScreenRecipe = {\n slots: [\"root\", \"hero\", \"mark\", \"title\", \"description\", \"main\", \"footer\"] as const,\n defaults: authScreenDefaults,\n maxWidth: 416,\n /** 마크는 아이콘(48)보다 크고 일러스트(120+)보다 작다 — 제품을 알아볼 최소 크기다. */\n markSize: 72,\n markRadius: radius.lg,\n title: typography.titleLarge,\n description: typography.body,\n /** 정책 링크는 글자 높이만큼만 누를 수 있으면 44pt 기준에 못 미친다. */\n footerMinTouchTarget: control.minTouchTarget,\n footerText: typography.caption,\n /** 제공자 버튼 높이는 Provider 계약이 소유한다 — 여기서 다시 정하지 않는다. */\n providerMinHeight: authProviderButtonRecipe.minHeight,\n // 2026-10-01: the action card stays mounted during login so its dimensions and background survive the loader.\n mainCard: { padding: spacing.md, radius: radius.lg },\n densities: {\n regular: {\n heroGap: spacing.md,\n mainGap: spacing.xl,\n footerGap: spacing.xl,\n paddingInline: layout.pagePadding.regular,\n paddingBlock: spacing.xxxl,\n },\n compact: {\n heroGap: spacing.xs,\n mainGap: spacing.md,\n footerGap: spacing.md,\n paddingInline: layout.pagePadding.compact,\n paddingBlock: spacing.lg,\n },\n },\n} as const;\n\nconst densities = new Set<AuthScreenDensity>([\"compact\", \"regular\"]);\n\nexport function validateAuthScreenDescriptor(descriptor: AuthScreenDescriptor): void {\n if (descriptor === null || typeof descriptor !== \"object\") {\n throw new TypeError(\"AuthScreen descriptor must be an object\");\n }\n if (descriptor.density !== undefined && !densities.has(descriptor.density)) {\n throw new TypeError(`Unsupported AuthScreen density: ${String(descriptor.density)}`);\n }\n if (descriptor.hasFooter !== undefined && typeof descriptor.hasFooter !== \"boolean\") {\n throw new TypeError(\"AuthScreen hasFooter must be a boolean\");\n }\n}\n\nexport function resolveAuthScreenDescriptor(\n descriptor: AuthScreenDescriptor = {},\n): ResolvedAuthScreenDescriptor {\n validateAuthScreenDescriptor(descriptor);\n const density = descriptor.density ?? authScreenDefaults.density;\n const scale = authScreenRecipe.densities[density];\n return {\n density,\n hasFooter: descriptor.hasFooter ?? authScreenDefaults.hasFooter,\n heroGap: scale.heroGap,\n mainGap: scale.mainGap,\n footerGap: scale.footerGap,\n paddingInline: scale.paddingInline,\n paddingBlock: scale.paddingBlock,\n maxWidth: authScreenRecipe.maxWidth,\n };\n}\n\nexport const authScreenBehavior = {\n controlled: [],\n inputs: [\"density\", \"hasFooter\"],\n events: [],\n stateAxes: {},\n web: { roles: [\"main\"], keyboard: [], focus: \"none\" },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"the-hero-and-the-main-action-stay-one-vertically-centred-block\",\n \"the-footer-sits-at-the-bottom-and-never-overlaps-the-main-action\",\n \"content-taller-than-the-viewport-scrolls-instead-of-pushing-the-footer-off-screen\",\n \"the-product-owns-every-string-and-the-mark-asset-while-the-layout-stays-here\",\n \"provider-button-height-comes-from-the-provider-contract-not-from-this-one\",\n \"compact-density-shrinks-gaps-without-changing-the-two-region-structure\",\n \"policy-links-in-the-footer-keep-the-minimum-touch-target\",\n ],\n} as const satisfies BehaviorContract;\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Avatar owns the accessible name; fallback artwork must remain decorative. */
|
|
2
|
+
export type AvatarFallbackContext = Readonly<{
|
|
3
|
+
size: number;
|
|
4
|
+
decorative: true;
|
|
5
|
+
}>;
|
|
6
|
+
export type BlobatarFallbackOptions = Readonly<{
|
|
7
|
+
/** Stable public product identifier, never a default email or display name. */
|
|
8
|
+
seed: string;
|
|
9
|
+
expression?: "idle" | "happy";
|
|
10
|
+
}>;
|
|
11
|
+
export declare function resolveBlobatarFallback(options: BlobatarFallbackOptions): Required<BlobatarFallbackOptions>;
|
|
12
|
+
export type BlobatarMotionExpression = 'idle' | 'happy' | 'sad' | 'surprised' | 'wink' | 'sleepy' | 'thinking';
|
|
13
|
+
export type BlobatarMotionOptions = Readonly<{
|
|
14
|
+
seed: string;
|
|
15
|
+
expression?: BlobatarMotionExpression;
|
|
16
|
+
active?: boolean;
|
|
17
|
+
visible?: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
export declare function resolveBlobatarMotion(options: BlobatarMotionOptions): {
|
|
20
|
+
seed: string;
|
|
21
|
+
expression: BlobatarMotionExpression;
|
|
22
|
+
active: boolean;
|
|
23
|
+
visible: boolean;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=avatar-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-fallback.d.ts","sourceRoot":"","sources":["../src/avatar-fallback.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AACjF,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B,CAAC,CAAC;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,uBAAuB,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAM3G;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAC,OAAO,GAAC,KAAK,GAAC,WAAW,GAAC,MAAM,GAAC,QAAQ,GAAC,UAAU,CAAC;AACnG,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAAC,IAAI,EAAC,MAAM,CAAC;IAAA,UAAU,CAAC,EAAC,wBAAwB,CAAC;IAAA,MAAM,CAAC,EAAC,OAAO,CAAC;IAAA,OAAO,CAAC,EAAC,OAAO,CAAA;CAAC,CAAC,CAAC;AAClI,wBAAgB,qBAAqB,CAAC,OAAO,EAAC,qBAAqB;;;;;EAIlE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function resolveBlobatarFallback(options) {
|
|
2
|
+
if (typeof options.seed !== "string" || !options.seed.trim())
|
|
3
|
+
throw new TypeError("Avatar seed must not be empty");
|
|
4
|
+
const expression = options.expression ?? "idle";
|
|
5
|
+
if (expression !== "idle" && expression !== "happy")
|
|
6
|
+
throw new TypeError("Unsupported avatar expression");
|
|
7
|
+
// Preserve exact identifiers: normalizing case would merge distinct accounts.
|
|
8
|
+
return { seed: options.seed, expression };
|
|
9
|
+
}
|
|
10
|
+
export function resolveBlobatarMotion(options) {
|
|
11
|
+
resolveBlobatarFallback({ seed: options.seed });
|
|
12
|
+
const expression = options.expression ?? 'idle';
|
|
13
|
+
if (!['idle', 'happy', 'sad', 'surprised', 'wink', 'sleepy', 'thinking'].includes(expression))
|
|
14
|
+
throw new TypeError('Unsupported animated avatar expression');
|
|
15
|
+
return { seed: options.seed, expression, active: options.active ?? false, visible: options.visible ?? true };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=avatar-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-fallback.js","sourceRoot":"","sources":["../src/avatar-fallback.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,uBAAuB,CAAC,OAAgC;IACtE,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACnH,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC;IAChD,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,OAAO;QAAE,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IAC1G,8EAA8E;IAC9E,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;AAC5C,CAAC;AAID,MAAM,UAAU,qBAAqB,CAAC,OAA6B;IAClE,uBAAuB,CAAC,EAAC,IAAI,EAAC,OAAO,CAAC,IAAI,EAAC,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAC,OAAO,CAAC,UAAU,IAAE,MAAM,CAAC;IAAA,IAAG,CAAC,CAAC,MAAM,EAAC,OAAO,EAAC,KAAK,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAC,MAAM,IAAI,SAAS,CAAC,wCAAwC,CAAC,CAAC;IACjM,OAAO,EAAC,IAAI,EAAC,OAAO,CAAC,IAAI,EAAC,UAAU,EAAC,MAAM,EAAC,OAAO,CAAC,MAAM,IAAE,KAAK,EAAC,OAAO,EAAC,OAAO,CAAC,OAAO,IAAE,IAAI,EAAC,CAAC;AAClG,CAAC","sourcesContent":["/** Avatar owns the accessible name; fallback artwork must remain decorative. */\nexport type AvatarFallbackContext = Readonly<{ size: number; decorative: true }>;\nexport type BlobatarFallbackOptions = Readonly<{\n /** Stable public product identifier, never a default email or display name. */\n seed: string;\n expression?: \"idle\" | \"happy\";\n}>;\nexport function resolveBlobatarFallback(options: BlobatarFallbackOptions): Required<BlobatarFallbackOptions> {\n if (typeof options.seed !== \"string\" || !options.seed.trim()) throw new TypeError(\"Avatar seed must not be empty\");\n const expression = options.expression ?? \"idle\";\n if (expression !== \"idle\" && expression !== \"happy\") throw new TypeError(\"Unsupported avatar expression\");\n // Preserve exact identifiers: normalizing case would merge distinct accounts.\n return { seed: options.seed, expression };\n}\n\nexport type BlobatarMotionExpression = 'idle'|'happy'|'sad'|'surprised'|'wink'|'sleepy'|'thinking';\nexport type BlobatarMotionOptions = Readonly<{seed:string;expression?:BlobatarMotionExpression;active?:boolean;visible?:boolean}>;\nexport function resolveBlobatarMotion(options:BlobatarMotionOptions){\n resolveBlobatarFallback({seed:options.seed});\n const expression=options.expression??'idle';if(!['idle','happy','sad','surprised','wink','sleepy','thinking'].includes(expression))throw new TypeError('Unsupported animated avatar expression');\n return {seed:options.seed,expression,active:options.active??false,visible:options.visible??true};\n}\n"]}
|
package/dist/behaviors.d.ts
CHANGED
|
@@ -598,8 +598,8 @@ export declare const behaviorRegistry: {
|
|
|
598
598
|
readonly overflowPolicy: readonly ["discard-oldest", "discard-newest"];
|
|
599
599
|
};
|
|
600
600
|
readonly defaults: {
|
|
601
|
-
readonly durationMs:
|
|
602
|
-
readonly minimumDurationMs:
|
|
601
|
+
readonly durationMs: 3000;
|
|
602
|
+
readonly minimumDurationMs: 3000;
|
|
603
603
|
readonly priority: "normal";
|
|
604
604
|
readonly dismissOnAction: true;
|
|
605
605
|
readonly maxVisible: 1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BottomNavigationPresentation = "bar" | "floating";
|
|
1
|
+
export type BottomNavigationPresentation = "bar" | "floating" | "capsule";
|
|
2
2
|
export type BottomNavigationDistribution = "equal" | "center-gap";
|
|
3
3
|
export type BottomNavigationDensity = "compact" | "regular";
|
|
4
4
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-navigation-defaults.d.ts","sourceRoot":"","sources":["../src/bottom-navigation-defaults.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,4BAA4B,GAAG,KAAK,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"bottom-navigation-defaults.d.ts","sourceRoot":"","sources":["../src/bottom-navigation-defaults.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,4BAA4B,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAC1E,MAAM,MAAM,4BAA4B,GAAG,OAAO,GAAG,YAAY,CAAC;AAClE,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,8BAA8B;;;;CAQzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-navigation-defaults.js","sourceRoot":"","sources":["../src/bottom-navigation-defaults.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,YAAY,EAAE,KAAK;IACnB,YAAY,EAAE,OAAO;IACrB,OAAO,EAAE,SAAS;CAKlB,CAAC","sourcesContent":["export type BottomNavigationPresentation = \"bar\" | \"floating\";\nexport type BottomNavigationDistribution = \"equal\" | \"center-gap\";\nexport type BottomNavigationDensity = \"compact\" | \"regular\";\n\n/**\n * Shared defaults kept outside the recipe barrel so behavior-only consumers\n * do not pull the complete visual recipe registry into their import graph.\n */\nexport const bottomNavigationRecipeDefaults = {\n presentation: \"bar\",\n distribution: \"equal\",\n density: \"regular\",\n} as const satisfies Readonly<{\n presentation: BottomNavigationPresentation;\n distribution: BottomNavigationDistribution;\n density: BottomNavigationDensity;\n}>;\n"]}
|
|
1
|
+
{"version":3,"file":"bottom-navigation-defaults.js","sourceRoot":"","sources":["../src/bottom-navigation-defaults.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,YAAY,EAAE,KAAK;IACnB,YAAY,EAAE,OAAO;IACrB,OAAO,EAAE,SAAS;CAKlB,CAAC","sourcesContent":["export type BottomNavigationPresentation = \"bar\" | \"floating\" | \"capsule\";\nexport type BottomNavigationDistribution = \"equal\" | \"center-gap\";\nexport type BottomNavigationDensity = \"compact\" | \"regular\";\n\n/**\n * Shared defaults kept outside the recipe barrel so behavior-only consumers\n * do not pull the complete visual recipe registry into their import graph.\n */\nexport const bottomNavigationRecipeDefaults = {\n presentation: \"bar\",\n distribution: \"equal\",\n density: \"regular\",\n} as const satisfies Readonly<{\n presentation: BottomNavigationPresentation;\n distribution: BottomNavigationDistribution;\n density: BottomNavigationDensity;\n}>;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-navigation.d.ts","sourceRoot":"","sources":["../src/bottom-navigation.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"bottom-navigation.d.ts","sourceRoot":"","sources":["../src/bottom-navigation.ts"],"names":[],"mappings":"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"}
|
|
@@ -93,7 +93,7 @@ function validateItem(item) {
|
|
|
93
93
|
if (item.badge !== undefined)
|
|
94
94
|
validateBadge(item.badge);
|
|
95
95
|
}
|
|
96
|
-
const presentations = new Set(["bar", "floating"]);
|
|
96
|
+
const presentations = new Set(["bar", "floating", "capsule"]);
|
|
97
97
|
const distributions = new Set(["equal", "center-gap"]);
|
|
98
98
|
const densities = new Set(["compact", "regular"]);
|
|
99
99
|
const directions = new Set(["ltr", "rtl"]);
|
|
@@ -130,6 +130,10 @@ export function validateBottomNavigationConfiguration(configuration, itemCount)
|
|
|
130
130
|
!keyboardBehaviors.has(configuration.keyboardBehavior)) {
|
|
131
131
|
throw new TypeError(`Unsupported BottomNavigation keyboardBehavior: ${String(configuration.keyboardBehavior)}`);
|
|
132
132
|
}
|
|
133
|
+
// Capsule actions sit beside the destination surface, so reserving a central gap would duplicate their space.
|
|
134
|
+
if (configuration.presentation === "capsule" && configuration.distribution === "center-gap") {
|
|
135
|
+
throw new TypeError("BottomNavigation capsule uses an adjacent action, not center-gap");
|
|
136
|
+
}
|
|
133
137
|
if (configuration.distribution === "center-gap" && itemCount % 2 !== 0) {
|
|
134
138
|
throw new RangeError("BottomNavigation center-gap distribution requires an even destination count");
|
|
135
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-navigation.js","sourceRoot":"","sources":["../src/bottom-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type BottomNavigationDensity,\n type BottomNavigationDistribution,\n type BottomNavigationPresentation,\n bottomNavigationRecipeDefaults,\n} from \"./bottom-navigation-defaults.js\";\nimport { formatCounterBadgeCount } from \"./counter-badge.js\";\nimport {\n type SemanticIconName,\n} from \"./icon.js\";\nimport type { DesignSystemDirection } from \"./design-system-provider.js\";\n\nexport type BottomNavigationDirection = DesignSystemDirection;\nexport type BottomNavigationKeyboardBehavior = \"hide\" | \"remain\";\n\nexport type BottomNavigationConfiguration = Readonly<{\n presentation?: BottomNavigationPresentation;\n distribution?: BottomNavigationDistribution;\n density?: BottomNavigationDensity;\n direction?: BottomNavigationDirection;\n keyboardBehavior?: BottomNavigationKeyboardBehavior;\n}>;\n\nexport type ResolvedBottomNavigationConfiguration = Readonly<{\n presentation: BottomNavigationPresentation;\n distribution: BottomNavigationDistribution;\n density: BottomNavigationDensity;\n direction: BottomNavigationDirection;\n keyboardBehavior: BottomNavigationKeyboardBehavior;\n}>;\n\n/**\n * Bottom navigation only accepts numeric counters. A silent status dot is too\n * ambiguous for a persistent, top-level destination.\n */\nexport type BottomNavigationCounterBadge = Readonly<{\n count: number;\n max?: number;\n /** Product-localized copy, for example \"3 unread messages\". */\n accessibilityLabel: string;\n}>;\n\n/**\n * BottomNavigation owns icon appearance through its density and state recipe.\n * Products may extend the semantic name registry, but never override size,\n * tone, stroke weight, accessibility, or logical direction at the call site.\n */\nexport type BottomNavigationIconDescriptor<\n IconName extends string = SemanticIconName,\n> = Readonly<{\n name: IconName;\n decorative?: true;\n accessibilityLabel?: never;\n size?: never;\n tone?: never;\n weight?: never;\n directionality?: never;\n}>;\n\nexport type ResolvedBottomNavigationIconDescriptor<\n IconName extends string = SemanticIconName,\n> = Readonly<{\n name: IconName;\n decorative: true;\n}>;\n\n/**\n * Items are stable top-level destinations, never actions. The icon is always\n * decorative because the visible/item accessibility label already names it.\n */\nexport type BottomNavigationItemDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n id: Key;\n label: string;\n accessibilityLabel?: string;\n icon: BottomNavigationIconDescriptor<IconName>;\n badge?: BottomNavigationCounterBadge;\n disabled?: boolean;\n}>;\n\n/** The router owns selectedKey; the design-system never keeps a second copy. */\nexport type BottomNavigationDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n accessibilityLabel: string;\n items: readonly BottomNavigationItemDescriptor<Key, IconName>[];\n selectedKey: Key;\n}>;\n\n/**\n * The resolved badge intentionally has no accessible label. Renderers hide its\n * subtree and use only the item's resolvedAccessibilityLabel, preventing the\n * counter from being announced twice.\n */\nexport type ResolvedBottomNavigationCounterBadge = Readonly<{\n visibleLabel: string;\n hiddenFromAccessibility: true;\n}>;\n\nexport type ResolvedBottomNavigationItemDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n id: Key;\n label: string;\n icon: ResolvedBottomNavigationIconDescriptor<IconName>;\n disabled: boolean;\n badge: ResolvedBottomNavigationCounterBadge | null;\n resolvedAccessibilityLabel: string;\n}>;\n\nexport type ResolvedBottomNavigationDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n accessibilityLabel: string;\n items: readonly ResolvedBottomNavigationItemDescriptor<Key, IconName>[];\n selectedKey: Key;\n}>;\n\nexport type BottomNavigationActivation<Key extends string = string> = Readonly<{\n key: Key;\n reason: \"navigate\" | \"reselect\";\n}>;\n\n/**\n * `maxItems` follows the platform bars rather than Material's five-destination\n * guidance: iOS tab bars and Android navigation bars both render six labelled\n * destinations without collapsing, and products that ship six need the shared\n * contract to describe them instead of routing around it.\n */\nexport const bottomNavigationBehaviorDefaults = {\n direction: \"ltr\",\n keyboardBehavior: \"hide\",\n minItems: 2,\n maxItems: 6,\n} as const satisfies Readonly<{\n direction: BottomNavigationDirection;\n keyboardBehavior: BottomNavigationKeyboardBehavior;\n minItems: number;\n maxItems: number;\n}>;\n\nfunction validateTrimmedCopy(value: unknown, field: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`${field} must not be empty`);\n }\n if (value !== value.trim()) {\n throw new TypeError(`${field} must not start or end with whitespace`);\n }\n}\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nconst configurationKeys = new Set([\n \"presentation\",\n \"distribution\",\n \"density\",\n \"direction\",\n \"keyboardBehavior\",\n]);\nconst descriptorKeys = new Set([\"accessibilityLabel\", \"items\", \"selectedKey\"]);\nconst itemKeys = new Set([\n \"id\",\n \"label\",\n \"accessibilityLabel\",\n \"icon\",\n \"badge\",\n \"disabled\",\n]);\nconst iconKeys = new Set([\"name\", \"decorative\"]);\nconst badgeKeys = new Set([\"count\", \"max\", \"accessibilityLabel\"]);\n\nfunction rejectUnknownKeys(\n value: Readonly<Record<string, unknown>>,\n allowed: ReadonlySet<string>,\n field: string,\n): void {\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n throw new TypeError(`Unsupported BottomNavigation ${field} field: ${key}`);\n }\n }\n}\n\nfunction normalizedCopy(value: string): string {\n return value.normalize(\"NFKC\").toLocaleLowerCase();\n}\n\nfunction validateBadge(badge: BottomNavigationCounterBadge): void {\n if (!isObject(badge)) {\n throw new TypeError(\"BottomNavigation badge must be an object\");\n }\n rejectUnknownKeys(badge, badgeKeys, \"badge\");\n if (!Number.isSafeInteger(badge.count) || badge.count < 0) {\n throw new TypeError(\"BottomNavigation badge count must be a non-negative safe integer\");\n }\n if (\n badge.max !== undefined &&\n (!Number.isSafeInteger(badge.max) || badge.max < 1)\n ) {\n throw new TypeError(\"BottomNavigation badge max must be a positive safe integer\");\n }\n validateTrimmedCopy(\n badge.accessibilityLabel,\n \"BottomNavigation badge accessibilityLabel\",\n );\n}\n\nfunction validateItem<Key extends string, IconName extends string>(\n item: BottomNavigationItemDescriptor<Key, IconName>,\n): void {\n if (!isObject(item)) {\n throw new TypeError(\"BottomNavigation item must be an object\");\n }\n rejectUnknownKeys(item, itemKeys, \"item\");\n validateTrimmedCopy(item.id, \"BottomNavigation item id\");\n validateTrimmedCopy(item.label, \"BottomNavigation item label\");\n if (item.accessibilityLabel !== undefined) {\n validateTrimmedCopy(\n item.accessibilityLabel,\n \"BottomNavigation item accessibilityLabel\",\n );\n if (\n !normalizedCopy(item.accessibilityLabel).includes(\n normalizedCopy(item.label),\n )\n ) {\n throw new TypeError(\n \"BottomNavigation item accessibilityLabel must include the visible label\",\n );\n }\n }\n if (!isObject(item.icon)) {\n throw new TypeError(\"BottomNavigation item icon must be an object\");\n }\n if (item.icon.decorative !== undefined && item.icon.decorative !== true) {\n throw new TypeError(\"BottomNavigation item icon must be decorative\");\n }\n rejectUnknownKeys(item.icon, iconKeys, \"item icon\");\n validateTrimmedCopy(item.icon.name, \"BottomNavigation item icon name\");\n if (item.disabled !== undefined && typeof item.disabled !== \"boolean\") {\n throw new TypeError(\"BottomNavigation item disabled must be a boolean\");\n }\n if (item.badge !== undefined) validateBadge(item.badge);\n}\n\nconst presentations = new Set<BottomNavigationPresentation>([\"bar\", \"floating\"]);\nconst distributions = new Set<BottomNavigationDistribution>([\"equal\", \"center-gap\"]);\nconst densities = new Set<BottomNavigationDensity>([\"compact\", \"regular\"]);\nconst directions = new Set<BottomNavigationDirection>([\"ltr\", \"rtl\"]);\nconst keyboardBehaviors = new Set<BottomNavigationKeyboardBehavior>([\n \"hide\",\n \"remain\",\n]);\n\nexport function validateBottomNavigationConfiguration(\n configuration: BottomNavigationConfiguration,\n itemCount: number,\n): void {\n if (!isObject(configuration)) {\n throw new TypeError(\"BottomNavigation configuration must be an object\");\n }\n rejectUnknownKeys(configuration, configurationKeys, \"configuration\");\n if (\n !Number.isSafeInteger(itemCount) ||\n itemCount < bottomNavigationBehaviorDefaults.minItems ||\n itemCount > bottomNavigationBehaviorDefaults.maxItems\n ) {\n throw new RangeError(\n `BottomNavigation configuration requires ${bottomNavigationBehaviorDefaults.minItems} to ${bottomNavigationBehaviorDefaults.maxItems} destinations`,\n );\n }\n if (\n configuration.presentation !== undefined &&\n !presentations.has(configuration.presentation)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation presentation: ${String(configuration.presentation)}`,\n );\n }\n if (\n configuration.distribution !== undefined &&\n !distributions.has(configuration.distribution)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation distribution: ${String(configuration.distribution)}`,\n );\n }\n if (configuration.density !== undefined && !densities.has(configuration.density)) {\n throw new TypeError(\n `Unsupported BottomNavigation density: ${String(configuration.density)}`,\n );\n }\n if (\n configuration.direction !== undefined &&\n !directions.has(configuration.direction)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation direction: ${String(configuration.direction)}`,\n );\n }\n if (\n configuration.keyboardBehavior !== undefined &&\n !keyboardBehaviors.has(configuration.keyboardBehavior)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation keyboardBehavior: ${String(configuration.keyboardBehavior)}`,\n );\n }\n if (configuration.distribution === \"center-gap\" && itemCount % 2 !== 0) {\n throw new RangeError(\n \"BottomNavigation center-gap distribution requires an even destination count\",\n );\n }\n}\n\nexport function resolveBottomNavigationConfiguration(\n configuration: BottomNavigationConfiguration,\n itemCount: number,\n): ResolvedBottomNavigationConfiguration {\n validateBottomNavigationConfiguration(configuration, itemCount);\n return {\n presentation:\n configuration.presentation ?? bottomNavigationRecipeDefaults.presentation,\n distribution:\n configuration.distribution ?? bottomNavigationRecipeDefaults.distribution,\n density: configuration.density ?? bottomNavigationRecipeDefaults.density,\n direction: configuration.direction ?? bottomNavigationBehaviorDefaults.direction,\n keyboardBehavior:\n configuration.keyboardBehavior ??\n bottomNavigationBehaviorDefaults.keyboardBehavior,\n };\n}\n\nexport function validateBottomNavigationDescriptor<\n Key extends string,\n IconName extends string,\n>(descriptor: BottomNavigationDescriptor<Key, IconName>): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"BottomNavigation descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, descriptorKeys, \"descriptor\");\n validateTrimmedCopy(\n descriptor.accessibilityLabel,\n \"BottomNavigation accessibilityLabel\",\n );\n if (!Array.isArray(descriptor.items)) {\n throw new TypeError(\"BottomNavigation items must be an array\");\n }\n validateTrimmedCopy(\n descriptor.selectedKey,\n \"BottomNavigation selectedKey\",\n );\n if (\n descriptor.items.length < bottomNavigationBehaviorDefaults.minItems ||\n descriptor.items.length > bottomNavigationBehaviorDefaults.maxItems\n ) {\n throw new RangeError(\n `BottomNavigation must contain ${bottomNavigationBehaviorDefaults.minItems} to ${bottomNavigationBehaviorDefaults.maxItems} destinations`,\n );\n }\n\n const ids = new Set<Key>();\n for (const item of descriptor.items) {\n validateItem(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate BottomNavigation item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n\n const selected = descriptor.items.find(\n (item) => item.id === descriptor.selectedKey,\n );\n if (!selected) {\n throw new RangeError(\n `BottomNavigation selectedKey must exist: ${descriptor.selectedKey}`,\n );\n }\n if (selected.disabled) {\n throw new RangeError(\"BottomNavigation selected destination must be enabled\");\n }\n}\n\nexport function resolveBottomNavigationItem<\n Key extends string,\n IconName extends string,\n>(\n item: BottomNavigationItemDescriptor<Key, IconName>,\n): ResolvedBottomNavigationItemDescriptor<Key, IconName> {\n validateItem(item);\n const baseAccessibilityLabel = item.accessibilityLabel ?? item.label;\n const visibleBadgeLabel = item.badge\n ? formatCounterBadgeCount(item.badge.count, item.badge.max)\n : null;\n const badge = visibleBadgeLabel\n ? ({\n visibleLabel: visibleBadgeLabel,\n hiddenFromAccessibility: true,\n } as const)\n : null;\n\n return {\n id: item.id,\n label: item.label,\n icon: { name: item.icon.name, decorative: true },\n disabled: item.disabled ?? false,\n badge,\n resolvedAccessibilityLabel:\n badge && item.badge\n ? `${baseAccessibilityLabel}, ${item.badge.accessibilityLabel}`\n : baseAccessibilityLabel,\n };\n}\n\nexport function resolveBottomNavigationDescriptor<\n Key extends string,\n IconName extends string,\n>(\n descriptor: BottomNavigationDescriptor<Key, IconName>,\n): ResolvedBottomNavigationDescriptor<Key, IconName> {\n validateBottomNavigationDescriptor(descriptor);\n return {\n accessibilityLabel: descriptor.accessibilityLabel,\n items: descriptor.items.map(resolveBottomNavigationItem),\n selectedKey: descriptor.selectedKey,\n };\n}\n\n/**\n * Produces navigation intent without mutating selectedKey. A renderer forwards\n * the request to its router, which updates selectedKey only after navigation.\n */\nexport function resolveBottomNavigationActivation<\n Key extends string,\n IconName extends string,\n>(\n descriptor: BottomNavigationDescriptor<Key, IconName>,\n key: Key,\n): BottomNavigationActivation<Key> | null {\n validateBottomNavigationDescriptor(descriptor);\n const item = descriptor.items.find((candidate) => candidate.id === key);\n if (!item || item.disabled) return null;\n return {\n key,\n reason: key === descriptor.selectedKey ? \"reselect\" : \"navigate\",\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bottom-navigation.js","sourceRoot":"","sources":["../src/bottom-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type BottomNavigationDensity,\n type BottomNavigationDistribution,\n type BottomNavigationPresentation,\n bottomNavigationRecipeDefaults,\n} from \"./bottom-navigation-defaults.js\";\nimport { formatCounterBadgeCount } from \"./counter-badge.js\";\nimport {\n type SemanticIconName,\n} from \"./icon.js\";\nimport type { DesignSystemDirection } from \"./design-system-provider.js\";\n\nexport type BottomNavigationDirection = DesignSystemDirection;\nexport type BottomNavigationKeyboardBehavior = \"hide\" | \"remain\";\n\nexport type BottomNavigationConfiguration = Readonly<{\n presentation?: BottomNavigationPresentation;\n distribution?: BottomNavigationDistribution;\n density?: BottomNavigationDensity;\n direction?: BottomNavigationDirection;\n keyboardBehavior?: BottomNavigationKeyboardBehavior;\n}>;\n\nexport type ResolvedBottomNavigationConfiguration = Readonly<{\n presentation: BottomNavigationPresentation;\n distribution: BottomNavigationDistribution;\n density: BottomNavigationDensity;\n direction: BottomNavigationDirection;\n keyboardBehavior: BottomNavigationKeyboardBehavior;\n}>;\n\n/**\n * Bottom navigation only accepts numeric counters. A silent status dot is too\n * ambiguous for a persistent, top-level destination.\n */\nexport type BottomNavigationCounterBadge = Readonly<{\n count: number;\n max?: number;\n /** Product-localized copy, for example \"3 unread messages\". */\n accessibilityLabel: string;\n}>;\n\n/**\n * BottomNavigation owns icon appearance through its density and state recipe.\n * Products may extend the semantic name registry, but never override size,\n * tone, stroke weight, accessibility, or logical direction at the call site.\n */\nexport type BottomNavigationIconDescriptor<\n IconName extends string = SemanticIconName,\n> = Readonly<{\n name: IconName;\n decorative?: true;\n accessibilityLabel?: never;\n size?: never;\n tone?: never;\n weight?: never;\n directionality?: never;\n}>;\n\nexport type ResolvedBottomNavigationIconDescriptor<\n IconName extends string = SemanticIconName,\n> = Readonly<{\n name: IconName;\n decorative: true;\n}>;\n\n/**\n * Items are stable top-level destinations, never actions. The icon is always\n * decorative because the visible/item accessibility label already names it.\n */\nexport type BottomNavigationItemDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n id: Key;\n label: string;\n accessibilityLabel?: string;\n icon: BottomNavigationIconDescriptor<IconName>;\n badge?: BottomNavigationCounterBadge;\n disabled?: boolean;\n}>;\n\n/** The router owns selectedKey; the design-system never keeps a second copy. */\nexport type BottomNavigationDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n accessibilityLabel: string;\n items: readonly BottomNavigationItemDescriptor<Key, IconName>[];\n selectedKey: Key;\n}>;\n\n/**\n * The resolved badge intentionally has no accessible label. Renderers hide its\n * subtree and use only the item's resolvedAccessibilityLabel, preventing the\n * counter from being announced twice.\n */\nexport type ResolvedBottomNavigationCounterBadge = Readonly<{\n visibleLabel: string;\n hiddenFromAccessibility: true;\n}>;\n\nexport type ResolvedBottomNavigationItemDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n id: Key;\n label: string;\n icon: ResolvedBottomNavigationIconDescriptor<IconName>;\n disabled: boolean;\n badge: ResolvedBottomNavigationCounterBadge | null;\n resolvedAccessibilityLabel: string;\n}>;\n\nexport type ResolvedBottomNavigationDescriptor<\n Key extends string = string,\n IconName extends string = SemanticIconName,\n> = Readonly<{\n accessibilityLabel: string;\n items: readonly ResolvedBottomNavigationItemDescriptor<Key, IconName>[];\n selectedKey: Key;\n}>;\n\nexport type BottomNavigationActivation<Key extends string = string> = Readonly<{\n key: Key;\n reason: \"navigate\" | \"reselect\";\n}>;\n\n/**\n * `maxItems` follows the platform bars rather than Material's five-destination\n * guidance: iOS tab bars and Android navigation bars both render six labelled\n * destinations without collapsing, and products that ship six need the shared\n * contract to describe them instead of routing around it.\n */\nexport const bottomNavigationBehaviorDefaults = {\n direction: \"ltr\",\n keyboardBehavior: \"hide\",\n minItems: 2,\n maxItems: 6,\n} as const satisfies Readonly<{\n direction: BottomNavigationDirection;\n keyboardBehavior: BottomNavigationKeyboardBehavior;\n minItems: number;\n maxItems: number;\n}>;\n\nfunction validateTrimmedCopy(value: unknown, field: string): asserts value is string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`${field} must not be empty`);\n }\n if (value !== value.trim()) {\n throw new TypeError(`${field} must not start or end with whitespace`);\n }\n}\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nconst configurationKeys = new Set([\n \"presentation\",\n \"distribution\",\n \"density\",\n \"direction\",\n \"keyboardBehavior\",\n]);\nconst descriptorKeys = new Set([\"accessibilityLabel\", \"items\", \"selectedKey\"]);\nconst itemKeys = new Set([\n \"id\",\n \"label\",\n \"accessibilityLabel\",\n \"icon\",\n \"badge\",\n \"disabled\",\n]);\nconst iconKeys = new Set([\"name\", \"decorative\"]);\nconst badgeKeys = new Set([\"count\", \"max\", \"accessibilityLabel\"]);\n\nfunction rejectUnknownKeys(\n value: Readonly<Record<string, unknown>>,\n allowed: ReadonlySet<string>,\n field: string,\n): void {\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n throw new TypeError(`Unsupported BottomNavigation ${field} field: ${key}`);\n }\n }\n}\n\nfunction normalizedCopy(value: string): string {\n return value.normalize(\"NFKC\").toLocaleLowerCase();\n}\n\nfunction validateBadge(badge: BottomNavigationCounterBadge): void {\n if (!isObject(badge)) {\n throw new TypeError(\"BottomNavigation badge must be an object\");\n }\n rejectUnknownKeys(badge, badgeKeys, \"badge\");\n if (!Number.isSafeInteger(badge.count) || badge.count < 0) {\n throw new TypeError(\"BottomNavigation badge count must be a non-negative safe integer\");\n }\n if (\n badge.max !== undefined &&\n (!Number.isSafeInteger(badge.max) || badge.max < 1)\n ) {\n throw new TypeError(\"BottomNavigation badge max must be a positive safe integer\");\n }\n validateTrimmedCopy(\n badge.accessibilityLabel,\n \"BottomNavigation badge accessibilityLabel\",\n );\n}\n\nfunction validateItem<Key extends string, IconName extends string>(\n item: BottomNavigationItemDescriptor<Key, IconName>,\n): void {\n if (!isObject(item)) {\n throw new TypeError(\"BottomNavigation item must be an object\");\n }\n rejectUnknownKeys(item, itemKeys, \"item\");\n validateTrimmedCopy(item.id, \"BottomNavigation item id\");\n validateTrimmedCopy(item.label, \"BottomNavigation item label\");\n if (item.accessibilityLabel !== undefined) {\n validateTrimmedCopy(\n item.accessibilityLabel,\n \"BottomNavigation item accessibilityLabel\",\n );\n if (\n !normalizedCopy(item.accessibilityLabel).includes(\n normalizedCopy(item.label),\n )\n ) {\n throw new TypeError(\n \"BottomNavigation item accessibilityLabel must include the visible label\",\n );\n }\n }\n if (!isObject(item.icon)) {\n throw new TypeError(\"BottomNavigation item icon must be an object\");\n }\n if (item.icon.decorative !== undefined && item.icon.decorative !== true) {\n throw new TypeError(\"BottomNavigation item icon must be decorative\");\n }\n rejectUnknownKeys(item.icon, iconKeys, \"item icon\");\n validateTrimmedCopy(item.icon.name, \"BottomNavigation item icon name\");\n if (item.disabled !== undefined && typeof item.disabled !== \"boolean\") {\n throw new TypeError(\"BottomNavigation item disabled must be a boolean\");\n }\n if (item.badge !== undefined) validateBadge(item.badge);\n}\n\nconst presentations = new Set<BottomNavigationPresentation>([\"bar\", \"floating\", \"capsule\"]);\nconst distributions = new Set<BottomNavigationDistribution>([\"equal\", \"center-gap\"]);\nconst densities = new Set<BottomNavigationDensity>([\"compact\", \"regular\"]);\nconst directions = new Set<BottomNavigationDirection>([\"ltr\", \"rtl\"]);\nconst keyboardBehaviors = new Set<BottomNavigationKeyboardBehavior>([\n \"hide\",\n \"remain\",\n]);\n\nexport function validateBottomNavigationConfiguration(\n configuration: BottomNavigationConfiguration,\n itemCount: number,\n): void {\n if (!isObject(configuration)) {\n throw new TypeError(\"BottomNavigation configuration must be an object\");\n }\n rejectUnknownKeys(configuration, configurationKeys, \"configuration\");\n if (\n !Number.isSafeInteger(itemCount) ||\n itemCount < bottomNavigationBehaviorDefaults.minItems ||\n itemCount > bottomNavigationBehaviorDefaults.maxItems\n ) {\n throw new RangeError(\n `BottomNavigation configuration requires ${bottomNavigationBehaviorDefaults.minItems} to ${bottomNavigationBehaviorDefaults.maxItems} destinations`,\n );\n }\n if (\n configuration.presentation !== undefined &&\n !presentations.has(configuration.presentation)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation presentation: ${String(configuration.presentation)}`,\n );\n }\n if (\n configuration.distribution !== undefined &&\n !distributions.has(configuration.distribution)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation distribution: ${String(configuration.distribution)}`,\n );\n }\n if (configuration.density !== undefined && !densities.has(configuration.density)) {\n throw new TypeError(\n `Unsupported BottomNavigation density: ${String(configuration.density)}`,\n );\n }\n if (\n configuration.direction !== undefined &&\n !directions.has(configuration.direction)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation direction: ${String(configuration.direction)}`,\n );\n }\n if (\n configuration.keyboardBehavior !== undefined &&\n !keyboardBehaviors.has(configuration.keyboardBehavior)\n ) {\n throw new TypeError(\n `Unsupported BottomNavigation keyboardBehavior: ${String(configuration.keyboardBehavior)}`,\n );\n }\n // Capsule actions sit beside the destination surface, so reserving a central gap would duplicate their space.\n if (configuration.presentation === \"capsule\" && configuration.distribution === \"center-gap\") {\n throw new TypeError(\"BottomNavigation capsule uses an adjacent action, not center-gap\");\n }\n if (configuration.distribution === \"center-gap\" && itemCount % 2 !== 0) {\n throw new RangeError(\n \"BottomNavigation center-gap distribution requires an even destination count\",\n );\n }\n}\n\nexport function resolveBottomNavigationConfiguration(\n configuration: BottomNavigationConfiguration,\n itemCount: number,\n): ResolvedBottomNavigationConfiguration {\n validateBottomNavigationConfiguration(configuration, itemCount);\n return {\n presentation:\n configuration.presentation ?? bottomNavigationRecipeDefaults.presentation,\n distribution:\n configuration.distribution ?? bottomNavigationRecipeDefaults.distribution,\n density: configuration.density ?? bottomNavigationRecipeDefaults.density,\n direction: configuration.direction ?? bottomNavigationBehaviorDefaults.direction,\n keyboardBehavior:\n configuration.keyboardBehavior ??\n bottomNavigationBehaviorDefaults.keyboardBehavior,\n };\n}\n\nexport function validateBottomNavigationDescriptor<\n Key extends string,\n IconName extends string,\n>(descriptor: BottomNavigationDescriptor<Key, IconName>): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"BottomNavigation descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, descriptorKeys, \"descriptor\");\n validateTrimmedCopy(\n descriptor.accessibilityLabel,\n \"BottomNavigation accessibilityLabel\",\n );\n if (!Array.isArray(descriptor.items)) {\n throw new TypeError(\"BottomNavigation items must be an array\");\n }\n validateTrimmedCopy(\n descriptor.selectedKey,\n \"BottomNavigation selectedKey\",\n );\n if (\n descriptor.items.length < bottomNavigationBehaviorDefaults.minItems ||\n descriptor.items.length > bottomNavigationBehaviorDefaults.maxItems\n ) {\n throw new RangeError(\n `BottomNavigation must contain ${bottomNavigationBehaviorDefaults.minItems} to ${bottomNavigationBehaviorDefaults.maxItems} destinations`,\n );\n }\n\n const ids = new Set<Key>();\n for (const item of descriptor.items) {\n validateItem(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate BottomNavigation item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n\n const selected = descriptor.items.find(\n (item) => item.id === descriptor.selectedKey,\n );\n if (!selected) {\n throw new RangeError(\n `BottomNavigation selectedKey must exist: ${descriptor.selectedKey}`,\n );\n }\n if (selected.disabled) {\n throw new RangeError(\"BottomNavigation selected destination must be enabled\");\n }\n}\n\nexport function resolveBottomNavigationItem<\n Key extends string,\n IconName extends string,\n>(\n item: BottomNavigationItemDescriptor<Key, IconName>,\n): ResolvedBottomNavigationItemDescriptor<Key, IconName> {\n validateItem(item);\n const baseAccessibilityLabel = item.accessibilityLabel ?? item.label;\n const visibleBadgeLabel = item.badge\n ? formatCounterBadgeCount(item.badge.count, item.badge.max)\n : null;\n const badge = visibleBadgeLabel\n ? ({\n visibleLabel: visibleBadgeLabel,\n hiddenFromAccessibility: true,\n } as const)\n : null;\n\n return {\n id: item.id,\n label: item.label,\n icon: { name: item.icon.name, decorative: true },\n disabled: item.disabled ?? false,\n badge,\n resolvedAccessibilityLabel:\n badge && item.badge\n ? `${baseAccessibilityLabel}, ${item.badge.accessibilityLabel}`\n : baseAccessibilityLabel,\n };\n}\n\nexport function resolveBottomNavigationDescriptor<\n Key extends string,\n IconName extends string,\n>(\n descriptor: BottomNavigationDescriptor<Key, IconName>,\n): ResolvedBottomNavigationDescriptor<Key, IconName> {\n validateBottomNavigationDescriptor(descriptor);\n return {\n accessibilityLabel: descriptor.accessibilityLabel,\n items: descriptor.items.map(resolveBottomNavigationItem),\n selectedKey: descriptor.selectedKey,\n };\n}\n\n/**\n * Produces navigation intent without mutating selectedKey. A renderer forwards\n * the request to its router, which updates selectedKey only after navigation.\n */\nexport function resolveBottomNavigationActivation<\n Key extends string,\n IconName extends string,\n>(\n descriptor: BottomNavigationDescriptor<Key, IconName>,\n key: Key,\n): BottomNavigationActivation<Key> | null {\n validateBottomNavigationDescriptor(descriptor);\n const item = descriptor.items.find((candidate) => candidate.id === key);\n if (!item || item.disabled) return null;\n return {\n key,\n reason: key === descriptor.selectedKey ? \"reselect\" : \"navigate\",\n };\n}\n"]}
|