@adia-ai/web-components 0.8.45 → 0.8.46

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/components/card/card.css +12 -23
  3. package/components/card/card.yaml +11 -0
  4. package/components/chart/chart.a2ui.json +16 -1
  5. package/components/chart/chart.class.js +611 -41
  6. package/components/chart/chart.css +174 -0
  7. package/components/chart/chart.d.ts +5 -1
  8. package/components/chart/chart.yaml +45 -1
  9. package/components/field/field.css +24 -2
  10. package/components/index.js +1 -0
  11. package/components/input/input.css +7 -0
  12. package/components/table/table.a2ui.json +19 -4
  13. package/components/table/table.class.js +163 -12
  14. package/components/table/table.d.ts +9 -3
  15. package/components/table/table.yaml +109 -8
  16. package/components/table-footer/table-footer.a2ui.json +150 -0
  17. package/components/table-footer/table-footer.class.js +391 -0
  18. package/components/table-footer/table-footer.css +64 -0
  19. package/components/table-footer/table-footer.d.ts +39 -0
  20. package/components/table-footer/table-footer.examples.md +46 -0
  21. package/components/table-footer/table-footer.js +17 -0
  22. package/components/table-footer/table-footer.yaml +219 -0
  23. package/components/table-toolbar/table-toolbar.yaml +17 -6
  24. package/custom-elements.json +138 -4
  25. package/dist/theme-provider.min.js +1 -1
  26. package/dist/web-components.min.css +1 -1
  27. package/dist/web-components.min.js +87 -87
  28. package/dist/web-components.sheet.js +1 -1
  29. package/package.json +1 -1
  30. package/patterns/chart-in-card/chart-in-card.examples.html +36 -9
  31. package/patterns/new-enrollments/new-enrollments.examples.html +140 -0
  32. package/patterns/new-enrollments/new-enrollments.html +54 -0
  33. package/patterns/table-in-card/table-in-card.examples.html +168 -0
  34. package/patterns/table-in-card/table-in-card.examples.js +139 -0
  35. package/patterns/table-in-card/table-in-card.html +86 -0
  36. package/styles/components.css +1 -0
@@ -0,0 +1,219 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: UITableFooter
3
+ tag: table-footer-ui
4
+ status: beta
5
+ component: TableFooter
6
+ category: agent
7
+ version: 1
8
+ description: >-
9
+ Footer / companion bar for a sibling table-ui (gh#1807, ADR-0080) — a
10
+ "Showing X–Y of N" range label paired with a composed pagination-ui.
11
+ Derivation-model API: set `page` / `page-size` / `range-total` and the
12
+ footer derives total pages and the range window itself, so it never
13
+ drifts out of sync with its own pager. Wired to the target table via a
14
+ [for] id-ref and CustomEvents only (ADR-0079/ADR-0080) — the footer never
15
+ writes a property or calls a method on the bound table. Drop below (or
16
+ inside a card-ui footer alongside) any table-ui to add the standard
17
+ data-grid pager + range summary without re-implementing pagination-ui's
18
+ ellipsis truncation, keyboard handling, or ARIA.
19
+ # Per ADR-0027 — primitives that programmatically create other primitives
20
+ # do NOT auto-import them. Consumer (or demo shell) must explicitly import.
21
+ composes:
22
+ - pagination-ui
23
+ props:
24
+ for:
25
+ description: id-ref of the table-ui to control. Falls back to the first sibling table-ui within the same parent when omitted.
26
+ type: string
27
+ default: ""
28
+ reflect: true
29
+ page:
30
+ description: >-
31
+ Current page, 1-based. Explicit attributes always win over derivation
32
+ (per-attribute, not all-or-nothing) — see page-size/range-total.
33
+ type: number
34
+ default: 1
35
+ reflect: true
36
+ page-size:
37
+ description: >-
38
+ Rows per page. Default 0 (unknown) is the ORDINARY default state, not
39
+ an exotic edge case: with page-size absent/0 and range-total present,
40
+ the range label renders in count-only form ("128 items") and the
41
+ pager does not render — the derivation math (pages = range-total /
42
+ page-size) is undefined without a page size. In a client-mode
43
+ composition (the resolved target has [paginate] > 0) and this
44
+ attribute is unset, the footer derives page-size from the target's
45
+ own [paginate] value.
46
+ type: number
47
+ default: 0
48
+ reflect: true
49
+ range-total:
50
+ description: >-
51
+ Total ROWS across all pages. Absent (the default) means "not yet
52
+ known" (loading) — never "zero"; both the range label and the pager
53
+ stay hidden until this is set. Explicit range-total="0" is the
54
+ confirmed-empty state (pager hidden; the `empty` slot renders in the
55
+ range label's position when supplied). Named identically to
56
+ table-toolbar-ui's [range-total] and table-ui's own [range-total]
57
+ (gh#1754, ADR-0082) and deliberately NOT `total` (pagination-ui's
58
+ `total` is total PAGES — the ADR-0063 B5 collision rule). When this
59
+ attribute is unset, the footer derives it from the resolved target
60
+ via a two-branch ladder (REQ-W-006, amended 0.2.0/ADR-0082):
61
+ (1) the target's own [range-total] attribute, when present — the
62
+ table-authoritative SERVER mode (gh#1754 Shape 1's second lawful
63
+ composition); the identity rule: the footer's range-total derives
64
+ from the table's range-total, never the table's filteredCount, and
65
+ this branch wins even when the target's [paginate] is also > 0;
66
+ (2) otherwise, when the target has [paginate] > 0, CLIENT mode: the
67
+ target's filtered row count (search + column filters applied, before
68
+ pagination). Refreshes on the target's `page` / `filter-change` /
69
+ `sort` events in either branch. Widened named staleness edge: a
70
+ programmatic `.data` swap on the target — or a `range-total` update
71
+ on the target without an accompanying page move — emits no event;
72
+ either re-set this footer's own attributes after such a change, or
73
+ accept staleness until the target's next event.
74
+ type: number
75
+ default: 0
76
+ reflect: true
77
+ siblings:
78
+ description: Passed through to the composed pagination-ui's own [siblings] (page-number window size on each side of the current page).
79
+ type: number
80
+ default: 1
81
+ reflect: true
82
+ size:
83
+ description: Passed through to the composed pagination-ui's own [size] (sm | md | lg, the universal size system).
84
+ type: string
85
+ default: md
86
+ reflect: true
87
+ events:
88
+ footer-page:
89
+ description: >-
90
+ gh#1807, ADR-0080 (events-only interaction contract, extending
91
+ ADR-0079's sender-prefixed toolbar-* precedent to a second sender) —
92
+ dispatched directly at the resolved [for] target on every pager
93
+ interaction. Detail: { page } (1-based). table-ui listens for this on
94
+ itself, applied only when [paginate] > 0 (clamped to the valid page
95
+ range; a non-numeric/NaN detail.page is a no-op); at [paginate="0"]
96
+ the command is a documented no-op on the table. Additive: any
97
+ consumer, not only table-footer-ui, may dispatch this at a table-ui.
98
+ page-change:
99
+ description: >-
100
+ The footer's own consumer-facing notification — a distinct name from
101
+ the inbound footer-page command (ADR-0079's command≠notification
102
+ separation). Fires on every pager interaction, in both client- and
103
+ server-mode. Detail: { page } (1-based). This is the server-mode
104
+ fetch hook: a consumer listens for it, fetches the new page, then
105
+ sets `table.data` and the footer's own `page` (and `range-total` if
106
+ the server's total moved) — the footer never optimistically advances
107
+ its own state in server-mode.
108
+ slots:
109
+ empty:
110
+ description: >-
111
+ Renders in place of the range label — the same [data-range] region —
112
+ when [range-total] is EXPLICITLY present and equal to "0" (checked
113
+ via hasAttribute, never the computed value alone, so an unset
114
+ range-total during loading never flashes this content). Mutually
115
+ exclusive with a positive [range-total]: the normal "Showing X–Y of N"
116
+ (or count-only) text always wins whenever range-total resolves
117
+ positive. Plain author-supplied markup — table-footer-ui does not
118
+ template or constrain its shape. Naming matches table-toolbar-ui's own
119
+ `empty` slot convention.
120
+ states:
121
+ - name: idle
122
+ description: Default, ready for interaction.
123
+ traits: []
124
+ tokens:
125
+ --table-footer-gap:
126
+ description: Gap between the range label and the pager
127
+ --table-footer-py:
128
+ description: Vertical padding
129
+ --table-footer-px:
130
+ description: Horizontal padding
131
+ --table-footer-summary-fg:
132
+ description: Range-label text color
133
+ --table-footer-summary-size:
134
+ description: Range-label font size
135
+ a2ui:
136
+ rules:
137
+ - >-
138
+ Pair <table-footer-ui> with <table-ui> via [for="<table-id>"] (or
139
+ rely on first-sibling fallback when both are inside the same parent).
140
+ Client-mode composition (a [paginate]-sliced table): set [no-pager]
141
+ on the table-ui so its own internal pagination bar doesn't render
142
+ alongside the footer's composed one — table-footer-ui never
143
+ auto-suppresses it for you.
144
+ - >-
145
+ Server-mode composition (gh#1754 Shape 1 — the server returns the
146
+ page's rows plus the total row count): set [range-total] to the
147
+ server's total, [page] / [page-size] from your own request state, and
148
+ listen for the footer's `page-change` event to fetch the next page —
149
+ the footer does not optimistically advance its own [page] in
150
+ server-mode; write it back after the fetch resolves.
151
+ - >-
152
+ The toolbar's [count] badge and this footer's range label coexist by
153
+ design — they answer different questions (the collection summary vs.
154
+ the page-window position) and neither replaces the other. In a
155
+ composition that includes a table-footer-ui, leave the toolbar's
156
+ [range-start]/[range-end]/[range-total] unset — the range summary's
157
+ home is the footer.
158
+ - >-
159
+ Canonical composition: card-ui header (title + primary action) →
160
+ a plain <section> wrapping a title-less <table-toolbar-ui for> → a
161
+ <section bleed> wrapping the <table-ui id> → a <footer divider>
162
+ wrapping this <table-footer-ui for> — see the table-in-card pattern.
163
+ - >-
164
+ Use <span slot="empty"> (or any markup) inside a table-footer-ui that
165
+ also sets [range-total="0"] to show a "no results" message in the
166
+ range label's own position, without a layout shift versus the normal
167
+ range text. Only fires on an EXPLICIT range-total="0" — omitting
168
+ range-total entirely (loading) never shows it.
169
+ anti_patterns:
170
+ - wrong: '<table-footer-ui range-start="1" range-end="10" range-total="128"></table-footer-ui>'
171
+ why: >-
172
+ table-footer-ui is derivation-only (REQ-F-002) — there is no
173
+ [range-start]/[range-end] attribute pair (that shape belongs to
174
+ table-toolbar-ui, which has no page/page-size knowledge of its own).
175
+ Setting [page] + [page-size] + [range-total] derives the range window
176
+ instead; a permanent three-way drift hazard is exactly what the
177
+ derivation model exists to avoid.
178
+ fix: '<table-footer-ui page="1" page-size="10" range-total="128"></table-footer-ui>'
179
+ examples:
180
+ - name: table-footer-server-mode
181
+ description: Server-mode footer (gh#1754 Shape 1) paired with a for=-bound table-ui.
182
+ a2ui: >-
183
+ [
184
+ {"id": "root", "component": "Column", "gap": "0", "children": ["tests", "ftr"]},
185
+ {"id": "tests", "component": "Table", "raw": true},
186
+ {"id": "ftr", "component": "TableFooter", "for": "tests", "page": 1, "page-size": 10, "range-total": 128}
187
+ ]
188
+ - name: table-footer-client-mode
189
+ description: Client-mode footer bound to a [paginate]d table-ui with its own internal pager suppressed via [no-pager].
190
+ a2ui: >-
191
+ [
192
+ {"id": "root", "component": "Column", "gap": "0", "children": ["tests-c", "ftr"]},
193
+ {"id": "tests-c", "component": "Table", "paginate": 10, "no-pager": true, "raw": true},
194
+ {"id": "ftr", "component": "TableFooter", "for": "tests-c"}
195
+ ]
196
+ keywords:
197
+ - table-footer
198
+ - pagination
199
+ - pager
200
+ - range
201
+ - showing
202
+ - data-grid
203
+ - directory
204
+ - admin
205
+ - backoffice
206
+ - listing
207
+ - records
208
+ synonyms:
209
+ pagination:
210
+ - table-footer
211
+ - pagination
212
+ pager:
213
+ - table-footer
214
+ - pagination
215
+ related:
216
+ - table
217
+ - table-toolbar
218
+ - pagination
219
+ - card
@@ -293,18 +293,29 @@ a2ui:
293
293
  - >-
294
294
  Pair <table-toolbar-ui> with <table-ui> via [for="<table-id>"]
295
295
  (or rely on first-sibling fallback when both are inside the
296
- same parent). One toolbar per table. Do NOT also use
297
- <card-ui>'s <header> on the same card that produces a doubled
298
- chrome row.
296
+ same parent). One toolbar per table. The doubled-chrome hazard is
297
+ a TITLE COLLISION, not a blanket card-header ban (gh#1807, SPEC
298
+ REQ-C-004): a toolbar carrying [text] must not share a card with
299
+ a heading-slotted <card-ui> <header> — that pairing doubles the
300
+ title row. A title-less toolbar (no [text]) inside a card whose
301
+ <header> owns the title is the canonical table-in-card
302
+ composition (see the table-in-card pattern) and is NOT doubled
303
+ chrome — the toolbar renders only its count/search/filter/sort/
304
+ columns cluster, with the title living in the card header alone.
299
305
  - >-
300
306
  All four affordances (search, filter, sort, columns) default ON.
301
307
  Opt out individually via [no-search] / [no-filter] / [no-sort]
302
308
  / [no-columns]. The previous [searchable] / [filterable]
303
309
  attributes are deprecated — do NOT emit them.
304
310
  - >-
305
- Place the toolbar ABOVE the <card-ui> containing the table-ui,
306
- or use [variant="card"] when standing alone outside a card-ui
307
- parent (the variant wraps the toolbar in card-style chrome).
311
+ Three legitimate placements: ABOVE the <card-ui> containing the
312
+ table-ui; standing alone outside a card-ui parent with
313
+ [variant="card"] (wraps the toolbar in card-style chrome); or
314
+ INSIDE the card, in its own plain (non-bleed) <section> between
315
+ the card header and the bleed table section — the canonical
316
+ table-in-card shape (gh#1807) — title-less (no [text]) since the
317
+ card header already owns the title (see the title-collision rule
318
+ above).
308
319
  - >-
309
320
  Use slot="action" (or [slot="actions"]) for trailing primary
310
321
  buttons (Invite, Export, +New). Use [text] / [count] props for
@@ -2091,6 +2091,20 @@
2091
2091
  },
2092
2092
  "description": "Chart 2.0 aspect-ratio bucket (ADR-0074). Unset (default) auto-snaps to the nearest of the three studied ratios by comparing the chart's own live box aspect against two midpoint boundaries (5:4 between 3:2/1:1, 4:5 between 1:1/2:3) — never interpolated; the resolved bucket reflects onto the host as `data-ratio-resolved`. Setting `ratio` explicitly pins that bucket regardless of the box's actual aspect, overriding the auto-snap. Orthogonal to `size` (overall scale) and `type`/`color` (ADR-0064's family axis) — this attribute lands the grammar + snap plumbing only; per-type studied renderings per bucket are later waves (gh#1624 plan steps 2-4)."
2093
2093
  },
2094
+ {
2095
+ "name": "labels",
2096
+ "type": {
2097
+ "text": "string"
2098
+ },
2099
+ "description": "Chart 2.0 axis-label mode (ADR-0081). Unset (default, \"\") always resolves to `outside` — today's pre-2.0 outside-the-plot axis labels, byte-for-byte unchanged. Set explicitly to `chip` to opt into the pill-treated overlay labels inset within the plot box (never hanging outside it) — the resolved value reflects onto the host as `data-labels-resolved=\"chip\"|\"outside\"`. Deliberately explicit-opt-in rather than auto-detected from full-bleed ancestry (`section[bleed]`/ `card-ui[padding=\"none\"]`): an earlier draft auto-activated chip mode on any full-bleed composition, which silently changed the rendering of every ALREADY-SHIPPED full-bleed chart the moment it shipped — this build's own 60-fixture Charts visual-eval floor caught it (2 `comp-chart-in-card-n-*` regressions). `chart-in-card` compositions opt in explicitly once their own examples are updated (gh#1805)."
2100
+ },
2101
+ {
2102
+ "name": "today",
2103
+ "type": {
2104
+ "text": "string"
2105
+ },
2106
+ "description": "Names the datum whose x-axis value marks \"today\" (ADR-0081). Unset (default, \"\") renders no marker — additive. Compared against each datum's x-key value via a loose string comparison (`String(datum[x]) === String(today)`), so category axes (\"month\", \"region\") work the same as literal date strings; chart-ui never auto-derives today from the clock. Renders a baseline dot + a short band tick (REQ-F-008) on cartesian types (bar, line, area, scatter, multi-line, stacked-bar, grouped-bar, composed); a no-op on every radial/part-to-whole type and on sparkline (gh#1690's N/A list). Fallback (not the primary mechanism): a datum already carrying a truthy `today` key is treated as an equivalent marker even with this attribute unset."
2107
+ },
2094
2108
  {
2095
2109
  "name": "smooth",
2096
2110
  "type": {
@@ -2158,7 +2172,7 @@
2158
2172
  "text": "array"
2159
2173
  },
2160
2174
  "default": "[]",
2161
- "description": "JS property (set programmatically — `el.data = [...]`). An array of plain objects; each object's keys are named by the `x` and `y` attributes — e.g. `<chart-ui x=\"month\" y=\"revenue\">` consumes `[{month:'Jan', revenue:3200}, {month:'Feb', revenue:4100}]`. The Chart.js `{labels, datasets}` envelope is NOT chart-ui's API — passing it (or any non-array value) renders an empty chart. May also be supplied declaratively as a JSON-array `data=\"[…]\"` attribute, hydrated once at connect. Custom accessor on the element class, not a reflected attribute."
2175
+ "description": "JS property (set programmatically — `el.data = [...]`). An array of plain objects; each object's keys are named by the `x` and `y` attributes — e.g. `<chart-ui x=\"month\" y=\"revenue\">` consumes `[{month:'Jan', revenue:3200}, {month:'Feb', revenue:4100}]`. The Chart.js `{labels, datasets}` envelope is NOT chart-ui's API — passing it (or any non-array value) renders an empty chart. May also be supplied declaratively as a JSON-array `data=\"[…]\"` attribute, hydrated once at connect. Custom accessor on the element class, not a reflected attribute. A datum object carrying a truthy `provisional` key (ADR-0081) renders that period's type-specific incomplete-period treatment (hollow/dashed bar, dashed line + faded area, hollow ring dot) on cartesian types; a no-op on radial/part-to-whole types and sparkline (gh#1690's N/A list). Provisional state is per-datum data-shape, not a chart-ui attribute — see ADR-0081 §3."
2162
2176
  }
2163
2177
  ],
2164
2178
  "slots": [
@@ -9912,6 +9926,103 @@
9912
9926
  }
9913
9927
  ]
9914
9928
  },
9929
+ {
9930
+ "kind": "javascript-module",
9931
+ "path": "components/table-footer/table-footer.js",
9932
+ "declarations": [
9933
+ {
9934
+ "kind": "class",
9935
+ "description": "Footer / companion bar for a sibling table-ui (gh#1807, ADR-0080) — a \"Showing X–Y of N\" range label paired with a composed pagination-ui. Derivation-model API: set `page` / `page-size` / `range-total` and the footer derives total pages and the range window itself, so it never drifts out of sync with its own pager. Wired to the target table via a [for] id-ref and CustomEvents only (ADR-0079/ADR-0080) — the footer never writes a property or calls a method on the bound table. Drop below (or inside a card-ui footer alongside) any table-ui to add the standard data-grid pager + range summary without re-implementing pagination-ui's ellipsis truncation, keyboard handling, or ARIA.",
9936
+ "name": "UITableFooter",
9937
+ "tagName": "table-footer-ui",
9938
+ "superclass": {
9939
+ "name": "UIElement",
9940
+ "module": "/core/element.js"
9941
+ },
9942
+ "attributes": [
9943
+ {
9944
+ "name": "for",
9945
+ "type": {
9946
+ "text": "string"
9947
+ },
9948
+ "description": "id-ref of the table-ui to control. Falls back to the first sibling table-ui within the same parent when omitted."
9949
+ },
9950
+ {
9951
+ "name": "page",
9952
+ "type": {
9953
+ "text": "number"
9954
+ },
9955
+ "default": "1",
9956
+ "description": "Current page, 1-based. Explicit attributes always win over derivation (per-attribute, not all-or-nothing) — see page-size/range-total."
9957
+ },
9958
+ {
9959
+ "name": "page-size",
9960
+ "type": {
9961
+ "text": "number"
9962
+ },
9963
+ "default": "0",
9964
+ "description": "Rows per page. Default 0 (unknown) is the ORDINARY default state, not an exotic edge case: with page-size absent/0 and range-total present, the range label renders in count-only form (\"128 items\") and the pager does not render — the derivation math (pages = range-total / page-size) is undefined without a page size. In a client-mode composition (the resolved target has [paginate] > 0) and this attribute is unset, the footer derives page-size from the target's own [paginate] value."
9965
+ },
9966
+ {
9967
+ "name": "range-total",
9968
+ "type": {
9969
+ "text": "number"
9970
+ },
9971
+ "default": "0",
9972
+ "description": "Total ROWS across all pages. Absent (the default) means \"not yet known\" (loading) — never \"zero\"; both the range label and the pager stay hidden until this is set. Explicit range-total=\"0\" is the confirmed-empty state (pager hidden; the `empty` slot renders in the range label's position when supplied). Named identically to table-toolbar-ui's [range-total] and table-ui's own [range-total] (gh#1754, ADR-0082) and deliberately NOT `total` (pagination-ui's `total` is total PAGES — the ADR-0063 B5 collision rule). When this attribute is unset, the footer derives it from the resolved target via a two-branch ladder (REQ-W-006, amended 0.2.0/ADR-0082): (1) the target's own [range-total] attribute, when present — the table-authoritative SERVER mode (gh#1754 Shape 1's second lawful composition); the identity rule: the footer's range-total derives from the table's range-total, never the table's filteredCount, and this branch wins even when the target's [paginate] is also > 0; (2) otherwise, when the target has [paginate] > 0, CLIENT mode: the target's filtered row count (search + column filters applied, before pagination). Refreshes on the target's `page` / `filter-change` / `sort` events in either branch. Widened named staleness edge: a programmatic `.data` swap on the target — or a `range-total` update on the target without an accompanying page move — emits no event; either re-set this footer's own attributes after such a change, or accept staleness until the target's next event."
9973
+ },
9974
+ {
9975
+ "name": "siblings",
9976
+ "type": {
9977
+ "text": "number"
9978
+ },
9979
+ "default": "1",
9980
+ "description": "Passed through to the composed pagination-ui's own [siblings] (page-number window size on each side of the current page)."
9981
+ },
9982
+ {
9983
+ "name": "size",
9984
+ "type": {
9985
+ "text": "string"
9986
+ },
9987
+ "default": "md",
9988
+ "description": "Passed through to the composed pagination-ui's own [size] (sm | md | lg, the universal size system)."
9989
+ }
9990
+ ],
9991
+ "slots": [
9992
+ {
9993
+ "name": "empty",
9994
+ "description": "Renders in place of the range label — the same [data-range] region — when [range-total] is EXPLICITLY present and equal to \"0\" (checked via hasAttribute, never the computed value alone, so an unset range-total during loading never flashes this content). Mutually exclusive with a positive [range-total]: the normal \"Showing X–Y of N\" (or count-only) text always wins whenever range-total resolves positive. Plain author-supplied markup — table-footer-ui does not template or constrain its shape. Naming matches table-toolbar-ui's own `empty` slot convention."
9995
+ }
9996
+ ],
9997
+ "events": [
9998
+ {
9999
+ "name": "footer-page",
10000
+ "type": {
10001
+ "text": "CustomEvent"
10002
+ },
10003
+ "description": "gh#1807, ADR-0080 (events-only interaction contract, extending ADR-0079's sender-prefixed toolbar-* precedent to a second sender) — dispatched directly at the resolved [for] target on every pager interaction. Detail: { page } (1-based). table-ui listens for this on itself, applied only when [paginate] > 0 (clamped to the valid page range; a non-numeric/NaN detail.page is a no-op); at [paginate=\"0\"] the command is a documented no-op on the table. Additive: any consumer, not only table-footer-ui, may dispatch this at a table-ui."
10004
+ },
10005
+ {
10006
+ "name": "page-change",
10007
+ "type": {
10008
+ "text": "CustomEvent"
10009
+ },
10010
+ "description": "The footer's own consumer-facing notification — a distinct name from the inbound footer-page command (ADR-0079's command≠notification separation). Fires on every pager interaction, in both client- and server-mode. Detail: { page } (1-based). This is the server-mode fetch hook: a consumer listens for it, fetches the new page, then sets `table.data` and the footer's own `page` (and `range-total` if the server's total moved) — the footer never optimistically advances its own state in server-mode."
10011
+ }
10012
+ ]
10013
+ }
10014
+ ],
10015
+ "exports": [
10016
+ {
10017
+ "kind": "custom-element-definition",
10018
+ "name": "table-footer-ui",
10019
+ "declaration": {
10020
+ "name": "UITableFooter",
10021
+ "module": "./components/table-footer/table-footer.js"
10022
+ }
10023
+ }
10024
+ ]
10025
+ },
9915
10026
  {
9916
10027
  "kind": "javascript-module",
9917
10028
  "path": "components/table-toolbar/table-toolbar.js",
@@ -10206,7 +10317,30 @@
10206
10317
  "text": "number"
10207
10318
  },
10208
10319
  "default": "0",
10209
- "description": "Rows per page. 0 = show all rows without pagination. When > 0, renders a pagination bar"
10320
+ "description": "Rows per page. 0 = show all rows without pagination. When > 0, renders the internal"
10321
+ },
10322
+ {
10323
+ "name": "no-pager",
10324
+ "type": {
10325
+ "text": "boolean"
10326
+ },
10327
+ "default": "false",
10328
+ "description": "Hide the internal pagination bar while leaving [paginate] slicing, page state, the `page` event, and the `footer-page` command listener all intact (gh#1807, ADR-0080). The anti-doubled-pager mechanism for a table-footer-ui composition, client- or server-mode alike (gh#1754, ADR-0082): without it, a bound footer's own composed pager and the table's own internal one would both render for any non-empty paginated table. Explicit author intent — never suppressed automatically just because a footer happens to be bound."
10329
+ },
10330
+ {
10331
+ "name": "range-total",
10332
+ "type": {
10333
+ "text": "number"
10334
+ },
10335
+ "default": "0",
10336
+ "description": "Total row count across ALL server pages (gh#1754, ADR-0082) — the table-authoritative server-mode data contract. Presence-gated: the attribute's PRESENCE, not its value, is the mode switch (checked via hasAttribute, never the coerced value alone — the same discipline table-footer-ui's own [range-total] ships). Absent (the default) means client mode: today's behavior exactly, [paginate] slices `.data` as always. Present with [paginate] > 0 means server mode: no local slicing (`.data` IS the current page and renders whole, after local search/sort/filter), the internal pager's page count becomes `max(1, ceil(range-total / paginate))` (one calc site, consolidated), and the internal page-reset sites (`data` set, `setFilter`, `clearFilters`) are suppressed so a fetch write-back never fights the pager back to page 0 — page state then moves only via pager interaction, the `footer-page` command, or `setState()`. The existing `page` event (0-based, unchanged) becomes the fetch trigger: the consumer listens for it, fetches that server page, and writes `.data` back. Explicit `range-total=\"0\"` is server-confirmed empty (pager hidden), never conflated with absent. With [paginate] absent/0, [range-total] is inert for this table's own rendering but stays readable by a bound table-footer-ui (REQ-W-006 branch 2) for its count-only label — see the footer-authoritative shape below, which remains lawful and is unaffected by this attribute. Named identically to table-toolbar-ui's and table-footer-ui's own [range-total] (rows, never `pagination-ui[total]`'s pages — the ADR-0063 B5 collision rule); a third instance of one name for one concept."
10337
+ },
10338
+ {
10339
+ "name": "filteredCount",
10340
+ "type": {
10341
+ "text": "number"
10342
+ },
10343
+ "description": "Read-only. The row count AFTER search + column filters apply but BEFORE pagination slices it (gh#1807, ADR-0080, REQ-W-006) — a plain JS getter, no setter, never a reflected attribute. table-footer-ui's client-mode range-total derivation reads this directly; a raw `.data.length` read (the toolbar's own `count` fallback precedent) would double-count a filtered-out row. In server mode ([range-total] set, gh#1754, ADR-0082), filteredCount stays page-scoped — the loaded page's rows after local filters, NOT the server total across all pages; [range-total] is the server total, filteredCount never is (REQ-D-006)."
10210
10344
  },
10211
10345
  {
10212
10346
  "name": "raw",
@@ -10221,7 +10355,7 @@
10221
10355
  "type": {
10222
10356
  "text": "string"
10223
10357
  },
10224
- "description": "Global search/filter string. Filters visible rows across all columns using"
10358
+ "description": "Global search/filter string. Filters visible rows across all columns using case-insensitive substring matching. Does NOT reset the current page (verified against source at the gh#1754/ADR-0082 build — unlike setFilter()/clearFilters(), no internal page reset ever ran for a search change; this description previously claimed \"resets to page 1 on change,\" which the source never did). Aligning the doc to observed behavior here, not a behavior change — a page reset on search, if wanted, is a separate client-mode ticket."
10225
10359
  },
10226
10360
  {
10227
10361
  "name": "selectable",
@@ -10307,7 +10441,7 @@
10307
10441
  "type": {
10308
10442
  "text": "CustomEvent"
10309
10443
  },
10310
- "description": "Fired when the user navigates to a different page."
10444
+ "description": "Fired when the table's own internal page state changes — an internal pager click, or a `footer-page` command applied (REQ-W-002). This is a RESYNC notification a bound `<table-footer-ui>` listens to for its own `page` attribute (REQ-W-004) — it is NOT the public server-mode fetch trigger a consumer should listen to; use `<table-footer-ui>`'s own `page-change` event for that (1-based, gh#1754/ADR-0082 REQ-W-003). Listening to both risks a duplicate fetch or an index-base mismatch."
10311
10445
  },
10312
10446
  {
10313
10447
  "name": "resize",