@visitwonders/assembly 0.15.0 → 0.16.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/README.md +8 -0
- package/dist/_app_/data/pagination-cluster.js +1 -0
- package/dist/_app_/data/pagination.js +1 -0
- package/dist/action/button.css +30 -19
- package/dist/data/index.js +1 -0
- package/dist/data/index.js.map +1 -1
- package/dist/data/pagination-cluster.js +106 -0
- package/dist/data/pagination-cluster.js.map +1 -0
- package/dist/data/pagination.css +216 -0
- package/dist/data/pagination.js +287 -0
- package/dist/data/pagination.js.map +1 -0
- package/dist/form/calendar.css +6 -6
- package/dist/form/checkbox.css +25 -24
- package/dist/form/date-range-picker.css +1 -1
- package/dist/form/input.css +16 -15
- package/dist/form/multi-combobox.css +2 -2
- package/dist/form/multi-select.css +2 -2
- package/dist/form/radio.css +19 -16
- package/dist/form/toggle.css +4 -2
- package/dist/status/tag.css +2 -2
- package/dist/styles/semantic/colors.css +31 -5
- package/dist/styles/semantic/component.css +154 -0
- package/dist/styles/semantic/effects.css +9 -3
- package/dist/styles/semantic/typography.css +60 -0
- package/dist/styles/tokens.css +3 -0
- package/dist/styles.css +258 -8
- package/dist/typography/{heading-css-726c4c3109f2b741657733e1ba103c67.css → heading-css-eca8b0ae619f69fcbe9535f4700db421.css} +35 -3
- package/dist/typography/heading.js +34 -39
- package/dist/typography/{text-css-935f55e9cd74b06a5ce61330c4c79ef9.css → text-css-a4c06f76a813db6b613c4f3c22e6bb85.css} +23 -9
- package/dist/typography/text.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -100,6 +100,14 @@ appends (rather than replaces) the Ember outlet view inside
|
|
|
100
100
|
|
|
101
101
|
[Longer description of how to use the addon in apps.]
|
|
102
102
|
|
|
103
|
+
## Migrations
|
|
104
|
+
|
|
105
|
+
Breaking changes — token retirements, behaviour shifts, and visual-parity diffs that downstream consumers should know about — are documented one-file-per-change under [`docs/migrations/`](docs/migrations).
|
|
106
|
+
|
|
107
|
+
Recent:
|
|
108
|
+
|
|
109
|
+
- [Token Extension and Focus Ring Upgrade](docs/migrations/2026-04-27-token-extension-and-focus-ring-upgrade.md) (2026-04-27): surface-vs-fill split, two-stop focus ring (WCAG 2.2 non-text-contrast), typography role sub-tokens, action + input matrices populated, halo tokens retired.
|
|
110
|
+
|
|
103
111
|
## Contributing
|
|
104
112
|
|
|
105
113
|
See the [Contributing](CONTRIBUTING.md) guide for details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@visitwonders/assembly/data/pagination-cluster";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@visitwonders/assembly/data/pagination";
|
package/dist/action/button.css
CHANGED
|
@@ -40,15 +40,11 @@
|
|
|
40
40
|
|
|
41
41
|
.button_e0e07a6eb[data-variant][data-tone]:focus-visible {
|
|
42
42
|
outline: none;
|
|
43
|
-
box-shadow:
|
|
44
|
-
0 0 0 2px var(--color-bg-surface),
|
|
45
|
-
0 0 0 4px var(--color-focus-ring-halo);
|
|
43
|
+
box-shadow: var(--focus-ring);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
.button_e0e07a6eb[data-variant][data-tone="critical"]:focus-visible {
|
|
49
|
-
box-shadow:
|
|
50
|
-
0 0 0 2px var(--color-bg-surface),
|
|
51
|
-
0 0 0 4px var(--color-focus-ring-halo-critical);
|
|
47
|
+
box-shadow: var(--focus-ring-critical);
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
/* ===================================
|
|
@@ -91,57 +87,72 @@
|
|
|
91
87
|
|
|
92
88
|
/* ===================================
|
|
93
89
|
* Solid Variant
|
|
90
|
+
*
|
|
91
|
+
* Solid variants are the canonical action-matrix consumers: bg + text
|
|
92
|
+
* come from `--color-action-{tone}-{bg|text}-*`. The `neutral` tone
|
|
93
|
+
* is a documented variant override — its visual is a "white card"
|
|
94
|
+
* (surface bg + control border + drop shadow), so it picks `bg` from
|
|
95
|
+
* `--color-bg-surface` rather than the matrix's `interactive` fill,
|
|
96
|
+
* but still pulls border/text/active-bg through the matrix.
|
|
94
97
|
* =================================== */
|
|
95
98
|
|
|
96
99
|
/* Primary */
|
|
97
100
|
.button_e0e07a6eb[data-variant="solid"][data-tone="primary"] {
|
|
98
|
-
background: var(--color-bg-
|
|
99
|
-
color: var(--color-text-
|
|
101
|
+
background: var(--color-action-primary-bg-default);
|
|
102
|
+
color: var(--color-action-primary-text-default);
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
.button_e0e07a6eb[data-variant="solid"][data-tone="primary"]:hover:not(:disabled) {
|
|
103
|
-
background: var(--color-
|
|
106
|
+
background: var(--color-action-primary-bg-hover);
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
.button_e0e07a6eb[data-variant="solid"][data-tone="primary"]:active:not(:disabled) {
|
|
107
|
-
background: var(--color-
|
|
110
|
+
background: var(--color-action-primary-bg-pressed);
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
/* Neutral
|
|
113
|
+
/* Neutral — variant override: bg stays on surface (white card),
|
|
114
|
+
border + text reach through the matrix. */
|
|
111
115
|
.button_e0e07a6eb[data-variant="solid"][data-tone="neutral"] {
|
|
112
116
|
background: var(--color-bg-surface);
|
|
113
|
-
color: var(--color-text);
|
|
114
|
-
border: 1px solid var(--color-border-
|
|
117
|
+
color: var(--color-action-neutral-text-default);
|
|
118
|
+
border: 1px solid var(--color-action-neutral-border-default);
|
|
115
119
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
.button_e0e07a6eb[data-variant="solid"][data-tone="neutral"]:hover:not(:disabled) {
|
|
119
|
-
border-color: var(--color-border-
|
|
123
|
+
border-color: var(--color-action-neutral-border-hover);
|
|
120
124
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.08);
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
.button_e0e07a6eb[data-variant="solid"][data-tone="neutral"]:active:not(:disabled) {
|
|
124
128
|
background: var(--color-bg-neutral-subtle);
|
|
125
|
-
border-color: var(--color-border-
|
|
129
|
+
border-color: var(--color-action-neutral-border-pressed);
|
|
126
130
|
box-shadow: none;
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
/* Critical */
|
|
130
134
|
.button_e0e07a6eb[data-variant="solid"][data-tone="critical"] {
|
|
131
|
-
background: var(--color-bg-
|
|
132
|
-
color: var(--color-text-
|
|
135
|
+
background: var(--color-action-critical-bg-default);
|
|
136
|
+
color: var(--color-action-critical-text-default);
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
.button_e0e07a6eb[data-variant="solid"][data-tone="critical"]:hover:not(:disabled) {
|
|
136
|
-
background: var(--color-
|
|
140
|
+
background: var(--color-action-critical-bg-hover);
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
.button_e0e07a6eb[data-variant="solid"][data-tone="critical"]:active:not(:disabled) {
|
|
140
|
-
background: var(--color-
|
|
144
|
+
background: var(--color-action-critical-bg-pressed);
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
/* ===================================
|
|
144
148
|
* Outline Variant
|
|
149
|
+
*
|
|
150
|
+
* Outline / ghost / link variants stay semantic-direct: their `text`
|
|
151
|
+
* carries the tone (so `--color-text-{tone}` is the right anchor),
|
|
152
|
+
* not the matrix's `--color-action-*-text-*` cells which encode
|
|
153
|
+
* "text on a solid fill" (e.g. white-on-primary). The matrix's
|
|
154
|
+
* `border` cells likewise target form-control borders (gray-300);
|
|
155
|
+
* outline-neutral wants the heavier `--color-border-neutral`.
|
|
145
156
|
* =================================== */
|
|
146
157
|
|
|
147
158
|
/* Primary */
|
package/dist/data/index.js
CHANGED
package/dist/data/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Pure helper for the Pagination component's page-cluster algorithm.
|
|
2
|
+
// Co-located here under src/data/ next to where pagination.gts will land.
|
|
3
|
+
// Spec contract: docs/specs/pagination.md §Behaviour > Page-cluster algorithm.
|
|
4
|
+
//
|
|
5
|
+
// This file has no Glimmer or DOM dependencies; it is a pure function so it
|
|
6
|
+
// can be unit-tested in isolation and reused by any future consumer.
|
|
7
|
+
|
|
8
|
+
// Required for the addon's rollup `app-reexports` pattern (`data/!(index).js`)
|
|
9
|
+
// to publish this file into `dist/`. Do not remove — without a default export
|
|
10
|
+
// the file is dropped from the build silently. Consumers should use the named
|
|
11
|
+
// exports below.
|
|
12
|
+
var paginationCluster = {};
|
|
13
|
+
function buildPageCluster(args) {
|
|
14
|
+
const {
|
|
15
|
+
totalPages,
|
|
16
|
+
currentPage,
|
|
17
|
+
siblingCount,
|
|
18
|
+
boundaryCount
|
|
19
|
+
} = args;
|
|
20
|
+
if (totalPages <= 0) return [];
|
|
21
|
+
if (totalPages === 1) return [{
|
|
22
|
+
kind: 'page',
|
|
23
|
+
page: 1
|
|
24
|
+
}];
|
|
25
|
+
const sibling = {
|
|
26
|
+
start: Math.max(1, currentPage - siblingCount),
|
|
27
|
+
end: Math.min(totalPages, currentPage + siblingCount)
|
|
28
|
+
};
|
|
29
|
+
const initial = [];
|
|
30
|
+
if (boundaryCount > 0) {
|
|
31
|
+
initial.push({
|
|
32
|
+
start: 1,
|
|
33
|
+
end: Math.min(boundaryCount, totalPages)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
initial.push(sibling);
|
|
37
|
+
if (boundaryCount > 0) {
|
|
38
|
+
initial.push({
|
|
39
|
+
start: Math.max(1, totalPages - boundaryCount + 1),
|
|
40
|
+
end: totalPages
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const merged = mergeOverlapping(initial);
|
|
44
|
+
const collapsed = collapseSinglePageGapIfOnly(merged);
|
|
45
|
+
return emit(collapsed, sibling);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Merge intervals that overlap or are directly touching (gap < 1).
|
|
49
|
+
// Input intervals are already in non-decreasing-start order by construction
|
|
50
|
+
// (left boundary, sibling, right boundary).
|
|
51
|
+
function mergeOverlapping(intervals) {
|
|
52
|
+
const sorted = [...intervals].sort((a, b) => a.start - b.start);
|
|
53
|
+
const result = [];
|
|
54
|
+
for (const next of sorted) {
|
|
55
|
+
const last = result[result.length - 1];
|
|
56
|
+
if (last && next.start <= last.end + 1) {
|
|
57
|
+
last.end = Math.max(last.end, next.end);
|
|
58
|
+
} else {
|
|
59
|
+
result.push({
|
|
60
|
+
...next
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Rule 4: a single hidden page is never elided — but only when it is the
|
|
68
|
+
// ONLY remaining gap. With multiple gaps, single-page gaps stay as ellipses
|
|
69
|
+
// so the cluster shape is consistent. See spec §Behaviour for worked
|
|
70
|
+
// examples that pin this interpretation (T=7,C=4 keeps both ellipses;
|
|
71
|
+
// T=6,C=3 inlines its single gap because there is only one).
|
|
72
|
+
function collapseSinglePageGapIfOnly(intervals) {
|
|
73
|
+
if (intervals.length !== 2) return intervals;
|
|
74
|
+
const [first, second] = intervals;
|
|
75
|
+
const gapSize = second.start - first.end - 1;
|
|
76
|
+
if (gapSize !== 1) return intervals;
|
|
77
|
+
return [{
|
|
78
|
+
start: first.start,
|
|
79
|
+
end: second.end
|
|
80
|
+
}];
|
|
81
|
+
}
|
|
82
|
+
function emit(intervals, sibling) {
|
|
83
|
+
const items = [];
|
|
84
|
+
for (let i = 0; i < intervals.length; i++) {
|
|
85
|
+
const interval = intervals[i];
|
|
86
|
+
for (let p = interval.start; p <= interval.end; p++) {
|
|
87
|
+
items.push({
|
|
88
|
+
kind: 'page',
|
|
89
|
+
page: p
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const next = intervals[i + 1];
|
|
93
|
+
if (next) {
|
|
94
|
+
const gapEnd = next.start - 1;
|
|
95
|
+
const key = gapEnd < sibling.start ? 'ellipsis-left' : 'ellipsis-right';
|
|
96
|
+
items.push({
|
|
97
|
+
kind: 'ellipsis',
|
|
98
|
+
key
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return items;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { buildPageCluster, paginationCluster as default };
|
|
106
|
+
//# sourceMappingURL=pagination-cluster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination-cluster.js","sources":["../../src/data/pagination-cluster.ts"],"sourcesContent":["// Pure helper for the Pagination component's page-cluster algorithm.\n// Co-located here under src/data/ next to where pagination.gts will land.\n// Spec contract: docs/specs/pagination.md §Behaviour > Page-cluster algorithm.\n//\n// This file has no Glimmer or DOM dependencies; it is a pure function so it\n// can be unit-tested in isolation and reused by any future consumer.\n\n// Required for the addon's rollup `app-reexports` pattern (`data/!(index).js`)\n// to publish this file into `dist/`. Do not remove — without a default export\n// the file is dropped from the build silently. Consumers should use the named\n// exports below.\nexport default {};\n\nexport type PageClusterItem =\n | { kind: 'page'; page: number }\n | { kind: 'ellipsis'; key: string };\n\nexport interface BuildClusterArgs {\n /** Total page count, T = ceil(totalItems / pageSize). Caller computes this. */\n totalPages: number;\n /** Current page, 1-indexed. Caller clamps to [1, totalPages] before passing. */\n currentPage: number;\n /** Pages shown either side of currentPage in the cluster. */\n siblingCount: number;\n /** Pages always shown at the start and end of the cluster. */\n boundaryCount: number;\n}\n\ninterface Interval {\n start: number;\n end: number;\n}\n\nexport function buildPageCluster(args: BuildClusterArgs): PageClusterItem[] {\n const { totalPages, currentPage, siblingCount, boundaryCount } = args;\n\n if (totalPages <= 0) return [];\n if (totalPages === 1) return [{ kind: 'page', page: 1 }];\n\n const sibling: Interval = {\n start: Math.max(1, currentPage - siblingCount),\n end: Math.min(totalPages, currentPage + siblingCount),\n };\n\n const initial: Interval[] = [];\n if (boundaryCount > 0) {\n initial.push({ start: 1, end: Math.min(boundaryCount, totalPages) });\n }\n initial.push(sibling);\n if (boundaryCount > 0) {\n initial.push({\n start: Math.max(1, totalPages - boundaryCount + 1),\n end: totalPages,\n });\n }\n\n const merged = mergeOverlapping(initial);\n const collapsed = collapseSinglePageGapIfOnly(merged);\n\n return emit(collapsed, sibling);\n}\n\n// Merge intervals that overlap or are directly touching (gap < 1).\n// Input intervals are already in non-decreasing-start order by construction\n// (left boundary, sibling, right boundary).\nfunction mergeOverlapping(intervals: Interval[]): Interval[] {\n const sorted = [...intervals].sort((a, b) => a.start - b.start);\n const result: Interval[] = [];\n for (const next of sorted) {\n const last = result[result.length - 1];\n if (last && next.start <= last.end + 1) {\n last.end = Math.max(last.end, next.end);\n } else {\n result.push({ ...next });\n }\n }\n return result;\n}\n\n// Rule 4: a single hidden page is never elided — but only when it is the\n// ONLY remaining gap. With multiple gaps, single-page gaps stay as ellipses\n// so the cluster shape is consistent. See spec §Behaviour for worked\n// examples that pin this interpretation (T=7,C=4 keeps both ellipses;\n// T=6,C=3 inlines its single gap because there is only one).\nfunction collapseSinglePageGapIfOnly(intervals: Interval[]): Interval[] {\n if (intervals.length !== 2) return intervals;\n const [first, second] = intervals as [Interval, Interval];\n const gapSize = second.start - first.end - 1;\n if (gapSize !== 1) return intervals;\n return [{ start: first.start, end: second.end }];\n}\n\nfunction emit(intervals: Interval[], sibling: Interval): PageClusterItem[] {\n const items: PageClusterItem[] = [];\n for (let i = 0; i < intervals.length; i++) {\n const interval = intervals[i]!;\n for (let p = interval.start; p <= interval.end; p++) {\n items.push({ kind: 'page', page: p });\n }\n const next = intervals[i + 1];\n if (next) {\n const gapEnd = next.start - 1;\n const key: string =\n gapEnd < sibling.start ? 'ellipsis-left' : 'ellipsis-right';\n items.push({ kind: 'ellipsis', key });\n }\n }\n return items;\n}\n"],"names":["buildPageCluster","args","totalPages","currentPage","siblingCount","boundaryCount","kind","page","sibling","start","Math","max","end","min","initial","push","merged","mergeOverlapping","collapsed","collapseSinglePageGapIfOnly","emit","intervals","sorted","sort","a","b","result","next","last","length","first","second","gapSize","items","i","interval","p","gapEnd","key"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,wBAAe,EAAE;AAsBV,SAASA,gBAAgBA,CAACC,IAAsB,EAAqB;EAC1E,MAAM;IAAEC,UAAU;IAAEC,WAAW;IAAEC,YAAY;AAAEC,IAAAA;AAAc,GAAC,GAAGJ,IAAI;AAErE,EAAA,IAAIC,UAAU,IAAI,CAAC,EAAE,OAAO,EAAE;AAC9B,EAAA,IAAIA,UAAU,KAAK,CAAC,EAAE,OAAO,CAAC;AAAEI,IAAAA,IAAI,EAAE,MAAM;AAAEC,IAAAA,IAAI,EAAE;AAAE,GAAC,CAAC;AAExD,EAAA,MAAMC,OAAiB,GAAG;IACxBC,KAAK,EAAEC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAER,WAAW,GAAGC,YAAY,CAAC;IAC9CQ,GAAG,EAAEF,IAAI,CAACG,GAAG,CAACX,UAAU,EAAEC,WAAW,GAAGC,YAAY;GACrD;EAED,MAAMU,OAAmB,GAAG,EAAE;EAC9B,IAAIT,aAAa,GAAG,CAAC,EAAE;IACrBS,OAAO,CAACC,IAAI,CAAC;AAAEN,MAAAA,KAAK,EAAE,CAAC;AAAEG,MAAAA,GAAG,EAAEF,IAAI,CAACG,GAAG,CAACR,aAAa,EAAEH,UAAU;AAAE,KAAC,CAAC;AACtE,EAAA;AACAY,EAAAA,OAAO,CAACC,IAAI,CAACP,OAAO,CAAC;EACrB,IAAIH,aAAa,GAAG,CAAC,EAAE;IACrBS,OAAO,CAACC,IAAI,CAAC;AACXN,MAAAA,KAAK,EAAEC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAET,UAAU,GAAGG,aAAa,GAAG,CAAC,CAAC;AAClDO,MAAAA,GAAG,EAAEV;AACP,KAAC,CAAC;AACJ,EAAA;AAEA,EAAA,MAAMc,MAAM,GAAGC,gBAAgB,CAACH,OAAO,CAAC;AACxC,EAAA,MAAMI,SAAS,GAAGC,2BAA2B,CAACH,MAAM,CAAC;AAErD,EAAA,OAAOI,IAAI,CAACF,SAAS,EAAEV,OAAO,CAAC;AACjC;;AAEA;AACA;AACA;AACA,SAASS,gBAAgBA,CAACI,SAAqB,EAAc;EAC3D,MAAMC,MAAM,GAAG,CAAC,GAAGD,SAAS,CAAC,CAACE,IAAI,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAKD,CAAC,CAACf,KAAK,GAAGgB,CAAC,CAAChB,KAAK,CAAC;EAC/D,MAAMiB,MAAkB,GAAG,EAAE;AAC7B,EAAA,KAAK,MAAMC,IAAI,IAAIL,MAAM,EAAE;IACzB,MAAMM,IAAI,GAAGF,MAAM,CAACA,MAAM,CAACG,MAAM,GAAG,CAAC,CAAC;IACtC,IAAID,IAAI,IAAID,IAAI,CAAClB,KAAK,IAAImB,IAAI,CAAChB,GAAG,GAAG,CAAC,EAAE;AACtCgB,MAAAA,IAAI,CAAChB,GAAG,GAAGF,IAAI,CAACC,GAAG,CAACiB,IAAI,CAAChB,GAAG,EAAEe,IAAI,CAACf,GAAG,CAAC;AACzC,IAAA,CAAC,MAAM;MACLc,MAAM,CAACX,IAAI,CAAC;QAAE,GAAGY;AAAK,OAAC,CAAC;AAC1B,IAAA;AACF,EAAA;AACA,EAAA,OAAOD,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASP,2BAA2BA,CAACE,SAAqB,EAAc;AACtE,EAAA,IAAIA,SAAS,CAACQ,MAAM,KAAK,CAAC,EAAE,OAAOR,SAAS;AAC5C,EAAA,MAAM,CAACS,KAAK,EAAEC,MAAM,CAAC,GAAGV,SAAiC;EACzD,MAAMW,OAAO,GAAGD,MAAM,CAACtB,KAAK,GAAGqB,KAAK,CAAClB,GAAG,GAAG,CAAC;AAC5C,EAAA,IAAIoB,OAAO,KAAK,CAAC,EAAE,OAAOX,SAAS;AACnC,EAAA,OAAO,CAAC;IAAEZ,KAAK,EAAEqB,KAAK,CAACrB,KAAK;IAAEG,GAAG,EAAEmB,MAAM,CAACnB;AAAI,GAAC,CAAC;AAClD;AAEA,SAASQ,IAAIA,CAACC,SAAqB,EAAEb,OAAiB,EAAqB;EACzE,MAAMyB,KAAwB,GAAG,EAAE;AACnC,EAAA,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGb,SAAS,CAACQ,MAAM,EAAEK,CAAC,EAAE,EAAE;AACzC,IAAA,MAAMC,QAAQ,GAAGd,SAAS,CAACa,CAAC,CAAE;AAC9B,IAAA,KAAK,IAAIE,CAAC,GAAGD,QAAQ,CAAC1B,KAAK,EAAE2B,CAAC,IAAID,QAAQ,CAACvB,GAAG,EAAEwB,CAAC,EAAE,EAAE;MACnDH,KAAK,CAAClB,IAAI,CAAC;AAAET,QAAAA,IAAI,EAAE,MAAM;AAAEC,QAAAA,IAAI,EAAE6B;AAAE,OAAC,CAAC;AACvC,IAAA;AACA,IAAA,MAAMT,IAAI,GAAGN,SAAS,CAACa,CAAC,GAAG,CAAC,CAAC;AAC7B,IAAA,IAAIP,IAAI,EAAE;AACR,MAAA,MAAMU,MAAM,GAAGV,IAAI,CAAClB,KAAK,GAAG,CAAC;MAC7B,MAAM6B,GAAW,GACfD,MAAM,GAAG7B,OAAO,CAACC,KAAK,GAAG,eAAe,GAAG,gBAAgB;MAC7DwB,KAAK,CAAClB,IAAI,CAAC;AAAET,QAAAA,IAAI,EAAE,UAAU;AAAEgC,QAAAA;AAAI,OAAC,CAAC;AACvC,IAAA;AACF,EAAA;AACA,EAAA,OAAOL,KAAK;AACd;;;;"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/* src/data/pagination.css */
|
|
2
|
+
/* ============================================================================
|
|
3
|
+
Pagination Component Styles
|
|
4
|
+
|
|
5
|
+
Spec: docs/specs/pagination.md
|
|
6
|
+
Per docs/component-tokens.md, Pagination routes semantic-direct (no
|
|
7
|
+
layer-3 component-token matrix). Every value below is a semantic token.
|
|
8
|
+
============================================================================ */
|
|
9
|
+
|
|
10
|
+
.pagination_e7638bd11 {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
flex-wrap: wrap;
|
|
15
|
+
gap: var(--spacing-gap-md);
|
|
16
|
+
font-size: var(--font-size-md);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* On `numbered`, there is no summary; the cluster sits alone (centred is fine
|
|
20
|
+
but the consumer gets to position it via the parent layout). */
|
|
21
|
+
.pagination_e7638bd11[data-variant="numbered"] {
|
|
22
|
+
justify-content: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* On `compact`, the indicator + prev/next form a tight row. */
|
|
26
|
+
.pagination_e7638bd11[data-variant="compact"] {
|
|
27
|
+
justify-content: center;
|
|
28
|
+
gap: var(--spacing-gap-xs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ============================================================================
|
|
32
|
+
Summary
|
|
33
|
+
============================================================================ */
|
|
34
|
+
|
|
35
|
+
.summary_e7638bd11 {
|
|
36
|
+
color: var(--color-text-secondary);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* ============================================================================
|
|
40
|
+
Controls (prev + cluster + next group)
|
|
41
|
+
|
|
42
|
+
The controls wrapper exists so prev/next sit OUTSIDE the cluster <ol>.
|
|
43
|
+
That keeps the <ol> semantically pure (only pages) and — load-bearingly —
|
|
44
|
+
isolates the cluster's reserved min-width from prev/next, so prev/next
|
|
45
|
+
anchor at fixed positions while the cluster's interior reflows.
|
|
46
|
+
============================================================================ */
|
|
47
|
+
|
|
48
|
+
.controls_e7638bd11 {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
gap: var(--spacing-gap-xs);
|
|
53
|
+
row-gap: var(--spacing-gap-md);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ============================================================================
|
|
57
|
+
Page Cluster
|
|
58
|
+
============================================================================ */
|
|
59
|
+
|
|
60
|
+
.cluster_e7638bd11 {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
flex-wrap: nowrap;
|
|
64
|
+
gap: var(--spacing-gap-xs);
|
|
65
|
+
margin: 0;
|
|
66
|
+
padding: 0;
|
|
67
|
+
list-style: none;
|
|
68
|
+
|
|
69
|
+
/* Reserve a stable footprint based on the maximum cluster width the
|
|
70
|
+
current sibling/boundary settings can produce, so prev/next don't
|
|
71
|
+
shift horizontally as the user clicks through pages. The actual
|
|
72
|
+
items centre within the reserved space when the cluster is shorter
|
|
73
|
+
than its maximum.
|
|
74
|
+
|
|
75
|
+
Formula: N items * sm-button-square + (N - 1) * gap, where N is
|
|
76
|
+
supplied via the `--pagination-max-items` runtime style prop on
|
|
77
|
+
the <ol>. Falls back to a reasonable default if the prop is missing
|
|
78
|
+
so the cluster never collapses. */
|
|
79
|
+
min-width: calc(
|
|
80
|
+
var(--pagination-max-items, 9) * var(--button-height-sm) +
|
|
81
|
+
(var(--pagination-max-items, 9) - 1) * var(--spacing-gap-xs)
|
|
82
|
+
);
|
|
83
|
+
justify-content: center;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.item_e7638bd11 {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Uniform width across every numeric page button so 1-digit and 3-digit
|
|
93
|
+
pages render at the same minimum size. Buttons grow uniformly past the
|
|
94
|
+
minimum once content (e.g. "100") demands it; ellipses match. The
|
|
95
|
+
target selectors hit only the cluster's numbered controls — prev/next
|
|
96
|
+
keep their text-driven width. */
|
|
97
|
+
.cluster_e7638bd11 .item_e7638bd11 [data-test-pagination-page] {
|
|
98
|
+
min-width: var(--button-height-sm);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Current-page fill on the wrapped Button. The fill is decorative — the
|
|
102
|
+
load-bearing affordances are aria-current, focus ring, and text contrast.
|
|
103
|
+
See spec §Contrast and §Token gaps for follow-up. */
|
|
104
|
+
.cluster_e7638bd11 .item_e7638bd11 [data-current="true"] {
|
|
105
|
+
background: var(--color-bg-fill-interactive-selected);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ============================================================================
|
|
109
|
+
Ellipsis
|
|
110
|
+
============================================================================ */
|
|
111
|
+
|
|
112
|
+
.ellipsis_e7638bd11 {
|
|
113
|
+
display: inline-flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
min-width: var(--button-height-sm);
|
|
117
|
+
height: var(--button-height-sm);
|
|
118
|
+
color: var(--color-text-tertiary);
|
|
119
|
+
user-select: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* ============================================================================
|
|
123
|
+
Custom :prev / :next block alignment
|
|
124
|
+
|
|
125
|
+
When a consumer overrides <:prev> or <:next>, their icon + text content
|
|
126
|
+
lands in Button's default slot (`.button-label`). That slot is a span
|
|
127
|
+
with `flex: 1 1 auto; text-box: cap alphabetic` — sized for text, not
|
|
128
|
+
for inline icon+text composition. Targeting it inside Pagination's
|
|
129
|
+
prev/next Buttons makes it a flex container so the Icon and text
|
|
130
|
+
centre against each other regardless of intrinsic SVG height.
|
|
131
|
+
|
|
132
|
+
For the default (non-overridden) path the icon goes through `<:prefix>`
|
|
133
|
+
(which is already `display: flex; align-items: center`) and `.button-label`
|
|
134
|
+
contains only the text — so this rule is a no-op there.
|
|
135
|
+
============================================================================ */
|
|
136
|
+
|
|
137
|
+
[data-test-pagination-prev] .button-label_e7638bd11,
|
|
138
|
+
[data-test-pagination-next] .button-label_e7638bd11 {
|
|
139
|
+
display: inline-flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: var(--spacing-gap-xs);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ============================================================================
|
|
145
|
+
Compact indicator
|
|
146
|
+
|
|
147
|
+
Height pinned to button-height-sm so the indicator matches the prev/next
|
|
148
|
+
button height exactly — otherwise the row's tallest child sets the line
|
|
149
|
+
height and the buttons appear top-anchored.
|
|
150
|
+
============================================================================ */
|
|
151
|
+
|
|
152
|
+
.indicator_e7638bd11 {
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
justify-content: center;
|
|
156
|
+
height: var(--button-height-sm);
|
|
157
|
+
padding: 0 var(--spacing-gap-xs);
|
|
158
|
+
color: var(--color-text-secondary);
|
|
159
|
+
white-space: nowrap;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ============================================================================
|
|
163
|
+
Jump-to-page form
|
|
164
|
+
|
|
165
|
+
The form is a horizontal flex row of [label] [input] [submit]. It sits to
|
|
166
|
+
the right of the navigation cluster with the wider --spacing-gap-md gap
|
|
167
|
+
(vs the --spacing-gap-xs that separates prev/next/first/last) so the form
|
|
168
|
+
reads as a distinct chunk, not a peer of the cluster controls.
|
|
169
|
+
|
|
170
|
+
NumberField wraps Label + input in a Control whose default orientation is
|
|
171
|
+
column. We override locally so the label sits inline with the input. The
|
|
172
|
+
selectors are scoped under the data-test attribute so we never bleed into
|
|
173
|
+
any other Form/NumberField on the page.
|
|
174
|
+
============================================================================ */
|
|
175
|
+
|
|
176
|
+
.controls_e7638bd11 > .jump-to_e7638bd11 {
|
|
177
|
+
display: flex;
|
|
178
|
+
flex-direction: row;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: var(--spacing-gap-xs);
|
|
181
|
+
margin-left: var(--spacing-gap-md);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Control wraps Label + input-wrapper. Default orientation is column (label
|
|
185
|
+
above input). Force row-flex so the label sits inline with the input, and
|
|
186
|
+
shrink-to-content via width: auto so it doesn't claim the parent's width. */
|
|
187
|
+
[data-test-pagination-jump-form] [data-test-form-control] {
|
|
188
|
+
flex-direction: row;
|
|
189
|
+
align-items: center;
|
|
190
|
+
gap: var(--spacing-gap-xs);
|
|
191
|
+
width: auto;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* The default Label has padding-top tuned for column layout; reset for the
|
|
195
|
+
inline row so the label sits centred against the input. white-space: nowrap
|
|
196
|
+
prevents "Go to page" from wrapping when the form is squeezed.
|
|
197
|
+
|
|
198
|
+
We target [data-test-form-label] / [data-test-number-field-wrapper] rather
|
|
199
|
+
than the .label / .number-field-input-wrapper class names, because this CSS
|
|
200
|
+
is processed through ember-scoped-css — class selectors get a hash suffix
|
|
201
|
+
tied to Pagination's scope, so they don't match elements rendered by
|
|
202
|
+
external components (Label, NumberField). The data-test attributes are
|
|
203
|
+
untouched by scoping and reach the actual elements. */
|
|
204
|
+
[data-test-pagination-jump-form] [data-test-form-label] {
|
|
205
|
+
flex-shrink: 0;
|
|
206
|
+
padding-top: 0;
|
|
207
|
+
white-space: nowrap;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* The number-field input wrapper is content-fluid by default; constrain it
|
|
211
|
+
to a fixed compact width so the form reads as a "type a small number"
|
|
212
|
+
affordance, not a "fill the page" input. */
|
|
213
|
+
[data-test-pagination-jump-form] [data-test-number-field-wrapper] {
|
|
214
|
+
width: 80px;
|
|
215
|
+
height: var(--button-height-sm);
|
|
216
|
+
}
|