@ponchia/ui 0.6.9 → 0.6.11

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 (145) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +38 -25
  3. package/annotations/index.d.ts +15 -15
  4. package/annotations/index.d.ts.map +1 -1
  5. package/annotations/index.js +52 -34
  6. package/behaviors/carousel.d.ts +7 -3
  7. package/behaviors/carousel.d.ts.map +1 -1
  8. package/behaviors/carousel.js +157 -27
  9. package/behaviors/combobox.d.ts +1 -1
  10. package/behaviors/combobox.d.ts.map +1 -1
  11. package/behaviors/combobox.js +46 -23
  12. package/behaviors/command.d.ts +1 -1
  13. package/behaviors/command.d.ts.map +1 -1
  14. package/behaviors/command.js +63 -23
  15. package/behaviors/connectors.d.ts.map +1 -1
  16. package/behaviors/connectors.js +126 -19
  17. package/behaviors/crosshair.d.ts.map +1 -1
  18. package/behaviors/crosshair.js +71 -8
  19. package/behaviors/dialog.d.ts.map +1 -1
  20. package/behaviors/dialog.js +20 -3
  21. package/behaviors/disclosure.d.ts.map +1 -1
  22. package/behaviors/disclosure.js +35 -6
  23. package/behaviors/dismissible.js +1 -1
  24. package/behaviors/forms.d.ts +23 -2
  25. package/behaviors/forms.d.ts.map +1 -1
  26. package/behaviors/forms.js +97 -9
  27. package/behaviors/glyph.d.ts.map +1 -1
  28. package/behaviors/glyph.js +56 -5
  29. package/behaviors/internal.d.ts.map +1 -1
  30. package/behaviors/internal.js +52 -5
  31. package/behaviors/menu.d.ts.map +1 -1
  32. package/behaviors/menu.js +2 -1
  33. package/behaviors/modal.d.ts.map +1 -1
  34. package/behaviors/modal.js +25 -9
  35. package/behaviors/popover.d.ts.map +1 -1
  36. package/behaviors/popover.js +8 -6
  37. package/behaviors/sources.d.ts.map +1 -1
  38. package/behaviors/sources.js +24 -3
  39. package/behaviors/splitter.d.ts.map +1 -1
  40. package/behaviors/splitter.js +27 -6
  41. package/behaviors/table.d.ts.map +1 -1
  42. package/behaviors/table.js +44 -7
  43. package/behaviors/tabs.d.ts.map +1 -1
  44. package/behaviors/tabs.js +51 -14
  45. package/behaviors/theme.d.ts.map +1 -1
  46. package/behaviors/theme.js +64 -4
  47. package/behaviors/toast.d.ts +6 -1
  48. package/behaviors/toast.d.ts.map +1 -1
  49. package/behaviors/toast.js +48 -12
  50. package/classes/classes.json +57 -2
  51. package/classes/index.d.ts +13 -2
  52. package/classes/index.js +88 -40
  53. package/connectors/index.d.ts +4 -4
  54. package/connectors/index.d.ts.map +1 -1
  55. package/connectors/index.js +14 -12
  56. package/css/annotations.css +1 -0
  57. package/css/app.css +7 -0
  58. package/css/base.css +3 -0
  59. package/css/bullet.css +41 -7
  60. package/css/code.css +14 -0
  61. package/css/command.css +10 -0
  62. package/css/dataviz.css +27 -0
  63. package/css/diff.css +2 -0
  64. package/css/disclosure.css +8 -0
  65. package/css/dots.css +1 -1
  66. package/css/feedback.css +9 -0
  67. package/css/interval.css +20 -2
  68. package/css/legend.css +10 -9
  69. package/css/marks.css +1 -0
  70. package/css/motion.css +2 -0
  71. package/css/overlay.css +14 -2
  72. package/css/primitives.css +1 -1
  73. package/css/report.css +3 -0
  74. package/css/sources.css +4 -4
  75. package/css/spotlight.css +6 -0
  76. package/css/table.css +19 -0
  77. package/css/term.css +4 -1
  78. package/css/tokens.css +8 -13
  79. package/css/workbench.css +128 -0
  80. package/dist/bronto.css +1 -1
  81. package/dist/css/analytical.css +1 -1
  82. package/dist/css/app.css +1 -1
  83. package/dist/css/bullet.css +1 -1
  84. package/dist/css/code.css +1 -1
  85. package/dist/css/command.css +1 -1
  86. package/dist/css/dataviz.css +1 -1
  87. package/dist/css/diff.css +1 -1
  88. package/dist/css/disclosure.css +1 -1
  89. package/dist/css/dots.css +1 -1
  90. package/dist/css/feedback.css +1 -1
  91. package/dist/css/interval.css +1 -1
  92. package/dist/css/legend.css +1 -1
  93. package/dist/css/marks.css +1 -1
  94. package/dist/css/overlay.css +1 -1
  95. package/dist/css/primitives.css +1 -1
  96. package/dist/css/report-kit.css +1 -1
  97. package/dist/css/sources.css +1 -1
  98. package/dist/css/spotlight.css +1 -1
  99. package/dist/css/table.css +1 -1
  100. package/dist/css/term.css +1 -1
  101. package/dist/css/tokens.css +1 -1
  102. package/dist/css/workbench.css +1 -1
  103. package/docs/annotations.md +27 -0
  104. package/docs/architecture.md +5 -3
  105. package/docs/bullet.md +6 -1
  106. package/docs/clamp.md +5 -0
  107. package/docs/command.md +3 -2
  108. package/docs/contrast.md +3 -3
  109. package/docs/crosshair.md +6 -0
  110. package/docs/dots.md +10 -3
  111. package/docs/figure.md +7 -0
  112. package/docs/glyphs.md +14 -2
  113. package/docs/highlights.md +9 -0
  114. package/docs/interval.md +6 -0
  115. package/docs/mermaid.md +5 -3
  116. package/docs/package-contract.md +24 -1
  117. package/docs/reference.md +21 -1
  118. package/docs/reporting.md +8 -8
  119. package/docs/selection.md +9 -0
  120. package/docs/sources.md +5 -0
  121. package/docs/state.md +6 -0
  122. package/docs/textref.md +18 -13
  123. package/docs/theming.md +18 -8
  124. package/docs/toc.md +6 -0
  125. package/docs/tree.md +9 -2
  126. package/docs/usage.md +2 -2
  127. package/docs/vega.md +5 -3
  128. package/docs/workbench.md +56 -9
  129. package/glyphs/glyphs.js +62 -8
  130. package/index.d.ts +1 -0
  131. package/llms.txt +18 -14
  132. package/package.json +98 -6
  133. package/qwik/index.d.ts +4 -3
  134. package/qwik/index.d.ts.map +1 -1
  135. package/qwik/index.js +7 -5
  136. package/react/index.d.ts +4 -3
  137. package/react/index.d.ts.map +1 -1
  138. package/react/index.js +3 -2
  139. package/solid/index.d.ts +7 -5
  140. package/solid/index.d.ts.map +1 -1
  141. package/solid/index.js +11 -7
  142. package/tokens/vega.d.ts +1 -1
  143. package/tokens/vega.js +3 -2
  144. package/vue/index.d.ts.map +1 -1
  145. package/vue/index.js +37 -3
@@ -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,CAuE3C;;;;;;;;;;6BAhHY,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":"AAuBA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,wDAHW,cAAc,GACZ,IAAI,CAahB;AAED;;;;;;;;;;;;;;GAcG;AACH,uDAHW,gBAAgB,GAAG,OAAO,eAAe,EAAE,YAAY,GACrD,OAAO,eAAe,EAAE,OAAO,CAgH3C;;;;;;;;;;6BAzJY,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;WAIpC,OAAO,GAAG,MAAM"}
@@ -1,6 +1,25 @@
1
1
  import { hasDom, resolveHost, noop, bindOnce, collectHosts, closestSafe } from './internal.js';
2
2
 
3
3
  const THEMES = ['light', 'dark'];
4
+ const DARK_SCHEME_QUERY = '(prefers-color-scheme: dark)';
5
+
6
+ const colorSchemeQuery = () =>
7
+ typeof matchMedia === 'function' ? matchMedia(DARK_SCHEME_QUERY) : null;
8
+
9
+ function onColorSchemeChange(query, listener) {
10
+ if (
11
+ typeof query?.addEventListener === 'function' &&
12
+ typeof query.removeEventListener === 'function'
13
+ ) {
14
+ query.addEventListener('change', listener);
15
+ return () => query.removeEventListener('change', listener);
16
+ }
17
+ if (typeof query?.addListener === 'function' && typeof query.removeListener === 'function') {
18
+ query.addListener(listener);
19
+ return () => query.removeListener(listener);
20
+ }
21
+ return null;
22
+ }
4
23
 
5
24
  /**
6
25
  * @typedef {object} ThemeStorageOpts
@@ -66,8 +85,12 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
66
85
  }
67
86
  };
68
87
 
69
- const prefersDark = () =>
70
- typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches;
88
+ let schemeQuery = null;
89
+ let removeSchemeListener = noop;
90
+
91
+ const hasExplicitTheme = () => THEMES.includes(docEl.getAttribute('data-theme'));
92
+
93
+ const prefersDark = () => (schemeQuery || colorSchemeQuery())?.matches === true;
71
94
 
72
95
  const current = () => {
73
96
  const attr = docEl.getAttribute('data-theme');
@@ -87,6 +110,38 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
87
110
  });
88
111
  };
89
112
 
113
+ const clearSchemeListener = () => {
114
+ removeSchemeListener();
115
+ removeSchemeListener = noop;
116
+ schemeQuery = null;
117
+ };
118
+
119
+ const syncSchemeListener = () => {
120
+ if (hasExplicitTheme()) {
121
+ clearSchemeListener();
122
+ return;
123
+ }
124
+ if (schemeQuery) return;
125
+ const query = colorSchemeQuery();
126
+ const cleanup = onColorSchemeChange(query, onSchemeChange);
127
+ if (!cleanup) return;
128
+ schemeQuery = query;
129
+ removeSchemeListener = cleanup;
130
+ };
131
+
132
+ function onSchemeChange() {
133
+ if (hasExplicitTheme()) {
134
+ clearSchemeListener();
135
+ return;
136
+ }
137
+ reflect();
138
+ }
139
+
140
+ const onThemeChange = () => {
141
+ reflect();
142
+ syncSchemeListener();
143
+ };
144
+
90
145
  const onClick = (e) => {
91
146
  const trigger = closestSafe(e.target, '[data-bronto-theme-toggle]');
92
147
  if (!trigger || !host.contains(trigger)) return;
@@ -94,6 +149,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
94
149
  const forced = trigger.getAttribute('data-bronto-theme-toggle');
95
150
  const next = THEMES.includes(forced) ? forced : current() === 'dark' ? 'light' : 'dark';
96
151
  docEl.setAttribute('data-theme', next);
152
+ clearSchemeListener();
97
153
  try {
98
154
  localStorage.setItem(storageKey, next);
99
155
  } catch {
@@ -105,12 +161,16 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
105
161
  );
106
162
  };
107
163
 
108
- applyStoredTheme({ storageKey, root: docEl });
109
- reflect();
110
164
  return bindOnce(host, 'themeToggle', () => {
165
+ applyStoredTheme({ storageKey, root: docEl });
166
+ reflect();
167
+ syncSchemeListener();
168
+ docEl.addEventListener('bronto:themechange', onThemeChange);
111
169
  host.addEventListener('click', onClick);
112
170
  return () => {
171
+ docEl.removeEventListener('bronto:themechange', onThemeChange);
113
172
  host.removeEventListener('click', onClick);
173
+ clearSchemeListener();
114
174
  for (const [el, state] of toggleStates) {
115
175
  if (state.had) el.setAttribute('aria-pressed', state.value);
116
176
  else el.removeAttribute('aria-pressed');
@@ -5,6 +5,7 @@
5
5
  * @property {number} [duration] Auto-dismiss delay in ms. `0` keeps it until dismissed. Default: `4000`.
6
6
  * @property {boolean} [assertive] Route to the assertive live region so AT interrupts immediately. Defaults to `true` when `tone === 'danger'`.
7
7
  * @property {boolean} [closable] Render a dismiss button on the toast.
8
+ * @property {string} [dismissLabel] Accessible label for the generated dismiss button. Default: `Dismiss`.
8
9
  */
9
10
  /**
10
11
  * Push a transient toast into a shared, screen-anchored stack. The stack
@@ -23,7 +24,7 @@
23
24
  * @param {ToastOpts} [opts]
24
25
  * @returns {import('./internal.js').Cleanup}
25
26
  */
26
- export function toast(message: string, { tone, title, duration, assertive, closable }?: ToastOpts): import("./internal.js").Cleanup;
27
+ export function toast(message: string, { tone, title, duration, assertive, closable, dismissLabel }?: ToastOpts): import("./internal.js").Cleanup;
27
28
  export type ToastOpts = {
28
29
  /**
29
30
  * Status tone — maps to `ui-toast--<tone>`.
@@ -45,5 +46,9 @@ export type ToastOpts = {
45
46
  * Render a dismiss button on the toast.
46
47
  */
47
48
  closable?: boolean | undefined;
49
+ /**
50
+ * Accessible label for the generated dismiss button. Default: `Dismiss`.
51
+ */
52
+ dismissLabel?: string | undefined;
48
53
  };
49
54
  //# sourceMappingURL=toast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["toast.js"],"names":[],"mappings":"AA2HA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,+BAJW,MAAM,mDACN,SAAS,GACP,OAAO,eAAe,EAAE,OAAO,CAgD3C"}
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["toast.js"],"names":[],"mappings":"AAwJA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,+BAJW,MAAM,iEACN,SAAS,GACP,OAAO,eAAe,EAAE,OAAO,CAsD3C"}
@@ -27,10 +27,10 @@ function toastStack(isAssertive) {
27
27
  stack.setAttribute('aria-live', isAssertive ? 'assertive' : 'polite');
28
28
  if (isAssertive) {
29
29
  stack.setAttribute('role', 'alert');
30
- // The assertive region carries one error at a time and must be read whole;
31
- // aria-atomic ensures the full toast (title + message) announces, not just
32
- // the changed fragment.
33
- stack.setAttribute('aria-atomic', 'true');
30
+ // The assertive stack may hold more than one sticky danger toast. Keep the
31
+ // container non-atomic so adding a new toast announces that toast only,
32
+ // while the per-toast aria-atomic below still reads its title + message.
33
+ stack.setAttribute('aria-atomic', 'false');
34
34
  }
35
35
  document.body.appendChild(stack);
36
36
  }
@@ -102,25 +102,54 @@ function removeToast(el) {
102
102
  }
103
103
  el.classList.add('is-leaving');
104
104
  let done = false;
105
+ let timer;
105
106
  const finish = () => {
106
107
  if (done) return;
107
108
  done = true;
109
+ el.removeEventListener('transitionend', finish);
110
+ if (timer !== undefined) clearTimeout(timer);
108
111
  el.remove();
109
112
  };
110
- el.addEventListener('transitionend', finish, { once: true });
111
- const timer = setTimeout(finish, dur * 1000 + 120);
113
+ el.addEventListener('transitionend', finish);
114
+ timer = setTimeout(finish, dur * 1000 + 120);
112
115
  timer?.unref?.(); // don't keep a Node test process alive
113
116
  }
114
117
 
115
- function addToastClose(el, dismiss) {
118
+ function addToastClose(el, dismiss, label = 'Dismiss') {
116
119
  const close = document.createElement('button');
117
120
  close.type = 'button';
118
121
  close.className = 'ui-toast__close';
119
- close.setAttribute('aria-label', 'Dismiss');
122
+ close.setAttribute('aria-label', label);
120
123
  close.addEventListener('click', dismiss);
121
124
  el.appendChild(close);
122
125
  }
123
126
 
127
+ function normalizeToastDuration(duration) {
128
+ let value;
129
+ try {
130
+ value = Number(duration);
131
+ } catch {
132
+ value = NaN;
133
+ }
134
+ if (Number.isFinite(value) && value >= 0) return value;
135
+ if (typeof console !== 'undefined') {
136
+ console.warn(
137
+ '[bronto] toast(): duration must be a finite non-negative number. Rendering a sticky, closable toast instead.',
138
+ );
139
+ }
140
+ return 0;
141
+ }
142
+
143
+ function toastDismissLabel(stack, dismissLabel) {
144
+ return (
145
+ (typeof dismissLabel === 'string' && dismissLabel.trim()) ||
146
+ stack.getAttribute('data-bronto-toast-dismiss-label')?.trim() ||
147
+ document.body?.getAttribute('data-bronto-toast-dismiss-label')?.trim() ||
148
+ document.documentElement?.getAttribute('data-bronto-toast-dismiss-label')?.trim() ||
149
+ 'Dismiss'
150
+ );
151
+ }
152
+
124
153
  /**
125
154
  * @typedef {object} ToastOpts
126
155
  * @property {'accent' | 'success' | 'warning' | 'danger' | 'info'} [tone] Status tone — maps to `ui-toast--<tone>`.
@@ -128,6 +157,7 @@ function addToastClose(el, dismiss) {
128
157
  * @property {number} [duration] Auto-dismiss delay in ms. `0` keeps it until dismissed. Default: `4000`.
129
158
  * @property {boolean} [assertive] Route to the assertive live region so AT interrupts immediately. Defaults to `true` when `tone === 'danger'`.
130
159
  * @property {boolean} [closable] Render a dismiss button on the toast.
160
+ * @property {string} [dismissLabel] Accessible label for the generated dismiss button. Default: `Dismiss`.
131
161
  */
132
162
 
133
163
  /**
@@ -147,7 +177,10 @@ function addToastClose(el, dismiss) {
147
177
  * @param {ToastOpts} [opts]
148
178
  * @returns {import('./internal.js').Cleanup}
149
179
  */
150
- export function toast(message, { tone, title, duration = 4000, assertive, closable } = {}) {
180
+ export function toast(
181
+ message,
182
+ { tone, title, duration = 4000, assertive, closable, dismissLabel } = {},
183
+ ) {
151
184
  if (!hasDom()) return noop;
152
185
  // Errors must interrupt: danger toasts (or an explicit `assertive`)
153
186
  // go to a SEPARATE assertive region so they announce immediately,
@@ -157,6 +190,7 @@ export function toast(message, { tone, title, duration = 4000, assertive, closab
157
190
  const isAssertive = assertive ?? tone === 'danger';
158
191
  const { stack, fresh: freshStack } = toastStack(isAssertive);
159
192
  const el = toastElement(message, { tone, title });
193
+ const normalizedDuration = normalizeToastDuration(duration);
160
194
  // Append after a frame the *first* time so the empty live region is
161
195
  // observed by AT before its first child arrives; once the region has
162
196
  // been observed, later toasts append synchronously.
@@ -185,12 +219,14 @@ export function toast(message, { tone, title, duration = 4000, assertive, closab
185
219
  // Explicitly opting OUT of the close button on a sticky toast strands it with
186
220
  // no in-UI dismissal; warn that the caller must retain and call the returned
187
221
  // dismiss function.
188
- if (duration === 0 && closable === false && typeof console !== 'undefined') {
222
+ if (normalizedDuration === 0 && closable === false && typeof console !== 'undefined') {
189
223
  console.warn(
190
224
  '[bronto] toast(): duration:0 + closable:false has no in-UI dismissal — keep the returned dismiss() and call it yourself, or set closable:true.',
191
225
  );
192
226
  }
193
- if (closable ?? duration === 0) addToastClose(el, dismiss);
194
- if (duration > 0) timer = setTimeout(dismiss, duration);
227
+ if (closable ?? normalizedDuration === 0) {
228
+ addToastClose(el, dismiss, toastDismissLabel(stack, dismissLabel));
229
+ }
230
+ if (normalizedDuration > 0) timer = setTimeout(dismiss, normalizedDuration);
195
231
  return dismiss;
196
232
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$comment": "@ponchia/ui class vocabulary as language-neutral data — validate emitted markup without executing the ESM cls map or parsing the .d.ts. Generated from classes/index.js — do not edit by hand; run `npm run classes:json:build`. Drift-checked in CI. `groups[].base` is null for a parts-only namespace (no standalone base class — do NOT emit it). A modifier whose name contains `__` (e.g. `ui-spark__bar--pos`) attaches to THAT part, not the base host. `states` is the author-applied `is-*` hooks (runtime/behavior-managed hooks are excluded); `behaviorAttributes` are the `data-bronto-*` wiring hooks the optional behaviors delegate on; `requiredAria` is the role/aria a generator must emit per component. `states` + `customProperties` are documented in docs/reference.md and ship outside `cls` by design.",
3
3
  "counts": {
4
- "classes": 634,
5
- "groups": 177
4
+ "classes": 644,
5
+ "groups": 179
6
6
  },
7
7
  "groups": {
8
8
  "ui-accordion": {
@@ -1081,6 +1081,13 @@
1081
1081
  "ui-segmented__option"
1082
1082
  ]
1083
1083
  },
1084
+ "ui-segmented-buttons": {
1085
+ "base": "ui-segmented-buttons",
1086
+ "modifiers": [],
1087
+ "parts": [
1088
+ "ui-segmented-buttons__button"
1089
+ ]
1090
+ },
1084
1091
  "ui-sel": {
1085
1092
  "base": "ui-sel",
1086
1093
  "modifiers": [
@@ -1437,6 +1444,20 @@
1437
1444
  "modifiers": [],
1438
1445
  "parts": []
1439
1446
  },
1447
+ "ui-toolstrip": {
1448
+ "base": "ui-toolstrip",
1449
+ "modifiers": [
1450
+ "ui-toolstrip--compact",
1451
+ "ui-toolstrip--floating"
1452
+ ],
1453
+ "parts": [
1454
+ "ui-toolstrip__actions",
1455
+ "ui-toolstrip__brand",
1456
+ "ui-toolstrip__context",
1457
+ "ui-toolstrip__group",
1458
+ "ui-toolstrip__search"
1459
+ ]
1460
+ },
1440
1461
  "ui-tooltip": {
1441
1462
  "base": "ui-tooltip",
1442
1463
  "modifiers": [],
@@ -1964,6 +1985,8 @@
1964
1985
  "ui-scroll-reveal",
1965
1986
  "ui-search",
1966
1987
  "ui-segmented",
1988
+ "ui-segmented-buttons",
1989
+ "ui-segmented-buttons__button",
1967
1990
  "ui-segmented__option",
1968
1991
  "ui-sel",
1969
1992
  "ui-sel--maybe",
@@ -2100,6 +2123,14 @@
2100
2123
  "ui-tool-call__name",
2101
2124
  "ui-tool-call__status",
2102
2125
  "ui-tool-log",
2126
+ "ui-toolstrip",
2127
+ "ui-toolstrip--compact",
2128
+ "ui-toolstrip--floating",
2129
+ "ui-toolstrip__actions",
2130
+ "ui-toolstrip__brand",
2131
+ "ui-toolstrip__context",
2132
+ "ui-toolstrip__group",
2133
+ "ui-toolstrip__search",
2103
2134
  "ui-tooltip",
2104
2135
  "ui-tooltip__bubble",
2105
2136
  "ui-tour-note",
@@ -2723,6 +2754,30 @@
2723
2754
  "behavior": "initCommand",
2724
2755
  "note": "wires the filter input + active-option keyboard model"
2725
2756
  },
2757
+ {
2758
+ "name": "data-bronto-carousel-roledescription",
2759
+ "on": "a .ui-carousel host",
2760
+ "behavior": "initCarousel",
2761
+ "note": "localized override for the carousel aria-roledescription; the behavior applies its English default only when this attribute and any authored aria-roledescription are absent."
2762
+ },
2763
+ {
2764
+ "name": "data-bronto-carousel-slide-roledescription",
2765
+ "on": "a .ui-carousel host (applies to each slide)",
2766
+ "behavior": "initCarousel",
2767
+ "note": "localized override for the per-slide aria-roledescription; an authored slide aria-roledescription is preserved and the English \"slide\" default applies only when both are absent."
2768
+ },
2769
+ {
2770
+ "name": "data-bronto-error-summary-title",
2771
+ "on": "a form/error-summary host enhanced by initFormValidation",
2772
+ "behavior": "initFormValidation",
2773
+ "note": "localized heading for the generated validation error summary; an authored title or this attribute is preserved, English is only the fallback."
2774
+ },
2775
+ {
2776
+ "name": "data-bronto-toast-dismiss-label",
2777
+ "on": "the toast host (or via toast() options)",
2778
+ "behavior": "toast",
2779
+ "note": "localized aria-label for the generated toast dismiss button; defaults to the English 'Dismiss' only when absent."
2780
+ },
2726
2781
  {
2727
2782
  "name": "data-bronto-sources",
2728
2783
  "on": "a source/citation island",
@@ -10,7 +10,7 @@ export type ClassValue =
10
10
  | boolean
11
11
  | null
12
12
  | undefined
13
- | ClassValue[];
13
+ | readonly ClassValue[];
14
14
 
15
15
  /** The flat registry of every class @ponchia/ui defines (literal). */
16
16
  export declare const cls: {
@@ -574,6 +574,16 @@ export declare const cls: {
574
574
  readonly inspector: 'ui-inspector';
575
575
  readonly inspectorHead: 'ui-inspector__head';
576
576
  readonly inspectorBody: 'ui-inspector__body';
577
+ readonly toolstrip: 'ui-toolstrip';
578
+ readonly toolstripFloating: 'ui-toolstrip--floating';
579
+ readonly toolstripCompact: 'ui-toolstrip--compact';
580
+ readonly toolstripBrand: 'ui-toolstrip__brand';
581
+ readonly toolstripContext: 'ui-toolstrip__context';
582
+ readonly toolstripGroup: 'ui-toolstrip__group';
583
+ readonly toolstripActions: 'ui-toolstrip__actions';
584
+ readonly toolstripSearch: 'ui-toolstrip__search';
585
+ readonly segmentedButtons: 'ui-segmented-buttons';
586
+ readonly segmentedButtonsButton: 'ui-segmented-buttons__button';
577
587
  readonly property: 'ui-property';
578
588
  readonly propertyLabel: 'ui-property__label';
579
589
  readonly propertyValue: 'ui-property__value';
@@ -651,7 +661,7 @@ export declare const cls: {
651
661
  };
652
662
 
653
663
  /** classnames-style joiner: skips falsy, flattens arrays. */
654
- export declare function cx(...parts: ClassValue[]): string;
664
+ export declare function cx(...parts: readonly ClassValue[]): string;
655
665
 
656
666
  export interface ButtonOpts {
657
667
  variant?: 'ghost' | 'subtle' | 'danger';
@@ -705,6 +715,7 @@ export interface DotgridOpts {
705
715
  export interface TableOpts {
706
716
  density?: 'dense' | 'comfortable';
707
717
  lined?: boolean;
718
+ breakAnywhere?: boolean;
708
719
  }
709
720
  export interface EyebrowOpts {
710
721
  muted?: boolean;
package/classes/index.js CHANGED
@@ -609,10 +609,20 @@ export const cls = Object.freeze({
609
609
  toolCallName: 'ui-tool-call__name',
610
610
  toolCallStatus: 'ui-tool-call__status',
611
611
  toolCallBody: 'ui-tool-call__body',
612
- // workbench — inspector / property / selection bar / splitter (css/workbench.css)
612
+ // workbench — inspector / property / toolstrip / selection bar / splitter (css/workbench.css)
613
613
  inspector: 'ui-inspector',
614
614
  inspectorHead: 'ui-inspector__head',
615
615
  inspectorBody: 'ui-inspector__body',
616
+ toolstrip: 'ui-toolstrip',
617
+ toolstripFloating: 'ui-toolstrip--floating',
618
+ toolstripCompact: 'ui-toolstrip--compact',
619
+ toolstripBrand: 'ui-toolstrip__brand',
620
+ toolstripContext: 'ui-toolstrip__context',
621
+ toolstripGroup: 'ui-toolstrip__group',
622
+ toolstripActions: 'ui-toolstrip__actions',
623
+ toolstripSearch: 'ui-toolstrip__search',
624
+ segmentedButtons: 'ui-segmented-buttons',
625
+ segmentedButtonsButton: 'ui-segmented-buttons__button',
616
626
  property: 'ui-property',
617
627
  propertyLabel: 'ui-property__label',
618
628
  propertyValue: 'ui-property__value',
@@ -693,50 +703,70 @@ export const cls = Object.freeze({
693
703
  themetoggleThumb: 'ui-themetoggle__thumb',
694
704
  });
695
705
 
696
- /** classnames-style joiner: skips falsy, flattens nested arrays of any depth. */
706
+ /**
707
+ * Mirrors clsx's permissive input: `number`/`boolean` accepted so guarded class
708
+ * expressions type-check; readonly arrays/tuples flatten the same as mutable ones.
709
+ * @typedef {string | number | boolean | null | undefined | ReadonlyArray<ClassValue>} ClassValue
710
+ */
711
+
712
+ /**
713
+ * classnames-style joiner: skips falsy, flattens nested arrays of any depth.
714
+ * @param {...ClassValue} parts
715
+ * @returns {string}
716
+ */
697
717
  export function cx(...parts) {
698
718
  const out = [];
699
- for (const p of parts.flat(Infinity)) if (p) out.push(p);
719
+ for (const p of parts.flat(Infinity)) if (p && typeof p !== 'boolean') out.push(p);
700
720
  return out.join(' ');
701
721
  }
702
722
 
703
723
  const j = (...p) => p.filter(Boolean).join(' ');
724
+ const valueClass = (map, value) => (value == null || !Object.hasOwn(map, value) ? '' : map[value]);
704
725
 
705
726
  // Lifecycle state → canonical tone class.
706
727
  const stateTone = (state) =>
707
- ({
708
- saving: cls.stateSaving,
709
- saved: cls.stateSaved,
710
- queued: cls.stateQueued,
711
- offline: cls.stateOffline,
712
- stale: cls.stateStale,
713
- conflict: cls.stateConflict,
714
- error: cls.stateError,
715
- locked: cls.stateLocked,
716
- reviewed: cls.stateReviewed,
717
- 'needs-review': cls.stateNeedsReview,
718
- })[state] || '';
728
+ valueClass(
729
+ {
730
+ saving: cls.stateSaving,
731
+ saved: cls.stateSaved,
732
+ queued: cls.stateQueued,
733
+ offline: cls.stateOffline,
734
+ stale: cls.stateStale,
735
+ conflict: cls.stateConflict,
736
+ error: cls.stateError,
737
+ locked: cls.stateLocked,
738
+ reviewed: cls.stateReviewed,
739
+ 'needs-review': cls.stateNeedsReview,
740
+ },
741
+ state,
742
+ );
719
743
 
720
744
  const jobTone = (state) =>
721
- ({
722
- queued: cls.jobQueued,
723
- running: cls.jobRunning,
724
- blocked: cls.jobBlocked,
725
- failed: cls.jobFailed,
726
- complete: cls.jobComplete,
727
- })[state] || '';
745
+ valueClass(
746
+ {
747
+ queued: cls.jobQueued,
748
+ running: cls.jobRunning,
749
+ blocked: cls.jobBlocked,
750
+ failed: cls.jobFailed,
751
+ complete: cls.jobComplete,
752
+ },
753
+ state,
754
+ );
728
755
 
729
756
  // Trust-state → tone class, shared by the source/citation/provenance recipes.
730
- // Object-literal lookup to match stateTone above (shorter, greppable, one idiom).
757
+ // Own-property object-literal lookup to match stateTone above.
731
758
  const srcTone = (state) =>
732
- ({
733
- verified: cls.srcVerified,
734
- unverified: cls.srcUnverified,
735
- generated: cls.srcGenerated,
736
- reviewed: cls.srcReviewed,
737
- stale: cls.srcStale,
738
- conflict: cls.srcConflict,
739
- })[state] || '';
759
+ valueClass(
760
+ {
761
+ verified: cls.srcVerified,
762
+ unverified: cls.srcUnverified,
763
+ generated: cls.srcGenerated,
764
+ reviewed: cls.srcReviewed,
765
+ stale: cls.srcStale,
766
+ conflict: cls.srcConflict,
767
+ },
768
+ state,
769
+ );
740
770
 
741
771
  // Component tone → modifier class. Same object-literal idiom as srcTone/stateTone
742
772
  // while keeping modifier classes grep-friendly.
@@ -749,13 +779,13 @@ const srcTone = (state) =>
749
779
  // is fine and returns no modifier.
750
780
  const toneClass = (component, map, tone) => {
751
781
  if (tone == null) return '';
752
- const hit = map[tone];
782
+ const hit = valueClass(map, tone);
753
783
  if (!hit && typeof console !== 'undefined') {
754
784
  console.warn(
755
785
  `[bronto] ui.${component}(): "${tone}" is not a ${component} tone (use one of: ${Object.keys(map).join(', ')}).`,
756
786
  );
757
787
  }
758
- return hit || '';
788
+ return hit;
759
789
  };
760
790
 
761
791
  const badgeTone = (tone) =>
@@ -852,8 +882,6 @@ const claimStatus = (status) =>
852
882
  status,
853
883
  );
854
884
 
855
- const valueClass = (map, value) => (value == null ? '' : map[value] || '');
856
-
857
885
  const annotationVariants = Object.freeze({
858
886
  label: cls.annotationLabelVariant,
859
887
  callout: cls.annotationCallout,
@@ -889,6 +917,13 @@ const annotationMotions = Object.freeze({
889
917
  focus: cls.annotationFocus,
890
918
  });
891
919
 
920
+ /**
921
+ * @typedef {object} TableOpts
922
+ * @property {'dense' | 'comfortable'} [density] Density modifier.
923
+ * @property {boolean} [lined] Add row separator lines.
924
+ * @property {boolean} [breakAnywhere] Allow long unspaced cell content to wrap instead of overflowing.
925
+ */
926
+
892
927
  export const ui = {
893
928
  button: ({ variant, icon, size } = {}) =>
894
929
  j(
@@ -920,6 +955,7 @@ export const ui = {
920
955
  dot: ({ tone, live } = {}) => j(cls.dot, dotTone(tone), live && cls.dotLive),
921
956
  dotgrid: ({ accent, dense } = {}) =>
922
957
  j(cls.dotgrid, accent && cls.dotgridAccent, dense && cls.dotgridDense),
958
+ /** @type {(opts?: TableOpts) => string} */
923
959
  table: ({ density, lined, breakAnywhere } = {}) =>
924
960
  j(
925
961
  cls.table,
@@ -1068,9 +1104,19 @@ export const ui = {
1068
1104
  // unknown; a meter is never indeterminate so it passes false. (Kept a boolean
1069
1105
  // flag rather than testing the role string, so check:recipe-types doesn't read it
1070
1106
  // as a recipe option literal.)
1071
- const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1107
+ const valueAttrs = (component, role, value, min, max, busyWhenIndeterminate) => {
1072
1108
  const lo = Number(min);
1073
1109
  const hi = Number(max);
1110
+ const validRange = Number.isFinite(lo) && Number.isFinite(hi) && hi > lo;
1111
+ if (!validRange) {
1112
+ if (typeof console !== 'undefined') {
1113
+ console.warn(
1114
+ `[bronto] attrs.${component}(): invalid range (expected finite min/max with max > min); omitting value ARIA.`,
1115
+ );
1116
+ }
1117
+ return busyWhenIndeterminate ? { role, 'aria-busy': 'true' } : { role };
1118
+ }
1119
+
1074
1120
  const raw = Number(value);
1075
1121
  // Indeterminate: an omitted/unknown value (attrs.progress() with no argument).
1076
1122
  // ARIA requires aria-valuenow be OMITTED here — emitting 0 announces "0%",
@@ -1083,7 +1129,7 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1083
1129
  return busyWhenIndeterminate ? { role, 'aria-busy': 'true' } : { role };
1084
1130
  }
1085
1131
  const now = Math.min(hi, Math.max(lo, raw));
1086
- const pct = hi > lo ? ((now - lo) / (hi - lo)) * 100 : 0;
1132
+ const pct = ((now - lo) / (hi - lo)) * 100;
1087
1133
  return {
1088
1134
  role,
1089
1135
  'aria-valuenow': now,
@@ -1109,10 +1155,12 @@ const valueAttrs = (role, value, min, max, busyWhenIndeterminate) => {
1109
1155
  * call with no value for the indeterminate sweep.
1110
1156
  */
1111
1157
  export const attrs = Object.freeze({
1112
- meter: (value, { min = 0, max = 100 } = {}) => valueAttrs('meter', value, min, max, false),
1158
+ meter: (value, { min = 0, max = 100 } = {}) =>
1159
+ valueAttrs('meter', 'meter', value, min, max, false),
1113
1160
  progress: (value, { min = 0, max = 100 } = {}) =>
1114
- valueAttrs('progressbar', value, min, max, true),
1115
- dotbar: (value, { min = 0, max = 100 } = {}) => valueAttrs('progressbar', value, min, max, true),
1161
+ valueAttrs('progress', 'progressbar', value, min, max, true),
1162
+ dotbar: (value, { min = 0, max = 100 } = {}) =>
1163
+ valueAttrs('dotbar', 'progressbar', value, min, max, true),
1116
1164
  });
1117
1165
 
1118
1166
  export default ui;
@@ -80,10 +80,10 @@ export function curvePath(from: Point, to: Point, opts?: {
80
80
  }): string;
81
81
  /**
82
82
  * Build a path between two points by `shape` (`straight` | `elbow` | `curve`).
83
- * @param {ConnectorPathOptions} [opts]
83
+ * @param {ConnectorPathOptions} opts
84
84
  * @returns {string}
85
85
  */
86
- export function connectorPath(opts?: ConnectorPathOptions): string;
86
+ export function connectorPath(opts: ConnectorPathOptions): string;
87
87
  /**
88
88
  * A filled triangle arrowhead at `p`, pointing along `angle` (radians).
89
89
  * @param {Point} p
@@ -136,10 +136,10 @@ export function endTangentAngle(from: Point, to: Point, shape?: ConnectorShape):
136
136
  * Connect two rects. Resolves anchor points (explicit `fromSide`/`toSide`, else
137
137
  * auto), builds the path, and returns `{ d, from, to, angle }` so the caller can
138
138
  * place an arrowhead/dot at `to` rotated by `angle`.
139
- * @param {ConnectRectsOptions} [opts]
139
+ * @param {ConnectRectsOptions} opts
140
140
  * @returns {ConnectRectsResult}
141
141
  */
142
- export function connectRects(opts?: ConnectRectsOptions): ConnectRectsResult;
142
+ export function connectRects(opts: ConnectRectsOptions): ConnectRectsResult;
143
143
  /**
144
144
  * @ponchia/ui/connectors — dependency-free SVG geometry for connecting two
145
145
  * elements (or two points) with a leader line.
@@ -1 +1 @@
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"}
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,oCAHW,oBAAoB,GAClB,MAAM,CASlB;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,mCAHW,mBAAmB,GACjB,kBAAkB,CAgB9B;AAhWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}