@ponchia/ui 0.6.7 → 0.6.9

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 (111) hide show
  1. package/CHANGELOG.md +129 -4
  2. package/README.md +4 -4
  3. package/annotations/index.d.ts.map +1 -1
  4. package/annotations/index.js +26 -9
  5. package/behaviors/carousel.d.ts.map +1 -1
  6. package/behaviors/carousel.js +145 -49
  7. package/behaviors/combobox.d.ts.map +1 -1
  8. package/behaviors/combobox.js +220 -92
  9. package/behaviors/command.d.ts.map +1 -1
  10. package/behaviors/command.js +74 -14
  11. package/behaviors/connectors.d.ts.map +1 -1
  12. package/behaviors/connectors.js +131 -32
  13. package/behaviors/crosshair.d.ts.map +1 -1
  14. package/behaviors/crosshair.js +47 -1
  15. package/behaviors/dialog.d.ts.map +1 -1
  16. package/behaviors/dialog.js +24 -9
  17. package/behaviors/disclosure.d.ts.map +1 -1
  18. package/behaviors/disclosure.js +33 -3
  19. package/behaviors/dismissible.d.ts.map +1 -1
  20. package/behaviors/dismissible.js +3 -2
  21. package/behaviors/forms.d.ts.map +1 -1
  22. package/behaviors/forms.js +211 -140
  23. package/behaviors/glyph.d.ts.map +1 -1
  24. package/behaviors/glyph.js +172 -132
  25. package/behaviors/inert.d.ts +1 -1
  26. package/behaviors/inert.d.ts.map +1 -1
  27. package/behaviors/inert.js +4 -3
  28. package/behaviors/internal.d.ts.map +1 -1
  29. package/behaviors/internal.js +4 -3
  30. package/behaviors/legend.d.ts +0 -5
  31. package/behaviors/legend.d.ts.map +1 -1
  32. package/behaviors/legend.js +45 -13
  33. package/behaviors/menu.d.ts.map +1 -1
  34. package/behaviors/menu.js +13 -8
  35. package/behaviors/modal.d.ts.map +1 -1
  36. package/behaviors/modal.js +77 -19
  37. package/behaviors/popover.d.ts +4 -3
  38. package/behaviors/popover.d.ts.map +1 -1
  39. package/behaviors/popover.js +94 -14
  40. package/behaviors/sources.d.ts.map +1 -1
  41. package/behaviors/sources.js +14 -2
  42. package/behaviors/splitter.d.ts.map +1 -1
  43. package/behaviors/splitter.js +149 -110
  44. package/behaviors/spotlight.d.ts.map +1 -1
  45. package/behaviors/spotlight.js +28 -2
  46. package/behaviors/table.d.ts +1 -1
  47. package/behaviors/table.d.ts.map +1 -1
  48. package/behaviors/table.js +108 -17
  49. package/behaviors/tabs.d.ts.map +1 -1
  50. package/behaviors/tabs.js +84 -20
  51. package/behaviors/theme.d.ts.map +1 -1
  52. package/behaviors/theme.js +25 -5
  53. package/behaviors/toast.js +5 -5
  54. package/classes/index.d.ts +15 -2
  55. package/classes/index.js +48 -35
  56. package/connectors/index.d.ts +41 -8
  57. package/connectors/index.d.ts.map +1 -1
  58. package/connectors/index.js +74 -19
  59. package/css/annotations.css +12 -0
  60. package/css/app.css +3 -4
  61. package/css/base.css +1 -1
  62. package/css/content.css +3 -3
  63. package/css/crosshair.css +27 -2
  64. package/css/disclosure.css +3 -3
  65. package/css/dots.css +4 -4
  66. package/css/feedback.css +8 -37
  67. package/css/forms.css +9 -12
  68. package/css/legend.css +1 -1
  69. package/css/marks.css +1 -1
  70. package/css/motion.css +6 -6
  71. package/css/navigation.css +12 -0
  72. package/css/overlay.css +5 -7
  73. package/css/primitives.css +14 -16
  74. package/css/sidenote.css +2 -2
  75. package/css/table.css +2 -2
  76. package/css/tokens.css +16 -0
  77. package/dist/bronto.css +1 -1
  78. package/dist/css/analytical.css +1 -1
  79. package/dist/css/annotations.css +1 -1
  80. package/dist/css/crosshair.css +1 -1
  81. package/dist/css/feedback.css +1 -1
  82. package/dist/css/navigation.css +1 -1
  83. package/dist/css/report-kit.css +1 -1
  84. package/dist/css/tokens.css +1 -1
  85. package/docs/adr/0001-color-system.md +3 -2
  86. package/docs/annotations.md +21 -1
  87. package/docs/architecture.md +74 -13
  88. package/docs/command.md +4 -1
  89. package/docs/connectors.md +16 -0
  90. package/docs/crosshair.md +1 -1
  91. package/docs/dots.md +4 -1
  92. package/docs/glyphs.md +11 -0
  93. package/docs/interop/react-flow.md +89 -0
  94. package/docs/migrations/0.2-to-0.3.md +1 -1
  95. package/docs/package-contract.md +7 -5
  96. package/docs/reporting.md +23 -12
  97. package/docs/stability.md +85 -9
  98. package/docs/theming.md +2 -2
  99. package/docs/usage.md +16 -2
  100. package/docs/vega.md +4 -4
  101. package/glyphs/glyphs.js +43 -33
  102. package/llms.txt +19 -8
  103. package/package.json +23 -4
  104. package/schemas/report-claims.v1.schema.json +1 -1
  105. package/svelte/index.d.ts +71 -45
  106. package/svelte/index.d.ts.map +1 -1
  107. package/svelte/index.js +29 -2
  108. package/tokens/index.js +2 -2
  109. package/vue/index.d.ts +42 -5
  110. package/vue/index.d.ts.map +1 -1
  111. package/vue/index.js +32 -1
package/behaviors/tabs.js CHANGED
@@ -1,4 +1,12 @@
1
- import { hasDom, resolveHost, noop, bindOnce, nextFieldUid, collectHosts } from './internal.js';
1
+ import {
2
+ hasDom,
3
+ resolveHost,
4
+ noop,
5
+ bindOnce,
6
+ nextFieldUid,
7
+ collectHosts,
8
+ closestSafe,
9
+ } from './internal.js';
2
10
 
3
11
  /**
4
12
  * Wire `[data-bronto-tabs]` groups for full keyboard a11y. The framework
@@ -24,6 +32,23 @@ export function initTabs({ root } = {}) {
24
32
  if (!host) return noop;
25
33
  const cleanups = [];
26
34
  const groups = collectHosts(host, '[data-bronto-tabs]');
35
+ const snapshotAttrs = (el, names) => {
36
+ const out = {};
37
+ for (const name of names) {
38
+ out[name] = {
39
+ had: el.hasAttribute(name),
40
+ value: el.getAttribute(name),
41
+ };
42
+ }
43
+ return out;
44
+ };
45
+ const restoreAttrs = (el, attrs) => {
46
+ for (const [name, attr] of Object.entries(attrs)) {
47
+ if (attr.had) el.setAttribute(name, attr.value);
48
+ else el.removeAttribute(name);
49
+ }
50
+ };
51
+
27
52
  for (const group of groups) {
28
53
  // Own group only — a tab/panel inside a nested [data-bronto-tabs]
29
54
  // belongs to that inner group, not this one.
@@ -31,20 +56,43 @@ export function initTabs({ root } = {}) {
31
56
  const tabs = [...group.querySelectorAll('.ui-tab')].filter(owned);
32
57
  const panels = [...group.querySelectorAll('.ui-tabs__panel')].filter(owned);
33
58
  if (!tabs.length) continue;
34
- const list = group.querySelector('.ui-tabs__list');
35
- if (list) list.setAttribute('role', 'tablist');
36
-
37
- // APG: bind each tab to its panel (aria-controls) and back
38
- // (aria-labelledby), minting stable ids only where absent.
39
- for (const t of tabs) {
40
- const p = panels.find((x) => x.dataset.panel === t.dataset.tab);
41
- if (!p) continue;
42
- const n = nextFieldUid();
43
- if (!t.id) t.id = `bronto-tab-${n}`;
44
- if (!p.id) p.id = `bronto-tabpanel-${n}`;
45
- t.setAttribute('aria-controls', p.id);
46
- p.setAttribute('aria-labelledby', t.id);
47
- }
59
+ const list = [...group.querySelectorAll('.ui-tabs__list')].find(owned);
60
+ const rememberState = () => ({
61
+ list: list ? snapshotAttrs(list, ['role']) : null,
62
+ tabs: new Map(
63
+ tabs.map((tab) => [
64
+ tab,
65
+ {
66
+ active: tab.classList.contains('is-active'),
67
+ attrs: snapshotAttrs(tab, ['id', 'role', 'aria-selected', 'aria-controls', 'tabindex']),
68
+ },
69
+ ]),
70
+ ),
71
+ panels: new Map(
72
+ panels.map((panel) => [
73
+ panel,
74
+ {
75
+ hidden: panel.hidden,
76
+ attrs: snapshotAttrs(panel, ['id', 'role', 'aria-labelledby', 'tabindex']),
77
+ },
78
+ ]),
79
+ ),
80
+ });
81
+ const restoreState = (state) => {
82
+ if (list && state.list) restoreAttrs(list, state.list);
83
+ for (const tab of tabs) {
84
+ const tabState = state.tabs.get(tab);
85
+ if (!tabState) continue;
86
+ tab.classList.toggle('is-active', tabState.active);
87
+ restoreAttrs(tab, tabState.attrs);
88
+ }
89
+ for (const panel of panels) {
90
+ const panelState = state.panels.get(panel);
91
+ if (!panelState) continue;
92
+ panel.hidden = panelState.hidden;
93
+ restoreAttrs(panel, panelState.attrs);
94
+ }
95
+ };
48
96
 
49
97
  const select = (tab) => {
50
98
  for (const t of tabs) {
@@ -55,14 +103,14 @@ export function initTabs({ root } = {}) {
55
103
  t.tabIndex = on ? 0 : -1;
56
104
  }
57
105
  // Only retarget panels when this tab actually controls one. A panel-less
58
- // tab must NOT hide every panel leave the prior panel visible (C30).
106
+ // tab must not hide every panel; leave the prior panel visible.
59
107
  if (!panels.some((p) => p.dataset.panel === tab.dataset.tab)) return;
60
108
  for (const p of panels) {
61
109
  p.setAttribute('role', 'tabpanel');
62
110
  const shown = p.dataset.panel === tab.dataset.tab;
63
111
  p.hidden = !shown;
64
112
  // APG: a tabpanel is focusable so keyboard users can reach a text-only
65
- // panel; hidden panels drop out of the tab order (C30).
113
+ // panel; hidden panels drop out of the tab order.
66
114
  if (shown) p.tabIndex = 0;
67
115
  else p.removeAttribute('tabindex');
68
116
  }
@@ -70,14 +118,15 @@ export function initTabs({ root } = {}) {
70
118
  const onClick = (e) => {
71
119
  // `tabs` is filtered to this group, so membership (not mere DOM
72
120
  // containment) is what isolates nested [data-bronto-tabs] groups.
73
- const tab = e.target.closest('.ui-tab');
121
+ const tab = closestSafe(e.target, '.ui-tab');
74
122
  if (tab && tabs.includes(tab)) {
123
+ e.preventDefault();
75
124
  select(tab);
76
125
  tab.focus();
77
126
  }
78
127
  };
79
128
  const onKey = (e) => {
80
- const i = tabs.indexOf(e.target.closest('.ui-tab'));
129
+ const i = tabs.indexOf(closestSafe(e.target, '.ui-tab'));
81
130
  if (i < 0) return;
82
131
  let n = i;
83
132
  if (e.key === 'ArrowRight' || e.key === 'ArrowDown') n = (i + 1) % tabs.length;
@@ -90,14 +139,29 @@ export function initTabs({ root } = {}) {
90
139
  select(tabs[n]);
91
140
  tabs[n].focus();
92
141
  };
93
- select(tabs.find((t) => t.classList.contains('is-active')) || tabs[0]);
94
142
  cleanups.push(
95
143
  bindOnce(group, 'tabs', () => {
144
+ const state = rememberState();
145
+ if (list) list.setAttribute('role', 'tablist');
146
+
147
+ // APG: bind each tab to its panel (aria-controls) and back
148
+ // (aria-labelledby), minting stable ids only where absent.
149
+ for (const t of tabs) {
150
+ const p = panels.find((x) => x.dataset.panel === t.dataset.tab);
151
+ if (!p) continue;
152
+ const n = nextFieldUid();
153
+ if (!t.id) t.id = `bronto-tab-${n}`;
154
+ if (!p.id) p.id = `bronto-tabpanel-${n}`;
155
+ t.setAttribute('aria-controls', p.id);
156
+ p.setAttribute('aria-labelledby', t.id);
157
+ }
158
+ select(tabs.find((t) => t.classList.contains('is-active')) || tabs[0]);
96
159
  group.addEventListener('click', onClick);
97
160
  group.addEventListener('keydown', onKey);
98
161
  return () => {
99
162
  group.removeEventListener('click', onClick);
100
163
  group.removeEventListener('keydown', onKey);
164
+ restoreState(state);
101
165
  };
102
166
  }),
103
167
  );
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["theme.js"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,wDAHW,cAAc,GACZ,IAAI,CAahB;AAED;;;;;;;;;;;;;;GAcG;AACH,uDAHW,gBAAgB,GAAG,OAAO,eAAe,EAAE,YAAY,GACrD,OAAO,eAAe,EAAE,OAAO,CAmD3C;;;;;;;;;;6BA5FY,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;WAIpC,OAAO,GAAG,MAAM"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["theme.js"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,wDAHW,cAAc,GACZ,IAAI,CAahB;AAED;;;;;;;;;;;;;;GAcG;AACH,uDAHW,gBAAgB,GAAG,OAAO,eAAe,EAAE,YAAY,GACrD,OAAO,eAAe,EAAE,OAAO,CAuE3C;;;;;;;;;;6BAhHY,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;WAIpC,OAAO,GAAG,MAAM"}
@@ -1,4 +1,4 @@
1
- import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, collectHosts, closestSafe } from './internal.js';
2
2
 
3
3
  const THEMES = ['light', 'dark'];
4
4
 
@@ -53,7 +53,18 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
53
53
  if (!hasDom()) return noop;
54
54
  const host = resolveHost(root);
55
55
  if (!host) return noop;
56
- const docEl = document.documentElement;
56
+ const doc = host.nodeType === 9 ? host : host.ownerDocument || document;
57
+ const docEl = doc.documentElement;
58
+ const toggleStates = new Map();
59
+
60
+ const rememberToggle = (el) => {
61
+ if (!toggleStates.has(el)) {
62
+ toggleStates.set(el, {
63
+ had: el.hasAttribute('aria-pressed'),
64
+ value: el.getAttribute('aria-pressed'),
65
+ });
66
+ }
67
+ };
57
68
 
58
69
  const prefersDark = () =>
59
70
  typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches;
@@ -67,6 +78,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
67
78
  const reflect = () => {
68
79
  const c = current();
69
80
  collectHosts(host, '[data-bronto-theme-toggle]').forEach((el) => {
81
+ rememberToggle(el);
70
82
  const forced = el.getAttribute('data-bronto-theme-toggle');
71
83
  // A forced control is "pressed" when its theme is the active one;
72
84
  // a plain toggle reflects whether dark is active.
@@ -76,8 +88,9 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
76
88
  };
77
89
 
78
90
  const onClick = (e) => {
79
- const trigger = e.target.closest('[data-bronto-theme-toggle]');
91
+ const trigger = closestSafe(e.target, '[data-bronto-theme-toggle]');
80
92
  if (!trigger || !host.contains(trigger)) return;
93
+ e.preventDefault();
81
94
  const forced = trigger.getAttribute('data-bronto-theme-toggle');
82
95
  const next = THEMES.includes(forced) ? forced : current() === 'dark' ? 'light' : 'dark';
83
96
  docEl.setAttribute('data-theme', next);
@@ -92,10 +105,17 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
92
105
  );
93
106
  };
94
107
 
95
- applyStoredTheme({ storageKey });
108
+ applyStoredTheme({ storageKey, root: docEl });
96
109
  reflect();
97
110
  return bindOnce(host, 'themeToggle', () => {
98
111
  host.addEventListener('click', onClick);
99
- return () => host.removeEventListener('click', onClick);
112
+ return () => {
113
+ host.removeEventListener('click', onClick);
114
+ for (const [el, state] of toggleStates) {
115
+ if (state.had) el.setAttribute('aria-pressed', state.value);
116
+ else el.removeAttribute('aria-pressed');
117
+ }
118
+ toggleStates.clear();
119
+ };
100
120
  });
101
121
  }
@@ -3,7 +3,7 @@ import { hasDom, noop } from './internal.js';
3
3
  // The tones that have a `.ui-toast--*` rule. The TS type already unions these,
4
4
  // but a plain-JS / LLM caller can pass any string — and an unknown tone built a
5
5
  // `.ui-toast--error` class that matches no CSS, yielding a silent neutral toast.
6
- // Validate so an unknown tone degrades to neutral *and warns*, never lies. (C16)
6
+ // Validate so an unknown tone degrades to neutral *and warns*, never lies.
7
7
  const TOAST_TONES = new Set(['accent', 'success', 'warning', 'danger', 'info']);
8
8
 
9
9
  // First-toast deferral queue. The very first toast on a brand-new stack
@@ -29,7 +29,7 @@ function toastStack(isAssertive) {
29
29
  stack.setAttribute('role', 'alert');
30
30
  // The assertive region carries one error at a time and must be read whole;
31
31
  // aria-atomic ensures the full toast (title + message) announces, not just
32
- // the diff. (component audit C38.)
32
+ // the changed fragment.
33
33
  stack.setAttribute('aria-atomic', 'true');
34
34
  }
35
35
  document.body.appendChild(stack);
@@ -67,7 +67,7 @@ function toastElement(message, { tone, title }) {
67
67
  // aria-atomic so a *titled* toast announces title + message as one unit, not
68
68
  // disjointly — and unlike aria-atomic on the polite STACK (which would re-read
69
69
  // every resident toast on each new one), scoping it to the toast keeps sibling
70
- // toasts out of the announcement. (component audit C23.)
70
+ // toasts out of the announcement.
71
71
  el.setAttribute('aria-atomic', 'true');
72
72
  if (title) {
73
73
  const t = document.createElement('p');
@@ -183,8 +183,8 @@ export function toast(message, { tone, title, duration = 4000, assertive, closab
183
183
  // `closable`. The button carries no text node (glyph is a CSS
184
184
  // ::before) so the toast's announced/textContent stays the message.
185
185
  // Explicitly opting OUT of the close button on a sticky toast strands it with
186
- // no in-UI dismissal warn that the caller must retain and call the returned
187
- // dismiss fn. (component audit C37.)
186
+ // no in-UI dismissal; warn that the caller must retain and call the returned
187
+ // dismiss function.
188
188
  if (duration === 0 && closable === false && typeof console !== 'undefined') {
189
189
  console.warn(
190
190
  '[bronto] toast(): duration:0 + closable:false has no in-UI dismissal — keep the returned dismiss() and call it yourself, or set closable:true.',
@@ -959,9 +959,22 @@ export interface ValueAttrs {
959
959
  'aria-valuemax': number;
960
960
  style: { '--value': number };
961
961
  }
962
+ export interface MeterValueAttrs extends ValueAttrs {
963
+ role: 'meter';
964
+ }
965
+ export interface ProgressValueAttrs extends ValueAttrs {
966
+ role: 'progressbar';
967
+ }
968
+ export interface IndeterminateProgressAttrs {
969
+ role: 'progressbar';
970
+ 'aria-busy': 'true';
971
+ }
962
972
  export interface Attrs {
963
- meter(value: number, opts?: ValueRangeOpts): ValueAttrs;
964
- progress(value: number, opts?: ValueRangeOpts): ValueAttrs;
973
+ meter(value: number, opts?: ValueRangeOpts): MeterValueAttrs;
974
+ progress(value: number, opts?: ValueRangeOpts): ProgressValueAttrs;
975
+ progress(value?: undefined, opts?: ValueRangeOpts): IndeterminateProgressAttrs;
976
+ dotbar(value: number, opts?: ValueRangeOpts): ProgressValueAttrs;
977
+ dotbar(value?: undefined, opts?: ValueRangeOpts): IndeterminateProgressAttrs;
965
978
  }
966
979
  /** Set the painted value AND its ARIA together so they cannot drift. */
967
980
  export declare const attrs: Attrs;
package/classes/index.js CHANGED
@@ -212,7 +212,7 @@ export const cls = Object.freeze({
212
212
  tableLined: 'ui-table--lined',
213
213
  tableBreakAnywhere: 'ui-table--break-anywhere',
214
214
  tableWrap: 'ui-table-wrap',
215
- // Loading state goes on the WRAP, so the modifier is named for the wrap (C19).
215
+ // Loading state goes on the wrap, so the modifier is named for the wrap.
216
216
  tableLoading: 'ui-table-wrap--loading',
217
217
  tableEmpty: 'ui-table__empty',
218
218
  tableSort: 'ui-table__sort',
@@ -467,7 +467,6 @@ export const cls = Object.freeze({
467
467
  crosshairLineX: 'ui-crosshair__line--x',
468
468
  crosshairLineY: 'ui-crosshair__line--y',
469
469
  crosshairBadge: 'ui-crosshair__badge',
470
- readout: 'ui-readout',
471
470
  // selection-state vocabulary (cross-cutting — css/selection.css)
472
471
  sel: 'ui-sel',
473
472
  selOn: 'ui-sel--on',
@@ -740,14 +739,14 @@ const srcTone = (state) =>
740
739
  })[state] || '';
741
740
 
742
741
  // Component tone → modifier class. Same object-literal idiom as srcTone/stateTone
743
- // (still grep-by-class-name; the modifier set differs per component). (Q9.)
742
+ // while keeping modifier classes grep-friendly.
744
743
  //
745
744
  // The set differs PER COMPONENT on purpose — `muted` is a badge/num tone, not an
746
745
  // alert/toast/meter/dot one — so a caller extrapolating a universal tone (e.g.
747
746
  // `ui.alert({ tone: 'muted' })`) used to get a silent no-op: a bare, untoned
748
747
  // element with no signal that the tone was dropped. Warn at dev time instead, so
749
748
  // that "validates-but-no-ops" trap is loud rather than invisible. An omitted tone
750
- // is fine (returns no modifier). (component audit C12.)
749
+ // is fine and returns no modifier.
751
750
  const toneClass = (component, map, tone) => {
752
751
  if (tone == null) return '';
753
752
  const hit = map[tone];
@@ -853,6 +852,43 @@ const claimStatus = (status) =>
853
852
  status,
854
853
  );
855
854
 
855
+ const valueClass = (map, value) => (value == null ? '' : map[value] || '');
856
+
857
+ const annotationVariants = Object.freeze({
858
+ label: cls.annotationLabelVariant,
859
+ callout: cls.annotationCallout,
860
+ elbow: cls.annotationElbow,
861
+ curve: cls.annotationCurve,
862
+ circle: cls.annotationCircle,
863
+ rect: cls.annotationRect,
864
+ threshold: cls.annotationThreshold,
865
+ badge: cls.annotationBadgeVariant,
866
+ bracket: cls.annotationBracket,
867
+ band: cls.annotationBand,
868
+ slope: cls.annotationSlope,
869
+ compare: cls.annotationCompare,
870
+ cluster: cls.annotationCluster,
871
+ axis: cls.annotationAxis,
872
+ timeline: cls.annotationTimeline,
873
+ evidence: cls.annotationEvidence,
874
+ });
875
+
876
+ const annotationTones = Object.freeze({
877
+ accent: cls.annotationAccent,
878
+ muted: cls.annotationMuted,
879
+ success: cls.annotationSuccess,
880
+ warning: cls.annotationWarning,
881
+ danger: cls.annotationDanger,
882
+ info: cls.annotationInfo,
883
+ });
884
+
885
+ const annotationMotions = Object.freeze({
886
+ draw: cls.annotationDraw,
887
+ pulse: cls.annotationPulse,
888
+ reveal: cls.annotationReveal,
889
+ focus: cls.annotationFocus,
890
+ });
891
+
856
892
  export const ui = {
857
893
  button: ({ variant, icon, size } = {}) =>
858
894
  j(
@@ -928,7 +964,7 @@ export const ui = {
928
964
  j(
929
965
  cls.legendSwatch,
930
966
  // Series 1–8 map to ui-legend__swatch--N; the explicit bounds-check keeps a
931
- // 0/9/non-integer from coining a class the stylesheet never defines. (Q9.)
967
+ // 0/9/non-integer from coining a class the stylesheet never defines.
932
968
  Number.isInteger(series) && series >= 1 && series <= 8 && cls[`legendSwatch${series}`],
933
969
  shape === 'circle' && cls.legendSwatchCircle,
934
970
  shape === 'line' && cls.legendSwatchLine,
@@ -936,32 +972,9 @@ export const ui = {
936
972
  annotation: ({ variant = 'callout', tone = 'accent', motion } = {}) =>
937
973
  j(
938
974
  cls.annotation,
939
- variant === 'label' && cls.annotationLabelVariant,
940
- variant === 'callout' && cls.annotationCallout,
941
- variant === 'elbow' && cls.annotationElbow,
942
- variant === 'curve' && cls.annotationCurve,
943
- variant === 'circle' && cls.annotationCircle,
944
- variant === 'rect' && cls.annotationRect,
945
- variant === 'threshold' && cls.annotationThreshold,
946
- variant === 'badge' && cls.annotationBadgeVariant,
947
- variant === 'bracket' && cls.annotationBracket,
948
- variant === 'band' && cls.annotationBand,
949
- variant === 'slope' && cls.annotationSlope,
950
- variant === 'compare' && cls.annotationCompare,
951
- variant === 'cluster' && cls.annotationCluster,
952
- variant === 'axis' && cls.annotationAxis,
953
- variant === 'timeline' && cls.annotationTimeline,
954
- variant === 'evidence' && cls.annotationEvidence,
955
- tone === 'accent' && cls.annotationAccent,
956
- tone === 'muted' && cls.annotationMuted,
957
- tone === 'success' && cls.annotationSuccess,
958
- tone === 'warning' && cls.annotationWarning,
959
- tone === 'danger' && cls.annotationDanger,
960
- tone === 'info' && cls.annotationInfo,
961
- motion === 'draw' && cls.annotationDraw,
962
- motion === 'pulse' && cls.annotationPulse,
963
- motion === 'reveal' && cls.annotationReveal,
964
- motion === 'focus' && cls.annotationFocus,
975
+ valueClass(annotationVariants, variant),
976
+ valueClass(annotationTones, tone),
977
+ valueClass(annotationMotions, motion),
965
978
  ),
966
979
  mark: ({ style, tone, motion } = {}) =>
967
980
  j(
@@ -1050,7 +1063,7 @@ export const ui = {
1050
1063
  // UNITLESS `--value` (0–100) and AT needs role + aria-valuenow/min/max. This sets
1051
1064
  // the painted value and its announced value together so they can't drift, and
1052
1065
  // normalizes an arbitrary {min,max} to the 0–100 `--value` the CSS expects while
1053
- // keeping aria-valuenow in the caller's real units. (component audit C8.)
1066
+ // keeping aria-valuenow in the caller's real units.
1054
1067
  // `busyWhenIndeterminate` — a progressbar advertises aria-busy when its value is
1055
1068
  // unknown; a meter is never indeterminate so it passes false. (Kept a boolean
1056
1069
  // flag rather than testing the role string, so check:recipe-types doesn't read it
@@ -1065,7 +1078,7 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1065
1078
  // advertises aria-busy; a meter has no indeterminate state, so a non-finite
1066
1079
  // value there is a caller error we still fail safe on by omitting the
1067
1080
  // misleading 0. Pair with `ui.progress({ indeterminate: true })` for the CSS
1068
- // sweep. (component audit C9.)
1081
+ // sweep.
1069
1082
  if (!Number.isFinite(raw)) {
1070
1083
  return busyWhenIndeterminate ? { role, 'aria-busy': 'true' } : { role };
1071
1084
  }
@@ -1087,13 +1100,13 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1087
1100
  * </div>
1088
1101
  * `value` is in your own units; pass `{ min, max }` (default 0–100) and the
1089
1102
  * `--value` width is normalized for you. Call `attrs.progress()` with no value
1090
- * for an indeterminate bar (omits aria-valuenow, sets aria-busy). (audit C9.)
1103
+ * for an indeterminate bar (omits aria-valuenow, sets aria-busy).
1091
1104
  *
1092
1105
  * `attrs.dotbar(value)` is the segmented analogue of `attrs.progress`: a
1093
1106
  * determinate `.ui-dotbar` carries the same progress data as `.ui-progress` but,
1094
1107
  * without this, was eight empty `<span>`s to AT (the segments are decorative —
1095
1108
  * mark them `aria-hidden`). Same progressbar role + aria-valuenow/min/max;
1096
- * call with no value for the indeterminate sweep. (component audit C10.)
1109
+ * call with no value for the indeterminate sweep.
1097
1110
  */
1098
1111
  export const attrs = Object.freeze({
1099
1112
  meter: (value, { min = 0, max = 100 } = {}) => valueAttrs('meter', value, min, max, false),
@@ -1,9 +1,42 @@
1
- export function finite(name: any, value: any, fallback: any): any;
2
- export function dimension(name: any, value: any, fallback: any): any;
3
- export function roundNumber(value: any): number;
4
- export function fmt(value: any): string;
5
- export function point(x: any, y: any): string;
6
- export function clamp(value: any, min: any, max: any): any;
1
+ /**
2
+ * Resolve a numeric option with an optional fallback.
3
+ * @param {string} name
4
+ * @param {number | null | undefined} value
5
+ * @param {number | null | undefined} [fallback]
6
+ * @returns {number}
7
+ */
8
+ export function finite(name: string, value: number | null | undefined, fallback?: number | null | undefined): number;
9
+ /**
10
+ * Resolve a non-negative numeric option with an optional fallback.
11
+ * @param {string} name
12
+ * @param {number | null | undefined} value
13
+ * @param {number | null | undefined} [fallback]
14
+ * @returns {number}
15
+ */
16
+ export function dimension(name: string, value: number | null | undefined, fallback?: number | null | undefined): number;
17
+ /**
18
+ * @param {number} value
19
+ * @returns {number}
20
+ */
21
+ export function roundNumber(value: number): number;
22
+ /**
23
+ * @param {number} value
24
+ * @returns {string}
25
+ */
26
+ export function fmt(value: number): string;
27
+ /**
28
+ * @param {number} x
29
+ * @param {number} y
30
+ * @returns {string}
31
+ */
32
+ export function point(x: number, y: number): string;
33
+ /**
34
+ * @param {number} value
35
+ * @param {number} min
36
+ * @param {number} max
37
+ * @returns {number}
38
+ */
39
+ export function clamp(value: number, min: number, max: number): number;
7
40
  /**
8
41
  * A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
9
42
  * @param {Rect} rect
@@ -70,8 +103,8 @@ export function arrowHead(p: Point, angle: number, size?: number, spread?: numbe
70
103
  export function dotMark(p: Point, radius?: number): string;
71
104
  /**
72
105
  * An axis-aligned rectangle path from its corners (callers derive the corners
73
- * from a centre or a top-left as they need). Shared by the annotation
74
- * rect/band and evidence-marker subjects. (code-quality audit Q5.)
106
+ * from a centre or a top-left as they need). Shared by annotation rect/band
107
+ * and evidence-marker subjects.
75
108
  * @param {number} left
76
109
  * @param {number} top
77
110
  * @param {number} right
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAkDA,kEAIC;AAED,qEAIC;AAKD,gDAGC;AAED,wCAEC;AAED,8CAEC;AAID,2DAGC;AAED;;;;;GAKG;AACH,kCAJW,IAAI,SACJ,IAAI,GACF,KAAK,CAoBjB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACd,MAAM,CAgBlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpB,MAAM,CAclB;AAED;;;;GAIG;AACH,qCAHW,oBAAoB,GAClB,MAAM,CAOlB;AAED;;;;;;;;GAQG;AACH,6BAPW,KAAK,SACL,MAAM,SACN,MAAM,WACN,MAAM,GAEJ,MAAM,CAalB;AAED;;;;;GAKG;AACH,2BAJW,KAAK,WACL,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;GASG;AACH,+BANW,MAAM,OACN,MAAM,SACN,MAAM,UACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;GAKG;AACH,oCAJW,IAAI,UACJ,IAAI,GACF;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAWpC;AAED;;;;;;;;GAQG;AACH,sCALW,KAAK,MACL,KAAK,UACL,cAAc,GACZ,MAAM,CAQlB;AAED;;;;;;GAMG;AACH,oCAHW,mBAAmB,GACjB,kBAAkB,CAW9B;AAvSD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAMH,wBAAyB,IAAI,CAAC;oBAhCjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;mBACxB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;mBACvD,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ;6BAC9C,UAAU,GAAG,OAAO,GAAG,OAAO;;UAG7B,KAAK;QACL,KAAK;;;;;;;;;;;;cAML,IAAI;YACJ,IAAI;;;;;;;;;;;;;;OAQJ,MAAM;UACN,KAAK;QACL,KAAK;;;;WACL,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAiDA;;;;;;GAMG;AACH,6BALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAED;;;;;;GAMG;AACH,gCALW,MAAM,SACN,MAAM,GAAG,IAAI,GAAG,SAAS,aACzB,MAAM,GAAG,IAAI,GAAG,SAAS,GACvB,MAAM,CAMlB;AAID;;;GAGG;AACH,mCAHW,MAAM,GACJ,MAAM,CAKlB;AAED;;;GAGG;AACH,2BAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,yBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CAIlB;AAGD;;;;;GAKG;AACH,6BALW,MAAM,OACN,MAAM,OACN,MAAM,GACJ,MAAM,CAKlB;AAqBD;;;;;GAKG;AACH,kCAJW,IAAI,SACJ,IAAI,GACF,KAAK,CAoBjB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;GAKG;AACH,mCAJW,KAAK,MACL,KAAK,GACH,MAAM,CAOlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACd,MAAM,CAgBlB;AAED;;;;;;GAMG;AACH,gCALW,KAAK,MACL,KAAK,SACL;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpB,MAAM,CAclB;AAED;;;;GAIG;AACH,qCAHW,oBAAoB,GAClB,MAAM,CAQlB;AAED;;;;;;;;GAQG;AACH,6BAPW,KAAK,SACL,MAAM,SACN,MAAM,WACN,MAAM,GAEJ,MAAM,CAalB;AAED;;;;;GAKG;AACH,2BAJW,KAAK,WACL,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;GASG;AACH,+BANW,MAAM,OACN,MAAM,SACN,MAAM,UACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;GAKG;AACH,oCAJW,IAAI,UACJ,IAAI,GACF;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAWpC;AAED;;;;;;;;GAQG;AACH,sCALW,KAAK,MACL,KAAK,UACL,cAAc,GACZ,MAAM,CASlB;AAED;;;;;;GAMG;AACH,oCAHW,mBAAmB,GACjB,kBAAkB,CAe9B;AA9VD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAKH,wBAAyB,IAAI,CAAC;oBA/BjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;mBACxB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;mBACvD,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ;6BAC9C,UAAU,GAAG,OAAO,GAAG,OAAO;;UAG7B,KAAK;QACL,KAAK;;;;;;;;;;;;cAML,IAAI;YACJ,IAAI;;;;;;;;;;;;;;OAQJ,MAAM;UACN,KAAK;QACL,KAAK;;;;WACL,MAAM"}