@hjmds/design-contracts 1.31.0 → 1.32.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/behaviors.d.ts +2 -0
- package/dist/behaviors.d.ts.map +1 -1
- package/dist/bottom-navigation.d.ts +19 -0
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +21 -0
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/catalog.d.ts +37 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +2 -1
- package/dist/catalog.js.map +1 -1
- package/dist/component-recipes.d.ts +39 -0
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +27 -1
- package/dist/component-recipes.js.map +1 -1
- package/dist/image-choice.d.ts +40 -0
- package/dist/image-choice.d.ts.map +1 -1
- package/dist/image-choice.js +49 -0
- package/dist/image-choice.js.map +1 -1
- package/dist/pagination.d.ts +53 -0
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +48 -0
- package/dist/pagination.js.map +1 -1
- package/dist/screen-patterns.d.ts +52 -2
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +34 -1
- package/dist/screen-patterns.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/authoring-tools.md +1 -0
- package/docs/bottom-navigation.md +18 -0
- package/docs/data-workflows.md +1 -0
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +7 -5
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +4 -2
- package/docs/pagination.md +17 -0
- package/docs/screen-patterns.md +37 -1
- package/docs/toast.md +10 -0
- package/docs/usage/README.md +6 -6
- package/docs/usage/components/bottom-cta.md +11 -0
- package/docs/usage/components/bottom-navigation.md +36 -2
- package/docs/usage/components/chat-date-separator.md +3 -3
- package/docs/usage/components/chat-message.md +5 -2
- package/docs/usage/components/chat-screen.md +1 -1
- package/docs/usage/components/comment-thread-row.md +1 -1
- package/docs/usage/components/conversation-row.md +3 -3
- package/docs/usage/components/icon-tile.md +9 -2
- package/docs/usage/components/image-choice-group.md +33 -4
- package/docs/usage/components/media-selection-screen.md +5 -3
- package/docs/usage/components/moderation-screen.md +20 -1
- package/docs/usage/components/page-indicator.md +3 -3
- package/docs/usage/components/pagination.md +28 -4
- package/docs/usage/components/screen-layout.md +6 -3
- package/docs/usage/components/search-screen.md +5 -3
- package/docs/usage/components/toast.md +5 -0
- package/docs/usage/components/typing-indicator.md +3 -3
- package/docs/usage/screens/common-conversation-list.md +2 -2
- package/package.json +1 -1
package/dist/image-choice.d.ts
CHANGED
|
@@ -6,8 +6,15 @@ export type ImageChoiceSelectionMode = "single" | "multiple";
|
|
|
6
6
|
export type ImageChoiceItemDescriptor = Readonly<{
|
|
7
7
|
id: string;
|
|
8
8
|
label: string;
|
|
9
|
+
/**
|
|
10
|
+
* Localized secondary line under the tile (a status such as "sent", a one-line summary). Always visible when given
|
|
11
|
+
* and read as the tile's description, so it must not repeat `label`.
|
|
12
|
+
*/
|
|
13
|
+
description?: string;
|
|
9
14
|
disabled?: boolean;
|
|
10
15
|
}>;
|
|
16
|
+
/** Lines the visible item label may wrap to before it is ellipsized (`showItemLabels`). */
|
|
17
|
+
export type ImageChoiceItemLabelLines = 1 | 2;
|
|
11
18
|
export declare const imageChoiceRecipe: {
|
|
12
19
|
readonly tileSize: 72;
|
|
13
20
|
readonly gap: 8;
|
|
@@ -22,7 +29,40 @@ export declare const imageChoiceRecipe: {
|
|
|
22
29
|
readonly itemLabelGap: 4;
|
|
23
30
|
readonly itemLabelVariant: "caption";
|
|
24
31
|
readonly itemLabelLines: 1;
|
|
32
|
+
/**
|
|
33
|
+
* 2026-10-18 BurnTok creation starters: multi-word titles were cut at one 80-wide line. Two lines keep a short
|
|
34
|
+
* title whole (word-keeping wrap) while the grid stays even; longer copy belongs in a list row, not a tile.
|
|
35
|
+
*/
|
|
36
|
+
readonly itemLabelMaxLines: 2;
|
|
37
|
+
/**
|
|
38
|
+
* One muted caption line under the label for a per-item status (2026-10-18 BurnTok share: "sent" was appended to
|
|
39
|
+
* the name and hidden by the ellipsis on long nicknames).
|
|
40
|
+
*/
|
|
41
|
+
readonly itemDescriptionVariant: "caption";
|
|
42
|
+
readonly itemDescriptionTone: "muted";
|
|
43
|
+
readonly itemDescriptionLines: 1;
|
|
44
|
+
/**
|
|
45
|
+
* Web `virtualized` viewport inset: the tile focus outline (2) plus its offset (2) would otherwise be clipped by the
|
|
46
|
+
* scrolling viewport at the first row and the edges.
|
|
47
|
+
*/
|
|
48
|
+
readonly virtualViewportInset: 4;
|
|
25
49
|
};
|
|
50
|
+
export declare function validateImageChoiceItemLabelLines(lines: number): asserts lines is ImageChoiceItemLabelLines;
|
|
51
|
+
/** Square ring frame around the picture, which is also the tile width. */
|
|
52
|
+
export declare function resolveImageChoiceOuterSize(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Caption lines every tile of a group reserves: label lines when labels show, plus the description line when any
|
|
55
|
+
* item has one. Reserving the same block in every tile keeps wrapped rows even and gives virtualized rows one height.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveImageChoiceCaptionLines(options: Readonly<{
|
|
58
|
+
showItemLabels: boolean;
|
|
59
|
+
itemLabelLines: ImageChoiceItemLabelLines;
|
|
60
|
+
hasDescription: boolean;
|
|
61
|
+
}>): number;
|
|
62
|
+
/** Tile height for a caption block of `captionLines` lines at the rendered caption line height (token × text scale). */
|
|
63
|
+
export declare function resolveImageChoiceTileHeight(captionLines: number, captionLineHeight: number): number;
|
|
64
|
+
/** Tiles per row in a slot `width` wide: as many fixed-size tiles as fit with the recipe gap, at least one. */
|
|
65
|
+
export declare function resolveImageChoiceColumns(width: number): number;
|
|
26
66
|
export declare function validateImageChoiceItems(items: readonly ImageChoiceItemDescriptor[]): void;
|
|
27
67
|
/** Single mode keeps exactly the pressed id (radio semantics); multiple toggles membership in order. */
|
|
28
68
|
export declare function resolveImageChoiceToggle(mode: "single", value: string | null, id: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-choice.d.ts","sourceRoot":"","sources":["../src/image-choice.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"image-choice.d.ts","sourceRoot":"","sources":["../src/image-choice.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AACH,2FAA2F;AAC3F,MAAM,MAAM,yBAAyB,GAAG,CAAC,GAAG,CAAC,CAAC;AAE9C,eAAO,MAAM,iBAAiB;;;;;;;IAO5B;;;OAGG;;;;IAIH;;;OAGG;;IAEH;;;OAGG;;;;IAIH;;;OAGG;;CAEK,CAAC;AAEX,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,yBAAyB,CAE3G;AAED,0EAA0E;AAC1E,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,QAAQ,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,yBAAyB,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,MAAM,CAEzK;AAED,wHAAwH;AACxH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAIpG;AAED,+GAA+G;AAC/G,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,yBAAyB,EAAE,GAAG,IAAI,CAS1F;AAED,wGAAwG;AACxG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;AACnG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC"}
|
package/dist/image-choice.js
CHANGED
|
@@ -13,7 +13,53 @@ export const imageChoiceRecipe = {
|
|
|
13
13
|
itemLabelGap: spacing.xxs,
|
|
14
14
|
itemLabelVariant: "caption",
|
|
15
15
|
itemLabelLines: 1,
|
|
16
|
+
/**
|
|
17
|
+
* 2026-10-18 BurnTok creation starters: multi-word titles were cut at one 80-wide line. Two lines keep a short
|
|
18
|
+
* title whole (word-keeping wrap) while the grid stays even; longer copy belongs in a list row, not a tile.
|
|
19
|
+
*/
|
|
20
|
+
itemLabelMaxLines: 2,
|
|
21
|
+
/**
|
|
22
|
+
* One muted caption line under the label for a per-item status (2026-10-18 BurnTok share: "sent" was appended to
|
|
23
|
+
* the name and hidden by the ellipsis on long nicknames).
|
|
24
|
+
*/
|
|
25
|
+
itemDescriptionVariant: "caption",
|
|
26
|
+
itemDescriptionTone: "muted",
|
|
27
|
+
itemDescriptionLines: 1,
|
|
28
|
+
/**
|
|
29
|
+
* Web `virtualized` viewport inset: the tile focus outline (2) plus its offset (2) would otherwise be clipped by the
|
|
30
|
+
* scrolling viewport at the first row and the edges.
|
|
31
|
+
*/
|
|
32
|
+
virtualViewportInset: 4,
|
|
16
33
|
};
|
|
34
|
+
export function validateImageChoiceItemLabelLines(lines) {
|
|
35
|
+
if (!Number.isInteger(lines) || lines < 1 || lines > imageChoiceRecipe.itemLabelMaxLines)
|
|
36
|
+
throw new RangeError("ImageChoiceGroup itemLabelLines must be 1 or 2");
|
|
37
|
+
}
|
|
38
|
+
/** Square ring frame around the picture, which is also the tile width. */
|
|
39
|
+
export function resolveImageChoiceOuterSize() {
|
|
40
|
+
return imageChoiceRecipe.tileSize + (imageChoiceRecipe.ringWidth + imageChoiceRecipe.ringOffset) * 2;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Caption lines every tile of a group reserves: label lines when labels show, plus the description line when any
|
|
44
|
+
* item has one. Reserving the same block in every tile keeps wrapped rows even and gives virtualized rows one height.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveImageChoiceCaptionLines(options) {
|
|
47
|
+
return (options.showItemLabels ? options.itemLabelLines : 0) + (options.hasDescription ? imageChoiceRecipe.itemDescriptionLines : 0);
|
|
48
|
+
}
|
|
49
|
+
/** Tile height for a caption block of `captionLines` lines at the rendered caption line height (token × text scale). */
|
|
50
|
+
export function resolveImageChoiceTileHeight(captionLines, captionLineHeight) {
|
|
51
|
+
if (!Number.isInteger(captionLines) || captionLines < 0 || !Number.isFinite(captionLineHeight) || captionLineHeight <= 0)
|
|
52
|
+
throw new TypeError("Invalid ImageChoiceGroup caption metrics");
|
|
53
|
+
const outer = resolveImageChoiceOuterSize();
|
|
54
|
+
return captionLines === 0 ? outer : outer + imageChoiceRecipe.itemLabelGap + captionLines * captionLineHeight;
|
|
55
|
+
}
|
|
56
|
+
/** Tiles per row in a slot `width` wide: as many fixed-size tiles as fit with the recipe gap, at least one. */
|
|
57
|
+
export function resolveImageChoiceColumns(width) {
|
|
58
|
+
if (!Number.isFinite(width) || width < 0)
|
|
59
|
+
throw new TypeError("Invalid ImageChoiceGroup width");
|
|
60
|
+
const outer = resolveImageChoiceOuterSize();
|
|
61
|
+
return Math.max(1, Math.floor((width + imageChoiceRecipe.gap) / (outer + imageChoiceRecipe.gap)));
|
|
62
|
+
}
|
|
17
63
|
export function validateImageChoiceItems(items) {
|
|
18
64
|
if (!items.length)
|
|
19
65
|
throw new TypeError("ImageChoiceGroup needs at least one item");
|
|
@@ -21,6 +67,9 @@ export function validateImageChoiceItems(items) {
|
|
|
21
67
|
for (const item of items) {
|
|
22
68
|
if (!item.id.trim() || ids.has(item.id) || !item.label.trim())
|
|
23
69
|
throw new TypeError("ImageChoiceGroup items need unique ids and localized labels");
|
|
70
|
+
// Omit the key for no description; a blank string would reserve and announce an empty line.
|
|
71
|
+
if (item.description !== undefined && !item.description.trim())
|
|
72
|
+
throw new TypeError("ImageChoiceGroup item description must not be blank");
|
|
24
73
|
ids.add(item.id);
|
|
25
74
|
}
|
|
26
75
|
}
|
package/dist/image-choice.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-choice.js","sourceRoot":"","sources":["../src/image-choice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"image-choice.js","sourceRoot":"","sources":["../src/image-choice.ts"],"names":[],"mappings":"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","sourcesContent":["import { radius, spacing } from \"./foundations.js\";\n\n/**\n * Picking by picture (avatar faces, photos). 2026-10-16 composition review: products imitated this with\n * rows of selected ghost Buttons because HJM had no image-choice control.\n */\nexport type ImageChoiceSelectionMode = \"single\" | \"multiple\";\nexport type ImageChoiceItemDescriptor = Readonly<{\n id: string;\n label: string;\n /**\n * Localized secondary line under the tile (a status such as \"sent\", a one-line summary). Always visible when given\n * and read as the tile's description, so it must not repeat `label`.\n */\n description?: string;\n disabled?: boolean;\n}>;\n/** Lines the visible item label may wrap to before it is ellipsized (`showItemLabels`). */\nexport type ImageChoiceItemLabelLines = 1 | 2;\n\nexport const imageChoiceRecipe = {\n tileSize: 72,\n gap: spacing.xs,\n radius: radius.md,\n ringWidth: 2,\n ringOffset: 2,\n checkSize: 20,\n /**\n * Visible per-tile caption (`showItemLabels`, 2026-10-18 BurnTok share recipients): people are recognised by name,\n * not only by face. One line, ellipsized, as wide as the tile so the wrap grid stays even.\n */\n itemLabelGap: spacing.xxs,\n itemLabelVariant: \"caption\",\n itemLabelLines: 1,\n /**\n * 2026-10-18 BurnTok creation starters: multi-word titles were cut at one 80-wide line. Two lines keep a short\n * title whole (word-keeping wrap) while the grid stays even; longer copy belongs in a list row, not a tile.\n */\n itemLabelMaxLines: 2,\n /**\n * One muted caption line under the label for a per-item status (2026-10-18 BurnTok share: \"sent\" was appended to\n * the name and hidden by the ellipsis on long nicknames).\n */\n itemDescriptionVariant: \"caption\",\n itemDescriptionTone: \"muted\",\n itemDescriptionLines: 1,\n /**\n * Web `virtualized` viewport inset: the tile focus outline (2) plus its offset (2) would otherwise be clipped by the\n * scrolling viewport at the first row and the edges.\n */\n virtualViewportInset: 4,\n} as const;\n\nexport function validateImageChoiceItemLabelLines(lines: number): asserts lines is ImageChoiceItemLabelLines {\n if (!Number.isInteger(lines) || lines < 1 || lines > imageChoiceRecipe.itemLabelMaxLines) throw new RangeError(\"ImageChoiceGroup itemLabelLines must be 1 or 2\");\n}\n\n/** Square ring frame around the picture, which is also the tile width. */\nexport function resolveImageChoiceOuterSize(): number {\n return imageChoiceRecipe.tileSize + (imageChoiceRecipe.ringWidth + imageChoiceRecipe.ringOffset) * 2;\n}\n\n/**\n * Caption lines every tile of a group reserves: label lines when labels show, plus the description line when any\n * item has one. Reserving the same block in every tile keeps wrapped rows even and gives virtualized rows one height.\n */\nexport function resolveImageChoiceCaptionLines(options: Readonly<{ showItemLabels: boolean; itemLabelLines: ImageChoiceItemLabelLines; hasDescription: boolean }>): number {\n return (options.showItemLabels ? options.itemLabelLines : 0) + (options.hasDescription ? imageChoiceRecipe.itemDescriptionLines : 0);\n}\n\n/** Tile height for a caption block of `captionLines` lines at the rendered caption line height (token × text scale). */\nexport function resolveImageChoiceTileHeight(captionLines: number, captionLineHeight: number): number {\n if (!Number.isInteger(captionLines) || captionLines < 0 || !Number.isFinite(captionLineHeight) || captionLineHeight <= 0) throw new TypeError(\"Invalid ImageChoiceGroup caption metrics\");\n const outer = resolveImageChoiceOuterSize();\n return captionLines === 0 ? outer : outer + imageChoiceRecipe.itemLabelGap + captionLines * captionLineHeight;\n}\n\n/** Tiles per row in a slot `width` wide: as many fixed-size tiles as fit with the recipe gap, at least one. */\nexport function resolveImageChoiceColumns(width: number): number {\n if (!Number.isFinite(width) || width < 0) throw new TypeError(\"Invalid ImageChoiceGroup width\");\n const outer = resolveImageChoiceOuterSize();\n return Math.max(1, Math.floor((width + imageChoiceRecipe.gap) / (outer + imageChoiceRecipe.gap)));\n}\n\nexport function validateImageChoiceItems(items: readonly ImageChoiceItemDescriptor[]): void {\n if (!items.length) throw new TypeError(\"ImageChoiceGroup needs at least one item\");\n const ids = new Set<string>();\n for (const item of items) {\n if (!item.id.trim() || ids.has(item.id) || !item.label.trim()) throw new TypeError(\"ImageChoiceGroup items need unique ids and localized labels\");\n // Omit the key for no description; a blank string would reserve and announce an empty line.\n if (item.description !== undefined && !item.description.trim()) throw new TypeError(\"ImageChoiceGroup item description must not be blank\");\n ids.add(item.id);\n }\n}\n\n/** Single mode keeps exactly the pressed id (radio semantics); multiple toggles membership in order. */\nexport function resolveImageChoiceToggle(mode: \"single\", value: string | null, id: string): string;\nexport function resolveImageChoiceToggle(mode: \"multiple\", value: readonly string[], id: string): string[];\nexport function resolveImageChoiceToggle(mode: ImageChoiceSelectionMode, value: string | null | readonly string[], id: string): string | string[] {\n if (mode === \"single\") return id;\n const list = (value ?? []) as readonly string[];\n return list.includes(id) ? list.filter(entry => entry !== id) : [...list, id];\n}\n"]}
|
package/dist/pagination.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type PaginationByCount = Readonly<{
|
|
|
8
8
|
totalCount: number;
|
|
9
9
|
pageSize: number;
|
|
10
10
|
totalPages?: never;
|
|
11
|
+
hasNext?: never;
|
|
11
12
|
siblingCount?: number;
|
|
12
13
|
boundaryCount?: number;
|
|
13
14
|
}>;
|
|
@@ -16,9 +17,40 @@ export type PaginationByPageCount = Readonly<{
|
|
|
16
17
|
totalPages: number;
|
|
17
18
|
totalCount?: never;
|
|
18
19
|
pageSize?: never;
|
|
20
|
+
hasNext?: never;
|
|
19
21
|
siblingCount?: number;
|
|
20
22
|
boundaryCount?: number;
|
|
21
23
|
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Page-addressable results whose total is unknown: the source only reports whether a next page exists (Kakao
|
|
26
|
+
* Local's `meta.is_end`, cursor APIs). The control shows previous, a product-composed current page label and
|
|
27
|
+
* next — no numbered window, because pages beyond the next one cannot be addressed. Added 2026-10-18 for
|
|
28
|
+
* choose-window search, which assembled its own previous/next Button row. Infinite feeds remain LoadMore.
|
|
29
|
+
*/
|
|
30
|
+
export type PaginationOpenEndedDescriptor = Readonly<{
|
|
31
|
+
currentPage: number;
|
|
32
|
+
hasNext: boolean;
|
|
33
|
+
totalCount?: never;
|
|
34
|
+
pageSize?: never;
|
|
35
|
+
totalPages?: never;
|
|
36
|
+
siblingCount?: never;
|
|
37
|
+
boundaryCount?: never;
|
|
38
|
+
}>;
|
|
39
|
+
/** The current page label is the visible text and the accessible name, e.g. "3페이지" / "Page 3". */
|
|
40
|
+
export type ComposePaginationCurrentPageLabel = (info: Readonly<{
|
|
41
|
+
page: number;
|
|
42
|
+
}>) => string;
|
|
43
|
+
export type ResolvedPaginationOpenEndedDescriptor = Readonly<{
|
|
44
|
+
currentPage: number;
|
|
45
|
+
hasPrevious: boolean;
|
|
46
|
+
hasNext: boolean;
|
|
47
|
+
labels: PaginationLabels;
|
|
48
|
+
currentPageLabel: string;
|
|
49
|
+
}>;
|
|
50
|
+
export type ResolvePaginationOpenEndedOptions = Readonly<{
|
|
51
|
+
labels: PaginationLabels;
|
|
52
|
+
composeCurrentPageLabel: ComposePaginationCurrentPageLabel;
|
|
53
|
+
}>;
|
|
22
54
|
/**
|
|
23
55
|
* Required contract is exactly current page + (total count or total page
|
|
24
56
|
* count) + page size — nothing else. A page-size changer and a "jump to page"
|
|
@@ -85,6 +117,14 @@ export type PaginationChangeReason = "previous" | "next" | "page";
|
|
|
85
117
|
export type PaginationChangeHandler = (page: number, reason: PaginationChangeReason) => void;
|
|
86
118
|
export declare function validatePaginationDescriptor(descriptor: PaginationDescriptor): void;
|
|
87
119
|
export declare function validatePaginationLabels(labels: PaginationLabels): void;
|
|
120
|
+
/** Distinguishes the open-ended form by its `hasNext` field, the one key the bounded forms forbid. */
|
|
121
|
+
export declare function isPaginationOpenEndedDescriptor(descriptor: PaginationDescriptor | PaginationOpenEndedDescriptor): descriptor is PaginationOpenEndedDescriptor;
|
|
122
|
+
export declare function validatePaginationOpenEndedDescriptor(descriptor: PaginationOpenEndedDescriptor): void;
|
|
123
|
+
/**
|
|
124
|
+
* Open-ended counterpart of resolvePaginationDescriptor. A first page without a next page still resolves (both
|
|
125
|
+
* steps unavailable) so the renderer, not the product, decides how a single page is presented.
|
|
126
|
+
*/
|
|
127
|
+
export declare function resolvePaginationOpenEndedDescriptor(descriptor: PaginationOpenEndedDescriptor, options: ResolvePaginationOpenEndedOptions): ResolvedPaginationOpenEndedDescriptor;
|
|
88
128
|
/**
|
|
89
129
|
* Pure page-window function, independent of accessibility naming, so its
|
|
90
130
|
* boundary inputs (total 1 page, total 2 pages, current at the first/last
|
|
@@ -154,6 +194,19 @@ export declare const paginationRecipe: {
|
|
|
154
194
|
readonly previous: "chevronStart";
|
|
155
195
|
readonly next: "chevronEnd";
|
|
156
196
|
};
|
|
197
|
+
/**
|
|
198
|
+
* Open-ended current page label: plain text between the steps, not a button, because it is the only page the
|
|
199
|
+
* control can show. It keeps the item's touch-target height so the row does not jump between modes.
|
|
200
|
+
*/
|
|
201
|
+
readonly pageLabel: {
|
|
202
|
+
readonly minHeight: 44;
|
|
203
|
+
readonly paddingHorizontal: 8;
|
|
204
|
+
readonly color: Readonly<{
|
|
205
|
+
source: "theme";
|
|
206
|
+
key: "textBody";
|
|
207
|
+
alpha?: number;
|
|
208
|
+
}>;
|
|
209
|
+
};
|
|
157
210
|
readonly focus: {
|
|
158
211
|
readonly color: Readonly<{
|
|
159
212
|
source: "theme";
|
package/dist/pagination.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
|
package/dist/pagination.js
CHANGED
|
@@ -86,6 +86,45 @@ export function validatePaginationLabels(labels) {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
const openEndedDescriptorKeys = new Set(["currentPage", "hasNext"]);
|
|
90
|
+
/** Distinguishes the open-ended form by its `hasNext` field, the one key the bounded forms forbid. */
|
|
91
|
+
export function isPaginationOpenEndedDescriptor(descriptor) {
|
|
92
|
+
return isObject(descriptor) && Object.prototype.hasOwnProperty.call(descriptor, "hasNext");
|
|
93
|
+
}
|
|
94
|
+
export function validatePaginationOpenEndedDescriptor(descriptor) {
|
|
95
|
+
if (!isObject(descriptor)) {
|
|
96
|
+
throw new TypeError("Pagination descriptor must be an object");
|
|
97
|
+
}
|
|
98
|
+
rejectUnknownKeys(descriptor, openEndedDescriptorKeys, "open-ended descriptor");
|
|
99
|
+
if (!isPositiveInteger(descriptor.currentPage)) {
|
|
100
|
+
throw new RangeError("Pagination currentPage must be a positive integer");
|
|
101
|
+
}
|
|
102
|
+
if (typeof descriptor.hasNext !== "boolean") {
|
|
103
|
+
throw new TypeError("Pagination hasNext must be a boolean");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Open-ended counterpart of resolvePaginationDescriptor. A first page without a next page still resolves (both
|
|
108
|
+
* steps unavailable) so the renderer, not the product, decides how a single page is presented.
|
|
109
|
+
*/
|
|
110
|
+
export function resolvePaginationOpenEndedDescriptor(descriptor, options) {
|
|
111
|
+
validatePaginationOpenEndedDescriptor(descriptor);
|
|
112
|
+
validatePaginationLabels(options.labels);
|
|
113
|
+
if (typeof options.composeCurrentPageLabel !== "function") {
|
|
114
|
+
throw new TypeError("Pagination composeCurrentPageLabel must be a function");
|
|
115
|
+
}
|
|
116
|
+
const currentPageLabel = options.composeCurrentPageLabel({ page: descriptor.currentPage });
|
|
117
|
+
if (typeof currentPageLabel !== "string" || currentPageLabel.trim().length === 0) {
|
|
118
|
+
throw new TypeError("Pagination composeCurrentPageLabel must return a non-empty string");
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
currentPage: descriptor.currentPage,
|
|
122
|
+
hasPrevious: descriptor.currentPage > 1,
|
|
123
|
+
hasNext: descriptor.hasNext,
|
|
124
|
+
labels: options.labels,
|
|
125
|
+
currentPageLabel,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
89
128
|
/**
|
|
90
129
|
* Pure page-window function, independent of accessibility naming, so its
|
|
91
130
|
* boundary inputs (total 1 page, total 2 pages, current at the first/last
|
|
@@ -208,6 +247,15 @@ export const paginationRecipe = {
|
|
|
208
247
|
icon: "more",
|
|
209
248
|
},
|
|
210
249
|
navIcon: { previous: "chevronStart", next: "chevronEnd" },
|
|
250
|
+
/**
|
|
251
|
+
* Open-ended current page label: plain text between the steps, not a button, because it is the only page the
|
|
252
|
+
* control can show. It keeps the item's touch-target height so the row does not jump between modes.
|
|
253
|
+
*/
|
|
254
|
+
pageLabel: {
|
|
255
|
+
minHeight: control.minTouchTarget,
|
|
256
|
+
paddingHorizontal: spacing.xs,
|
|
257
|
+
color: semanticColors.content.body,
|
|
258
|
+
},
|
|
211
259
|
focus: focusIndicatorContract,
|
|
212
260
|
states: { disabledOpacity: opacity.disabled },
|
|
213
261
|
};
|
package/dist/pagination.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, opacity, spacing } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Bounded, page-addressable results share this contract on Web and Native.\n * 2026-10-15 Diairy audit found product-owned Native pagination duplicated this\n * window/selection model. Open-ended feeds remain the separate LoadMore contract.\n */\nexport type PaginationByCount = Readonly<{\n currentPage: number;\n totalCount: number;\n pageSize: number;\n totalPages?: never;\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\nexport type PaginationByPageCount = Readonly<{\n currentPage: number;\n totalPages: number;\n totalCount?: never;\n pageSize?: never;\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\n/**\n * Required contract is exactly current page + (total count or total page\n * count) + page size — nothing else. A page-size changer and a \"jump to page\"\n * input are intentionally absent: neither has measured product demand, and\n * both add a second way to reach the same currentPage/totalPages state this\n * module already owns. If a product needs them, they compose as separate\n * controls that call the same onPageChange — they are not missing API surface\n * here, they are a deliberate exclusion (see docs/pagination.md).\n */\nexport type PaginationDescriptor = PaginationByCount | PaginationByPageCount;\n\nexport type PaginationLabels = Readonly<{\n previous: string;\n next: string;\n}>;\n\nexport const paginationDescriptorDefaults = {\n siblingCount: 1,\n boundaryCount: 1,\n} as const satisfies Readonly<{ siblingCount: number; boundaryCount: number }>;\n\nexport type PaginationItem =\n | Readonly<{ type: \"page\"; page: number }>\n | Readonly<{ type: \"ellipsis\" }>;\n\nexport type PaginationWindowOptions = Readonly<{\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\nexport type PaginationAccessibleNameInfo = Readonly<{\n page: number;\n totalPages: number;\n current: boolean;\n}>;\n\n/**\n * Products own the exact phrase order, same reason as Steps'\n * composeAccessibleName: Korean \"5 페이지 중 3 페이지\" and an English \"page 3\n * of 5\" have different word order, and every page button gets its own\n * composed name (not just a landmark summary) because ellipsis gaps mean a\n * bare visible number (\"7\") does not by itself convey how many pages exist.\n */\nexport type ComposePaginationAccessibleName = (\n info: PaginationAccessibleNameInfo,\n) => string;\n\nexport type ResolvedPaginationItem =\n | Readonly<{\n type: \"page\";\n page: number;\n current: boolean;\n accessibleName: string;\n }>\n | Readonly<{ type: \"ellipsis\" }>;\n\nexport type ResolvedPaginationDescriptor = Readonly<{\n currentPage: number;\n totalPages: number;\n hasPrevious: boolean;\n hasNext: boolean;\n labels: PaginationLabels;\n items: readonly ResolvedPaginationItem[];\n}>;\n\nexport type ResolvePaginationOptions = Readonly<{\n labels: PaginationLabels;\n composeAccessibleName: ComposePaginationAccessibleName;\n}>;\n\n/** Why a page change was requested — analytics and controllers key off this, not the target page alone. */\nexport type PaginationChangeReason = \"previous\" | \"next\" | \"page\";\nexport type PaginationChangeHandler = (\n page: number,\n reason: PaginationChangeReason,\n) => void;\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 1;\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n}\n\nconst descriptorKeys = new Set([\n \"currentPage\",\n \"totalCount\",\n \"pageSize\",\n \"totalPages\",\n \"siblingCount\",\n \"boundaryCount\",\n]);\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 Pagination ${field} field: ${key}`);\n }\n }\n}\n\nfunction resolveTotalPages(runtime: Readonly<Record<string, unknown>>): number {\n const hasTotalPages = Object.prototype.hasOwnProperty.call(runtime, \"totalPages\");\n const hasTotalCount = Object.prototype.hasOwnProperty.call(runtime, \"totalCount\");\n const hasPageSize = Object.prototype.hasOwnProperty.call(runtime, \"pageSize\");\n\n if (hasTotalPages) {\n if (hasTotalCount || hasPageSize) {\n throw new TypeError(\n \"Pagination descriptor must not combine totalPages with totalCount/pageSize\",\n );\n }\n if (!isPositiveInteger(runtime.totalPages)) {\n throw new RangeError(\"Pagination totalPages must be a positive integer\");\n }\n return runtime.totalPages;\n }\n\n if (!hasTotalCount || !hasPageSize) {\n throw new TypeError(\n \"Pagination descriptor requires either totalPages or totalCount with pageSize\",\n );\n }\n if (!isNonNegativeInteger(runtime.totalCount)) {\n throw new RangeError(\"Pagination totalCount must be a non-negative integer\");\n }\n if (!isPositiveInteger(runtime.pageSize)) {\n throw new RangeError(\"Pagination pageSize must be a positive integer\");\n }\n return Math.max(1, Math.ceil(runtime.totalCount / runtime.pageSize));\n}\n\nexport function validatePaginationDescriptor(\n descriptor: PaginationDescriptor,\n): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"Pagination descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, descriptorKeys, \"descriptor\");\n const runtime = descriptor as Readonly<Record<string, unknown>>;\n const totalPages = resolveTotalPages(runtime);\n if (!isPositiveInteger(runtime.currentPage)) {\n throw new RangeError(\"Pagination currentPage must be a positive integer\");\n }\n if (runtime.currentPage > totalPages) {\n throw new RangeError(\n `Pagination currentPage ${String(runtime.currentPage)} exceeds totalPages ${totalPages}`,\n );\n }\n if (\n runtime.siblingCount !== undefined &&\n !isNonNegativeInteger(runtime.siblingCount)\n ) {\n throw new RangeError(\"Pagination siblingCount must be a non-negative integer\");\n }\n if (\n runtime.boundaryCount !== undefined &&\n !isNonNegativeInteger(runtime.boundaryCount)\n ) {\n throw new RangeError(\"Pagination boundaryCount must be a non-negative integer\");\n }\n}\n\nexport function validatePaginationLabels(labels: PaginationLabels): void {\n if (!isObject(labels)) {\n throw new TypeError(\"Pagination labels must be an object\");\n }\n for (const field of [\"previous\", \"next\"] as const) {\n const value = labels[field];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Pagination labels.${field} must not be empty`);\n }\n }\n}\n\n/**\n * Pure page-window function, independent of accessibility naming, so its\n * boundary inputs (total 1 page, total 2 pages, current at the first/last\n * page) can be locked with tests without wiring a composer. Builds the set of\n * pages that must always be visible (both boundaries + the sibling window\n * around currentPage) and inserts a single ellipsis marker for any gap larger\n * than one page — never a lone hidden page, never two consecutive ellipses.\n */\nexport function computePaginationItems(\n totalPages: number,\n currentPage: number,\n options: PaginationWindowOptions = {},\n): readonly PaginationItem[] {\n if (!isPositiveInteger(totalPages)) {\n throw new RangeError(\"Pagination totalPages must be a positive integer\");\n }\n if (!isPositiveInteger(currentPage) || currentPage > totalPages) {\n throw new RangeError(\n `Pagination currentPage must be an integer between 1 and ${totalPages}`,\n );\n }\n const siblingCount = options.siblingCount ?? paginationDescriptorDefaults.siblingCount;\n const boundaryCount = options.boundaryCount ?? paginationDescriptorDefaults.boundaryCount;\n if (!isNonNegativeInteger(siblingCount)) {\n throw new RangeError(\"Pagination siblingCount must be a non-negative integer\");\n }\n if (!isNonNegativeInteger(boundaryCount)) {\n throw new RangeError(\"Pagination boundaryCount must be a non-negative integer\");\n }\n\n const shown = new Set<number>();\n for (let page = 1; page <= Math.min(boundaryCount, totalPages); page += 1) {\n shown.add(page);\n }\n for (\n let page = Math.max(1, totalPages - boundaryCount + 1);\n page <= totalPages;\n page += 1\n ) {\n shown.add(page);\n }\n for (\n let page = Math.max(1, currentPage - siblingCount);\n page <= Math.min(totalPages, currentPage + siblingCount);\n page += 1\n ) {\n shown.add(page);\n }\n\n const sortedPages = [...shown].sort((a, b) => a - b);\n const items: PaginationItem[] = [];\n let previousPage: number | null = null;\n for (const page of sortedPages) {\n if (previousPage !== null) {\n const gap = page - previousPage;\n // A gap of exactly 2 hides a single page — showing that number costs no\n // more space than \"...\" and is more useful, so ellipsis is reserved for\n // two or more genuinely hidden pages.\n if (gap === 2) {\n items.push({ type: \"page\", page: previousPage + 1 });\n } else if (gap > 2) {\n items.push({ type: \"ellipsis\" });\n }\n }\n items.push({ type: \"page\", page });\n previousPage = page;\n }\n return items;\n}\n\n/**\n * Attaches per-button accessible names and current-page derivation on top of\n * computePaginationItems. Ellipsis items intentionally carry no\n * accessibleName — they are decoration (\"...\"), not content, and must be\n * `aria-hidden`/excluded from the announcement.\n */\nexport function resolvePaginationDescriptor(\n descriptor: PaginationDescriptor,\n options: ResolvePaginationOptions,\n): ResolvedPaginationDescriptor {\n validatePaginationDescriptor(descriptor);\n validatePaginationLabels(options.labels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Pagination composeAccessibleName must be a function\");\n }\n\n const runtime = descriptor as Readonly<Record<string, unknown>>;\n const totalPages = resolveTotalPages(runtime);\n const currentPage = descriptor.currentPage;\n const rawItems = computePaginationItems(totalPages, currentPage, {\n siblingCount:\n descriptor.siblingCount ?? paginationDescriptorDefaults.siblingCount,\n boundaryCount:\n descriptor.boundaryCount ?? paginationDescriptorDefaults.boundaryCount,\n });\n\n const items: ResolvedPaginationItem[] = rawItems.map((item) => {\n if (item.type === \"ellipsis\") return item;\n const current = item.page === currentPage;\n const accessibleName = options.composeAccessibleName({\n page: item.page,\n totalPages,\n current,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Pagination composeAccessibleName must return a non-empty string\",\n );\n }\n return { type: \"page\", page: item.page, current, accessibleName };\n });\n\n return {\n currentPage,\n totalPages,\n hasPrevious: currentPage > 1,\n hasNext: currentPage < totalPages,\n labels: options.labels,\n items,\n };\n}\n\n/**\n * Visual recipe only covers chrome (size, gap, focus, current-item tone).\n * Following identity.md's \"primary fill은 주요 행동에, contentBrand는 ...\n * 현재 위치에\" rule and Steps' own current-step treatment\n * (stepsRecipe.indicator: border + content color, no background fill), the\n * current page is marked with a `border.focus`/`content.brand` outline, not a\n * filled `action.brand` pill — a page number is a location, not a command.\n */\nexport const paginationRecipe = {\n slots: [\"root\", \"item\", \"ellipsis\", \"previous\", \"next\"] as const,\n gap: spacing.xxs,\n item: {\n minSize: control.minTouchTarget,\n radius: \"md\" as const,\n color: {\n default: semanticColors.content.body,\n current: semanticColors.content.brand,\n },\n background: { hover: semanticColors.interaction.hover },\n border: { current: semanticColors.border.focus },\n },\n ellipsis: {\n color: semanticColors.content.secondary,\n icon: \"more\" as const,\n },\n navIcon: { previous: \"chevronStart\" as const, next: \"chevronEnd\" as const },\n focus: focusIndicatorContract,\n states: { disabledOpacity: opacity.disabled },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n item: {\n minSize: number;\n radius: \"md\";\n color: Record<\"default\" | \"current\", ColorReference>;\n background: Record<\"hover\", ColorReference>;\n border: Record<\"current\", ColorReference>;\n };\n ellipsis: { color: ColorReference; icon: SemanticIconName };\n navIcon: Record<\"previous\" | \"next\", SemanticIconName>;\n focus: typeof focusIndicatorContract;\n states: { disabledOpacity: number };\n};\n\n/**\n * Literal scenario names for behaviorRegistry.pagination (lead wires into\n * src/behaviors.ts).\n */\nexport const paginationBehaviorScenarios = [\n \"current-page-marked-and-announced-without-a-composed-live-region-spam\",\n \"ellipsis-is-decorative-and-excluded-from-the-accessible-tree\",\n \"previous-disabled-on-first-page-next-disabled-on-last-page\",\n \"single-page-renders-no-ellipsis-and-no-disabled-dead-buttons\",\n \"page-window-never-produces-two-consecutive-ellipses\",\n \"nav-landmark-present-with-a-localized-accessible-name\",\n] as const;\n"]}
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, opacity, spacing } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Bounded, page-addressable results share this contract on Web and Native.\n * 2026-10-15 Diairy audit found product-owned Native pagination duplicated this\n * window/selection model. Open-ended feeds remain the separate LoadMore contract.\n */\nexport type PaginationByCount = Readonly<{\n currentPage: number;\n totalCount: number;\n pageSize: number;\n totalPages?: never;\n hasNext?: never;\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\nexport type PaginationByPageCount = Readonly<{\n currentPage: number;\n totalPages: number;\n totalCount?: never;\n pageSize?: never;\n hasNext?: never;\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\n/**\n * Page-addressable results whose total is unknown: the source only reports whether a next page exists (Kakao\n * Local's `meta.is_end`, cursor APIs). The control shows previous, a product-composed current page label and\n * next — no numbered window, because pages beyond the next one cannot be addressed. Added 2026-10-18 for\n * choose-window search, which assembled its own previous/next Button row. Infinite feeds remain LoadMore.\n */\nexport type PaginationOpenEndedDescriptor = Readonly<{\n currentPage: number;\n hasNext: boolean;\n totalCount?: never;\n pageSize?: never;\n totalPages?: never;\n siblingCount?: never;\n boundaryCount?: never;\n}>;\n\n/** The current page label is the visible text and the accessible name, e.g. \"3페이지\" / \"Page 3\". */\nexport type ComposePaginationCurrentPageLabel = (info: Readonly<{ page: number }>) => string;\n\nexport type ResolvedPaginationOpenEndedDescriptor = Readonly<{\n currentPage: number;\n hasPrevious: boolean;\n hasNext: boolean;\n labels: PaginationLabels;\n currentPageLabel: string;\n}>;\n\nexport type ResolvePaginationOpenEndedOptions = Readonly<{\n labels: PaginationLabels;\n composeCurrentPageLabel: ComposePaginationCurrentPageLabel;\n}>;\n\n/**\n * Required contract is exactly current page + (total count or total page\n * count) + page size — nothing else. A page-size changer and a \"jump to page\"\n * input are intentionally absent: neither has measured product demand, and\n * both add a second way to reach the same currentPage/totalPages state this\n * module already owns. If a product needs them, they compose as separate\n * controls that call the same onPageChange — they are not missing API surface\n * here, they are a deliberate exclusion (see docs/pagination.md).\n */\nexport type PaginationDescriptor = PaginationByCount | PaginationByPageCount;\n\nexport type PaginationLabels = Readonly<{\n previous: string;\n next: string;\n}>;\n\nexport const paginationDescriptorDefaults = {\n siblingCount: 1,\n boundaryCount: 1,\n} as const satisfies Readonly<{ siblingCount: number; boundaryCount: number }>;\n\nexport type PaginationItem =\n | Readonly<{ type: \"page\"; page: number }>\n | Readonly<{ type: \"ellipsis\" }>;\n\nexport type PaginationWindowOptions = Readonly<{\n siblingCount?: number;\n boundaryCount?: number;\n}>;\n\nexport type PaginationAccessibleNameInfo = Readonly<{\n page: number;\n totalPages: number;\n current: boolean;\n}>;\n\n/**\n * Products own the exact phrase order, same reason as Steps'\n * composeAccessibleName: Korean \"5 페이지 중 3 페이지\" and an English \"page 3\n * of 5\" have different word order, and every page button gets its own\n * composed name (not just a landmark summary) because ellipsis gaps mean a\n * bare visible number (\"7\") does not by itself convey how many pages exist.\n */\nexport type ComposePaginationAccessibleName = (\n info: PaginationAccessibleNameInfo,\n) => string;\n\nexport type ResolvedPaginationItem =\n | Readonly<{\n type: \"page\";\n page: number;\n current: boolean;\n accessibleName: string;\n }>\n | Readonly<{ type: \"ellipsis\" }>;\n\nexport type ResolvedPaginationDescriptor = Readonly<{\n currentPage: number;\n totalPages: number;\n hasPrevious: boolean;\n hasNext: boolean;\n labels: PaginationLabels;\n items: readonly ResolvedPaginationItem[];\n}>;\n\nexport type ResolvePaginationOptions = Readonly<{\n labels: PaginationLabels;\n composeAccessibleName: ComposePaginationAccessibleName;\n}>;\n\n/** Why a page change was requested — analytics and controllers key off this, not the target page alone. */\nexport type PaginationChangeReason = \"previous\" | \"next\" | \"page\";\nexport type PaginationChangeHandler = (\n page: number,\n reason: PaginationChangeReason,\n) => void;\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 1;\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n}\n\nconst descriptorKeys = new Set([\n \"currentPage\",\n \"totalCount\",\n \"pageSize\",\n \"totalPages\",\n \"siblingCount\",\n \"boundaryCount\",\n]);\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 Pagination ${field} field: ${key}`);\n }\n }\n}\n\nfunction resolveTotalPages(runtime: Readonly<Record<string, unknown>>): number {\n const hasTotalPages = Object.prototype.hasOwnProperty.call(runtime, \"totalPages\");\n const hasTotalCount = Object.prototype.hasOwnProperty.call(runtime, \"totalCount\");\n const hasPageSize = Object.prototype.hasOwnProperty.call(runtime, \"pageSize\");\n\n if (hasTotalPages) {\n if (hasTotalCount || hasPageSize) {\n throw new TypeError(\n \"Pagination descriptor must not combine totalPages with totalCount/pageSize\",\n );\n }\n if (!isPositiveInteger(runtime.totalPages)) {\n throw new RangeError(\"Pagination totalPages must be a positive integer\");\n }\n return runtime.totalPages;\n }\n\n if (!hasTotalCount || !hasPageSize) {\n throw new TypeError(\n \"Pagination descriptor requires either totalPages or totalCount with pageSize\",\n );\n }\n if (!isNonNegativeInteger(runtime.totalCount)) {\n throw new RangeError(\"Pagination totalCount must be a non-negative integer\");\n }\n if (!isPositiveInteger(runtime.pageSize)) {\n throw new RangeError(\"Pagination pageSize must be a positive integer\");\n }\n return Math.max(1, Math.ceil(runtime.totalCount / runtime.pageSize));\n}\n\nexport function validatePaginationDescriptor(\n descriptor: PaginationDescriptor,\n): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"Pagination descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, descriptorKeys, \"descriptor\");\n const runtime = descriptor as Readonly<Record<string, unknown>>;\n const totalPages = resolveTotalPages(runtime);\n if (!isPositiveInteger(runtime.currentPage)) {\n throw new RangeError(\"Pagination currentPage must be a positive integer\");\n }\n if (runtime.currentPage > totalPages) {\n throw new RangeError(\n `Pagination currentPage ${String(runtime.currentPage)} exceeds totalPages ${totalPages}`,\n );\n }\n if (\n runtime.siblingCount !== undefined &&\n !isNonNegativeInteger(runtime.siblingCount)\n ) {\n throw new RangeError(\"Pagination siblingCount must be a non-negative integer\");\n }\n if (\n runtime.boundaryCount !== undefined &&\n !isNonNegativeInteger(runtime.boundaryCount)\n ) {\n throw new RangeError(\"Pagination boundaryCount must be a non-negative integer\");\n }\n}\n\nexport function validatePaginationLabels(labels: PaginationLabels): void {\n if (!isObject(labels)) {\n throw new TypeError(\"Pagination labels must be an object\");\n }\n for (const field of [\"previous\", \"next\"] as const) {\n const value = labels[field];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Pagination labels.${field} must not be empty`);\n }\n }\n}\n\nconst openEndedDescriptorKeys = new Set([\"currentPage\", \"hasNext\"]);\n\n/** Distinguishes the open-ended form by its `hasNext` field, the one key the bounded forms forbid. */\nexport function isPaginationOpenEndedDescriptor(\n descriptor: PaginationDescriptor | PaginationOpenEndedDescriptor,\n): descriptor is PaginationOpenEndedDescriptor {\n return isObject(descriptor) && Object.prototype.hasOwnProperty.call(descriptor, \"hasNext\");\n}\n\nexport function validatePaginationOpenEndedDescriptor(\n descriptor: PaginationOpenEndedDescriptor,\n): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"Pagination descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, openEndedDescriptorKeys, \"open-ended descriptor\");\n if (!isPositiveInteger(descriptor.currentPage)) {\n throw new RangeError(\"Pagination currentPage must be a positive integer\");\n }\n if (typeof descriptor.hasNext !== \"boolean\") {\n throw new TypeError(\"Pagination hasNext must be a boolean\");\n }\n}\n\n/**\n * Open-ended counterpart of resolvePaginationDescriptor. A first page without a next page still resolves (both\n * steps unavailable) so the renderer, not the product, decides how a single page is presented.\n */\nexport function resolvePaginationOpenEndedDescriptor(\n descriptor: PaginationOpenEndedDescriptor,\n options: ResolvePaginationOpenEndedOptions,\n): ResolvedPaginationOpenEndedDescriptor {\n validatePaginationOpenEndedDescriptor(descriptor);\n validatePaginationLabels(options.labels);\n if (typeof options.composeCurrentPageLabel !== \"function\") {\n throw new TypeError(\"Pagination composeCurrentPageLabel must be a function\");\n }\n const currentPageLabel = options.composeCurrentPageLabel({ page: descriptor.currentPage });\n if (typeof currentPageLabel !== \"string\" || currentPageLabel.trim().length === 0) {\n throw new TypeError(\"Pagination composeCurrentPageLabel must return a non-empty string\");\n }\n return {\n currentPage: descriptor.currentPage,\n hasPrevious: descriptor.currentPage > 1,\n hasNext: descriptor.hasNext,\n labels: options.labels,\n currentPageLabel,\n };\n}\n\n/**\n * Pure page-window function, independent of accessibility naming, so its\n * boundary inputs (total 1 page, total 2 pages, current at the first/last\n * page) can be locked with tests without wiring a composer. Builds the set of\n * pages that must always be visible (both boundaries + the sibling window\n * around currentPage) and inserts a single ellipsis marker for any gap larger\n * than one page — never a lone hidden page, never two consecutive ellipses.\n */\nexport function computePaginationItems(\n totalPages: number,\n currentPage: number,\n options: PaginationWindowOptions = {},\n): readonly PaginationItem[] {\n if (!isPositiveInteger(totalPages)) {\n throw new RangeError(\"Pagination totalPages must be a positive integer\");\n }\n if (!isPositiveInteger(currentPage) || currentPage > totalPages) {\n throw new RangeError(\n `Pagination currentPage must be an integer between 1 and ${totalPages}`,\n );\n }\n const siblingCount = options.siblingCount ?? paginationDescriptorDefaults.siblingCount;\n const boundaryCount = options.boundaryCount ?? paginationDescriptorDefaults.boundaryCount;\n if (!isNonNegativeInteger(siblingCount)) {\n throw new RangeError(\"Pagination siblingCount must be a non-negative integer\");\n }\n if (!isNonNegativeInteger(boundaryCount)) {\n throw new RangeError(\"Pagination boundaryCount must be a non-negative integer\");\n }\n\n const shown = new Set<number>();\n for (let page = 1; page <= Math.min(boundaryCount, totalPages); page += 1) {\n shown.add(page);\n }\n for (\n let page = Math.max(1, totalPages - boundaryCount + 1);\n page <= totalPages;\n page += 1\n ) {\n shown.add(page);\n }\n for (\n let page = Math.max(1, currentPage - siblingCount);\n page <= Math.min(totalPages, currentPage + siblingCount);\n page += 1\n ) {\n shown.add(page);\n }\n\n const sortedPages = [...shown].sort((a, b) => a - b);\n const items: PaginationItem[] = [];\n let previousPage: number | null = null;\n for (const page of sortedPages) {\n if (previousPage !== null) {\n const gap = page - previousPage;\n // A gap of exactly 2 hides a single page — showing that number costs no\n // more space than \"...\" and is more useful, so ellipsis is reserved for\n // two or more genuinely hidden pages.\n if (gap === 2) {\n items.push({ type: \"page\", page: previousPage + 1 });\n } else if (gap > 2) {\n items.push({ type: \"ellipsis\" });\n }\n }\n items.push({ type: \"page\", page });\n previousPage = page;\n }\n return items;\n}\n\n/**\n * Attaches per-button accessible names and current-page derivation on top of\n * computePaginationItems. Ellipsis items intentionally carry no\n * accessibleName — they are decoration (\"...\"), not content, and must be\n * `aria-hidden`/excluded from the announcement.\n */\nexport function resolvePaginationDescriptor(\n descriptor: PaginationDescriptor,\n options: ResolvePaginationOptions,\n): ResolvedPaginationDescriptor {\n validatePaginationDescriptor(descriptor);\n validatePaginationLabels(options.labels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Pagination composeAccessibleName must be a function\");\n }\n\n const runtime = descriptor as Readonly<Record<string, unknown>>;\n const totalPages = resolveTotalPages(runtime);\n const currentPage = descriptor.currentPage;\n const rawItems = computePaginationItems(totalPages, currentPage, {\n siblingCount:\n descriptor.siblingCount ?? paginationDescriptorDefaults.siblingCount,\n boundaryCount:\n descriptor.boundaryCount ?? paginationDescriptorDefaults.boundaryCount,\n });\n\n const items: ResolvedPaginationItem[] = rawItems.map((item) => {\n if (item.type === \"ellipsis\") return item;\n const current = item.page === currentPage;\n const accessibleName = options.composeAccessibleName({\n page: item.page,\n totalPages,\n current,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Pagination composeAccessibleName must return a non-empty string\",\n );\n }\n return { type: \"page\", page: item.page, current, accessibleName };\n });\n\n return {\n currentPage,\n totalPages,\n hasPrevious: currentPage > 1,\n hasNext: currentPage < totalPages,\n labels: options.labels,\n items,\n };\n}\n\n/**\n * Visual recipe only covers chrome (size, gap, focus, current-item tone).\n * Following identity.md's \"primary fill은 주요 행동에, contentBrand는 ...\n * 현재 위치에\" rule and Steps' own current-step treatment\n * (stepsRecipe.indicator: border + content color, no background fill), the\n * current page is marked with a `border.focus`/`content.brand` outline, not a\n * filled `action.brand` pill — a page number is a location, not a command.\n */\nexport const paginationRecipe = {\n slots: [\"root\", \"item\", \"ellipsis\", \"previous\", \"next\"] as const,\n gap: spacing.xxs,\n item: {\n minSize: control.minTouchTarget,\n radius: \"md\" as const,\n color: {\n default: semanticColors.content.body,\n current: semanticColors.content.brand,\n },\n background: { hover: semanticColors.interaction.hover },\n border: { current: semanticColors.border.focus },\n },\n ellipsis: {\n color: semanticColors.content.secondary,\n icon: \"more\" as const,\n },\n navIcon: { previous: \"chevronStart\" as const, next: \"chevronEnd\" as const },\n /**\n * Open-ended current page label: plain text between the steps, not a button, because it is the only page the\n * control can show. It keeps the item's touch-target height so the row does not jump between modes.\n */\n pageLabel: {\n minHeight: control.minTouchTarget,\n paddingHorizontal: spacing.xs,\n color: semanticColors.content.body,\n },\n focus: focusIndicatorContract,\n states: { disabledOpacity: opacity.disabled },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n item: {\n minSize: number;\n radius: \"md\";\n color: Record<\"default\" | \"current\", ColorReference>;\n background: Record<\"hover\", ColorReference>;\n border: Record<\"current\", ColorReference>;\n };\n ellipsis: { color: ColorReference; icon: SemanticIconName };\n navIcon: Record<\"previous\" | \"next\", SemanticIconName>;\n pageLabel: { minHeight: number; paddingHorizontal: number; color: ColorReference };\n focus: typeof focusIndicatorContract;\n states: { disabledOpacity: number };\n};\n\n/**\n * Literal scenario names for behaviorRegistry.pagination (lead wires into\n * src/behaviors.ts).\n */\nexport const paginationBehaviorScenarios = [\n \"current-page-marked-and-announced-without-a-composed-live-region-spam\",\n \"ellipsis-is-decorative-and-excluded-from-the-accessible-tree\",\n \"previous-disabled-on-first-page-next-disabled-on-last-page\",\n \"single-page-renders-no-ellipsis-and-no-disabled-dead-buttons\",\n \"page-window-never-produces-two-consecutive-ellipses\",\n \"nav-landmark-present-with-a-localized-accessible-name\",\n] as const;\n"]}
|
|
@@ -53,9 +53,13 @@ export declare function validateScreenExit(exit: Readonly<{
|
|
|
53
53
|
* `body-edge`: header, notice and footer keep the shell padding; only the body's inline padding is 0 so full-width
|
|
54
54
|
* list rows (ConversationRow, ListRow-based items) supply their own inset — review S5#9 measured DM avatars ~32pt
|
|
55
55
|
* from the edge because body padding and row padding stacked (2026-10-17 user decision).
|
|
56
|
+
* `body-bleed`: header, notice and footer keep the shell padding; the body has no gutter on any side, so a map or
|
|
57
|
+
* canvas meets the header (or notice), the screen edges and the footer. With no footer the body is still the last
|
|
58
|
+
* region and clears the bottom insets the screen owns (`safeAreaBottom`, an overlay BottomNavigation) without the
|
|
59
|
+
* gutter. choose-window's map screens used `body-edge` and kept a 16pt strip above and below the map (2026-10-18).
|
|
56
60
|
*/
|
|
57
|
-
export type ScreenContentInset = "default" | "none" | "body-edge";
|
|
58
|
-
export declare const screenContentInsets: readonly ["default", "none", "body-edge"];
|
|
61
|
+
export type ScreenContentInset = "default" | "none" | "body-edge" | "body-bleed";
|
|
62
|
+
export declare const screenContentInsets: readonly ["default", "none", "body-edge", "body-bleed"];
|
|
59
63
|
/**
|
|
60
64
|
* 화면 진입 시 첫 입력 포커스(2026-10-18 사용자 요청: "input이 있는 화면에 진입하면 포커스되도록, 웹앱 둘 다").
|
|
61
65
|
* 앱마다 autoFocus를 따로 붙이던 일을 화면 골격이 맡는다. 입력 칸은 가장 가까운 화면·시트 범위에 자신을 등록하고,
|
|
@@ -232,6 +236,12 @@ export type ChatReactionSummaryItem = Readonly<{
|
|
|
232
236
|
export type ChatReactionSummary = Readonly<{
|
|
233
237
|
label: string;
|
|
234
238
|
items: readonly ChatReactionSummaryItem[];
|
|
239
|
+
/**
|
|
240
|
+
* Counts only, for readers who cannot react (2026-10-18 Utilverse guests): drawn in the same place beside the time
|
|
241
|
+
* as non-interactive text named by `label`, without the pill surface, so it does not look or act like a control.
|
|
242
|
+
* Default `false` (the pill button that opens the hold menu).
|
|
243
|
+
*/
|
|
244
|
+
readOnly?: boolean;
|
|
235
245
|
}>;
|
|
236
246
|
/**
|
|
237
247
|
* Validates ChatMessage `reactions.summary` and marks which counts are drawn. The label (the button's accessible name)
|
|
@@ -280,7 +290,22 @@ export declare const mediaAlbumRecipe: {
|
|
|
280
290
|
/** Previous/next are small ghost IconButtons so the counter row stays compact beside thumbnails. */
|
|
281
291
|
readonly controlSize: "small";
|
|
282
292
|
readonly controlGlyphSize: 20;
|
|
293
|
+
/**
|
|
294
|
+
* Loading / failed page drawn by `MediaAlbumPageStatus` (2026-10-18 Utilverse: the page frame was one accessible
|
|
295
|
+
* image, so the loading text and retry button inside it were unreachable by screen readers). The status block is
|
|
296
|
+
* centered in the frame (VQ-09); retry is a small secondary Button.
|
|
297
|
+
*/
|
|
298
|
+
readonly pageStatus: {
|
|
299
|
+
readonly gap: 8;
|
|
300
|
+
readonly padding: 12;
|
|
301
|
+
readonly textVariant: "caption";
|
|
302
|
+
readonly retryTone: "secondary";
|
|
303
|
+
readonly retrySize: "small";
|
|
304
|
+
};
|
|
283
305
|
};
|
|
306
|
+
export type MediaAlbumPageStatusKind = "loading" | "error";
|
|
307
|
+
/** A page status names what the reader is waiting for or what failed; the label is the only copy. */
|
|
308
|
+
export declare function validateMediaAlbumPageStatus(kind: MediaAlbumPageStatusKind, label: string): void;
|
|
284
309
|
/** A controlled index outside the set is a contract error, like Carousel's unknown `currentKey`. */
|
|
285
310
|
export declare function validateMediaAlbum(count: number, index: number): void;
|
|
286
311
|
/** One message or comment carries one photo set: either the eager `media` previews or the paged `mediaAlbum`. */
|
|
@@ -456,6 +481,21 @@ export declare function resolveSearchCommit(value: string): string | null;
|
|
|
456
481
|
*/
|
|
457
482
|
export type SearchEmptyCause = "none" | "filters" | "query";
|
|
458
483
|
export declare function resolveSearchEmptyCause(count: number | null, appliedFilterCount: number): SearchEmptyCause;
|
|
484
|
+
/**
|
|
485
|
+
* What the SearchScreen filter rail does to a result. `narrow` (default) only removes results, so with nothing applied
|
|
486
|
+
* a zero result is the query's and no filter can bring results back. `scope` chooses what is searched (keyword vs
|
|
487
|
+
* address, region, map area): changing it can turn a zero into results even with nothing applied (2026-10-18:
|
|
488
|
+
* choose-window's keyword/address switch is the recovery for a zero result, so it skipped resultSummary entirely).
|
|
489
|
+
*/
|
|
490
|
+
export type SearchFiltersRole = "narrow" | "scope";
|
|
491
|
+
export declare const searchFiltersRoles: readonly ["narrow", "scope"];
|
|
492
|
+
/**
|
|
493
|
+
* Whether SearchScreen shows its filter rail (`filters` and the filter sheet trigger). Two-step search shows it only
|
|
494
|
+
* beside results so typing keeps the pinned area short. A query-caused zero hides it only when the filters narrow,
|
|
495
|
+
* because then none of them can fix the zero; scope filters stay as the recovery path. A filter-caused zero always keeps
|
|
496
|
+
* it so the cause can be undone. Both renderers call this so the rule cannot drift between platforms.
|
|
497
|
+
*/
|
|
498
|
+
export declare function resolveSearchFilterRailVisible(phase: SearchScreenPhase, twoStep: boolean, cause: SearchEmptyCause, role?: SearchFiltersRole): boolean;
|
|
459
499
|
/**
|
|
460
500
|
* Index to focus after removing item `removedIndex` from a list that now has `remaining` items:
|
|
461
501
|
* the item that slid into its place, else the new last item, else -1 (move to the fallback target).
|
|
@@ -537,4 +577,14 @@ export declare const iconTileRecipe: Readonly<{
|
|
|
537
577
|
size: IconTileSize;
|
|
538
578
|
}>;
|
|
539
579
|
}>;
|
|
580
|
+
/**
|
|
581
|
+
* What an IconTile passes to an `icon` render function: the rendered icon edge (`iconSize` for `fixed`, the measured
|
|
582
|
+
* cell-clamped edge for `fill`). Fixed-size artwork (images, generated launcher art) sizes itself from it; Utilverse
|
|
583
|
+
* re-computed the 48–68 clamp from its grid width to size its artwork (2026-10-18).
|
|
584
|
+
*/
|
|
585
|
+
export type IconTileArtwork = Readonly<{
|
|
586
|
+
edge: number;
|
|
587
|
+
}>;
|
|
588
|
+
/** The edge a `size="fill"` icon takes in a cell of `cellWidth`, for layout estimates made before the tile measures. */
|
|
589
|
+
export declare function resolveIconTileFillEdge(cellWidth: number): number;
|
|
540
590
|
//# sourceMappingURL=screen-patterns.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,CAAC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GAC3B,QAAQ,CAAC;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1G,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,kBAAsC;;;;;EAatF;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;IAM7B;;;OAGG;;;CAGK,CAAC;AAEX;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC;AAC9C,eAAO,MAAM,gBAAgB;IAC3B,gHAAgH;;;;CAIxG,CAAC;AACX,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,UAAU,EAAE,OAAO,QAItG;AAED
|
|
1
|
+
{"version":3,"file":"screen-patterns.d.ts","sourceRoot":"","sources":["../src/screen-patterns.ts"],"names":[],"mappings":"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"}
|