@adia-ai/web-components 0.8.45 → 0.8.47

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 (49) hide show
  1. package/CHANGELOG.md +18 -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/nav/nav.css +7 -3
  13. package/components/nav-group/nav-group.css +38 -41
  14. package/components/nav-item/nav-item.css +11 -5
  15. package/components/preview/preview.class.js +12 -2
  16. package/components/table/table.a2ui.json +19 -4
  17. package/components/table/table.class.js +163 -12
  18. package/components/table/table.d.ts +9 -3
  19. package/components/table/table.yaml +109 -8
  20. package/components/table-footer/table-footer.a2ui.json +150 -0
  21. package/components/table-footer/table-footer.class.js +391 -0
  22. package/components/table-footer/table-footer.css +64 -0
  23. package/components/table-footer/table-footer.d.ts +39 -0
  24. package/components/table-footer/table-footer.examples.md +46 -0
  25. package/components/table-footer/table-footer.js +17 -0
  26. package/components/table-footer/table-footer.yaml +219 -0
  27. package/components/table-toolbar/table-toolbar.a2ui.json +10 -0
  28. package/components/table-toolbar/table-toolbar.class.js +70 -7
  29. package/components/table-toolbar/table-toolbar.css +100 -3
  30. package/components/table-toolbar/table-toolbar.d.ts +2 -0
  31. package/components/table-toolbar/table-toolbar.examples.md +41 -41
  32. package/components/table-toolbar/table-toolbar.yaml +31 -6
  33. package/core/anchor.js +21 -1
  34. package/custom-elements.json +145 -4
  35. package/dist/host.min.css +1 -1
  36. package/dist/host.sheet.js +1 -1
  37. package/dist/theme-provider.min.js +2 -2
  38. package/dist/web-components.min.css +1 -1
  39. package/dist/web-components.min.js +87 -87
  40. package/dist/web-components.sheet.js +1 -1
  41. package/package.json +1 -1
  42. package/patterns/chart-in-card/chart-in-card.examples.html +36 -9
  43. package/patterns/new-enrollments/new-enrollments.examples.html +140 -0
  44. package/patterns/new-enrollments/new-enrollments.html +54 -0
  45. package/patterns/table-in-card/table-in-card.examples.html +168 -0
  46. package/patterns/table-in-card/table-in-card.examples.js +139 -0
  47. package/patterns/table-in-card/table-in-card.html +86 -0
  48. package/styles/colors/material-static.css +276 -234
  49. package/styles/components.css +1 -0
@@ -40,6 +40,20 @@ props:
40
40
  type: string
41
41
  default: ""
42
42
  reflect: true
43
+ size:
44
+ description: >-
45
+ Toolbar scale, via sizing.css's universal [size="sm|md|lg"] ambient
46
+ tokens. Not table-toolbar-specific chrome — the same attribute that
47
+ scales any component. Set it once on the toolbar instead of on each
48
+ slotted scope/actions-leading/actions control individually; anything
49
+ slotted with no [size] of its own inherits the ambient value.
50
+ Default (empty) = md.
51
+ type: string
52
+ default: ""
53
+ enum:
54
+ - sm
55
+ - md
56
+ - lg
43
57
  text:
44
58
  description: Title text shown on the left.
45
59
  type: string
@@ -293,18 +307,29 @@ a2ui:
293
307
  - >-
294
308
  Pair <table-toolbar-ui> with <table-ui> via [for="<table-id>"]
295
309
  (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.
310
+ same parent). One toolbar per table. The doubled-chrome hazard is
311
+ a TITLE COLLISION, not a blanket card-header ban (gh#1807, SPEC
312
+ REQ-C-004): a toolbar carrying [text] must not share a card with
313
+ a heading-slotted <card-ui> <header> — that pairing doubles the
314
+ title row. A title-less toolbar (no [text]) inside a card whose
315
+ <header> owns the title is the canonical table-in-card
316
+ composition (see the table-in-card pattern) and is NOT doubled
317
+ chrome — the toolbar renders only its count/search/filter/sort/
318
+ columns cluster, with the title living in the card header alone.
299
319
  - >-
300
320
  All four affordances (search, filter, sort, columns) default ON.
301
321
  Opt out individually via [no-search] / [no-filter] / [no-sort]
302
322
  / [no-columns]. The previous [searchable] / [filterable]
303
323
  attributes are deprecated — do NOT emit them.
304
324
  - >-
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).
325
+ Three legitimate placements: ABOVE the <card-ui> containing the
326
+ table-ui; standing alone outside a card-ui parent with
327
+ [variant="card"] (wraps the toolbar in card-style chrome); or
328
+ INSIDE the card, in its own plain (non-bleed) <section> between
329
+ the card header and the bleed table section — the canonical
330
+ table-in-card shape (gh#1807) — title-less (no [text]) since the
331
+ card header already owns the title (see the title-collision rule
332
+ above).
308
333
  - >-
309
334
  Use slot="action" (or [slot="actions"]) for trailing primary
310
335
  buttons (Invite, Export, +New). Use [text] / [count] props for
package/core/anchor.js CHANGED
@@ -122,7 +122,27 @@ function anchorNative(anchor, popover, { placement, gap, matchWidth }) {
122
122
  popover.style.left = '';
123
123
 
124
124
  return () => {
125
- anchor.style.anchorName = prevAnchorName;
125
+ // Ownership-guarded restore (bug, found live): a trigger element that
126
+ // is ITSELF a popover's own `[slot="trigger"]` can become the anchor
127
+ // for a SECOND, nested popover opened from inside the first (menu-ui's
128
+ // dropdown item dispatching `action`, which table-toolbar-ui's overflow
129
+ // "More" trigger handles by re-anchoring a filter/sort/columns panel to
130
+ // that SAME element). Anchor-name assignment here is a blind overwrite,
131
+ // never a set/append — so whichever `anchorPopover()` call's cleanup
132
+ // runs LAST always wins, even when it's cleaning up a DIFFERENT,
133
+ // already-superseded anchor-name than the one currently on the element.
134
+ // menu-ui's own close (`render()`'s reactive `#hide()`, scheduled async
135
+ // — reordering the synchronous call site doesn't change when this
136
+ // runs) fires after the nested popover's `anchorPopover()` has already
137
+ // claimed the trigger, and unconditionally restoring `prevAnchorName`
138
+ // clobbered that fresh claim back to empty — the nested popover's own
139
+ // `position-anchor` was left pointing at a name resolving to nothing,
140
+ // rendering unpositioned at the viewport origin instead of near its
141
+ // trigger. Only restore when this cleanup's OWN name is still the
142
+ // current value — i.e., nobody has claimed the anchor since; otherwise
143
+ // leave it alone, since undoing someone else's still-active claim is
144
+ // never correct regardless of call order.
145
+ if (anchor.style.anchorName === name) anchor.style.anchorName = prevAnchorName;
126
146
  for (const [k, v] of Object.entries(prevStyles)) popover.style[k] = v;
127
147
  };
128
148
  }
@@ -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",
@@ -9933,6 +10044,13 @@
9933
10044
  },
9934
10045
  "description": "id-ref of the table-ui to control. Falls back to the first sibling table-ui within the same parent when omitted."
9935
10046
  },
10047
+ {
10048
+ "name": "size",
10049
+ "type": {
10050
+ "text": "string"
10051
+ },
10052
+ "description": "Toolbar scale, via sizing.css's universal [size=\"sm|md|lg\"] ambient tokens. Not table-toolbar-specific chrome — the same attribute that scales any component. Set it once on the toolbar instead of on each slotted scope/actions-leading/actions control individually; anything slotted with no [size] of its own inherits the ambient value. Default (empty) = md."
10053
+ },
9936
10054
  {
9937
10055
  "name": "text",
9938
10056
  "type": {
@@ -10206,7 +10324,30 @@
10206
10324
  "text": "number"
10207
10325
  },
10208
10326
  "default": "0",
10209
- "description": "Rows per page. 0 = show all rows without pagination. When > 0, renders a pagination bar"
10327
+ "description": "Rows per page. 0 = show all rows without pagination. When > 0, renders the internal"
10328
+ },
10329
+ {
10330
+ "name": "no-pager",
10331
+ "type": {
10332
+ "text": "boolean"
10333
+ },
10334
+ "default": "false",
10335
+ "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."
10336
+ },
10337
+ {
10338
+ "name": "range-total",
10339
+ "type": {
10340
+ "text": "number"
10341
+ },
10342
+ "default": "0",
10343
+ "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."
10344
+ },
10345
+ {
10346
+ "name": "filteredCount",
10347
+ "type": {
10348
+ "text": "number"
10349
+ },
10350
+ "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
10351
  },
10211
10352
  {
10212
10353
  "name": "raw",
@@ -10221,7 +10362,7 @@
10221
10362
  "type": {
10222
10363
  "text": "string"
10223
10364
  },
10224
- "description": "Global search/filter string. Filters visible rows across all columns using"
10365
+ "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
10366
  },
10226
10367
  {
10227
10368
  "name": "selectable",
@@ -10307,7 +10448,7 @@
10307
10448
  "type": {
10308
10449
  "text": "CustomEvent"
10309
10450
  },
10310
- "description": "Fired when the user navigates to a different page."
10451
+ "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
10452
  },
10312
10453
  {
10313
10454
  "name": "resize",