@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
@@ -1,5 +1,196 @@
1
1
  import { hasDom, resolveHost, noop, bindOnce, nextFieldUid, collectHosts } from './internal.js';
2
2
 
3
+ function snapshotAttrs(el, names) {
4
+ const out = {};
5
+ for (const name of names) {
6
+ out[name] = {
7
+ had: el.hasAttribute(name),
8
+ value: el.getAttribute(name),
9
+ };
10
+ }
11
+ return out;
12
+ }
13
+
14
+ function restoreAttrs(el, attrs) {
15
+ for (const [name, attr] of Object.entries(attrs)) {
16
+ if (attr.had) el.setAttribute(name, attr.value);
17
+ else el.removeAttribute(name);
18
+ }
19
+ }
20
+
21
+ function createValidationState() {
22
+ return {
23
+ priorNoValidate: new Map(),
24
+ controlState: new Map(),
25
+ slotState: new Map(),
26
+ summaryState: new Map(),
27
+ // Borrowed `.ui-hint` help text is restored after an invalid field becomes valid.
28
+ hintHelp: new WeakMap(),
29
+ };
30
+ }
31
+
32
+ function suppressNativeValidation(form, state) {
33
+ if (!state.priorNoValidate.has(form)) state.priorNoValidate.set(form, form.noValidate);
34
+ form.noValidate = true;
35
+ }
36
+
37
+ function rememberControl(control, state) {
38
+ if (!state.controlState.has(control)) {
39
+ state.controlState.set(
40
+ control,
41
+ snapshotAttrs(control, ['aria-invalid', 'aria-describedby', 'id']),
42
+ );
43
+ }
44
+ }
45
+
46
+ function rememberSlot(slot, state) {
47
+ if (!state.slotState.has(slot)) {
48
+ state.slotState.set(slot, {
49
+ attrs: snapshotAttrs(slot, ['id']),
50
+ text: slot.textContent,
51
+ hadErrorClass: slot.classList.contains('ui-hint--error'),
52
+ });
53
+ }
54
+ }
55
+
56
+ function rememberSummary(summary, state) {
57
+ if (!state.summaryState.has(summary)) {
58
+ state.summaryState.set(summary, {
59
+ attrs: snapshotAttrs(summary, ['role', 'tabindex']),
60
+ children: [...summary.childNodes],
61
+ hidden: summary.hidden,
62
+ });
63
+ }
64
+ }
65
+
66
+ function ensureId(el, prefix) {
67
+ if (!el.id) el.id = `${prefix}-${nextFieldUid()}`;
68
+ return el.id;
69
+ }
70
+
71
+ function slotFor(control) {
72
+ const field = control.closest('.ui-field');
73
+ if (!field) return null;
74
+ const dedicated = field.querySelector('[data-bronto-error]');
75
+ if (dedicated) return dedicated;
76
+ return field.querySelector('.ui-hint');
77
+ }
78
+
79
+ function link(control, slot) {
80
+ const slotId = ensureId(slot, 'bronto-err');
81
+ const ids = (control.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
82
+ if (!ids.includes(slotId)) {
83
+ ids.push(slotId);
84
+ control.setAttribute('aria-describedby', ids.join(' '));
85
+ }
86
+ }
87
+
88
+ function unlink(control, slot) {
89
+ if (!slot.id) return;
90
+ const ids = (control.getAttribute('aria-describedby') || '')
91
+ .split(/\s+/)
92
+ .filter((id) => id && id !== slot.id);
93
+ if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));
94
+ else control.removeAttribute('aria-describedby');
95
+ }
96
+
97
+ function slotKind(slot) {
98
+ // Decide the slot TYPE by `[data-bronto-error]`, not by `.ui-hint`: canonical
99
+ // dedicated error markup can carry both.
100
+ const dedicated = !!slot?.matches?.('[data-bronto-error]');
101
+ const hasHintClass = !!slot?.classList.contains('ui-hint');
102
+ return { hasHintClass, borrowedHint: hasHintClass && !dedicated };
103
+ }
104
+
105
+ function clearFieldError(control, slot, kind, state) {
106
+ control.removeAttribute('aria-invalid');
107
+ if (!slot) return;
108
+ if (kind.hasHintClass) slot.classList.remove('ui-hint--error');
109
+ if (kind.borrowedHint) {
110
+ slot.textContent = state.hintHelp.get(slot) ?? '';
111
+ return;
112
+ }
113
+ slot.textContent = '';
114
+ unlink(control, slot);
115
+ }
116
+
117
+ function showFieldError(control, slot, kind, state) {
118
+ control.setAttribute('aria-invalid', 'true');
119
+ if (!slot) return;
120
+ if (kind.borrowedHint && !state.hintHelp.has(slot)) state.hintHelp.set(slot, slot.textContent);
121
+ slot.textContent = control.validationMessage;
122
+ if (kind.hasHintClass) slot.classList.add('ui-hint--error');
123
+ link(control, slot);
124
+ }
125
+
126
+ function validateField(control, state) {
127
+ if (!control.willValidate) return true;
128
+ rememberControl(control, state);
129
+ const ok = control.validity.valid;
130
+ const slot = slotFor(control);
131
+ if (slot) rememberSlot(slot, state);
132
+ const kind = slotKind(slot);
133
+ if (ok) clearFieldError(control, slot, kind, state);
134
+ else showFieldError(control, slot, kind, state);
135
+ return ok;
136
+ }
137
+
138
+ function controlsOf(form) {
139
+ return [...form.elements].filter(
140
+ (el) => el.willValidate && el.type !== 'submit' && el.type !== 'button',
141
+ );
142
+ }
143
+
144
+ function summaryItem(control) {
145
+ const id = ensureId(control, 'bronto-field');
146
+ const li = document.createElement('li');
147
+ const a = document.createElement('a');
148
+ a.href = `#${id}`;
149
+ a.textContent = control.validationMessage;
150
+ a.addEventListener('click', (e) => {
151
+ e.preventDefault();
152
+ control.focus();
153
+ });
154
+ li.appendChild(a);
155
+ return li;
156
+ }
157
+
158
+ function refreshSummary(form, invalid, state) {
159
+ const summary = form.querySelector('[data-bronto-error-summary]');
160
+ if (!summary) return;
161
+ rememberSummary(summary, state);
162
+ if (!invalid.length) {
163
+ summary.hidden = true;
164
+ summary.replaceChildren();
165
+ return;
166
+ }
167
+ const title = document.createElement('p');
168
+ title.className = 'ui-error-summary__title';
169
+ title.textContent = 'There is a problem';
170
+ const list = document.createElement('ul');
171
+ list.className = 'ui-error-summary__list';
172
+ list.append(...invalid.map(summaryItem));
173
+ summary.replaceChildren(title, list);
174
+ summary.setAttribute('role', 'alert');
175
+ summary.tabIndex = -1;
176
+ summary.hidden = false;
177
+ }
178
+
179
+ function restoreValidationState(state) {
180
+ for (const [form, noValidate] of state.priorNoValidate) form.noValidate = noValidate;
181
+ for (const [summary, snapshot] of state.summaryState) {
182
+ summary.replaceChildren(...snapshot.children);
183
+ summary.hidden = snapshot.hidden;
184
+ restoreAttrs(summary, snapshot.attrs);
185
+ }
186
+ for (const [slot, snapshot] of state.slotState) {
187
+ slot.textContent = snapshot.text;
188
+ slot.classList.toggle('ui-hint--error', snapshot.hadErrorClass);
189
+ restoreAttrs(slot, snapshot.attrs);
190
+ }
191
+ for (const [control, attrs] of state.controlState) restoreAttrs(control, attrs);
192
+ }
193
+
3
194
  /**
4
195
  * Accessible form validation glue for `<form data-bronto-validate>`.
5
196
  * Progressive enhancement over the native Constraint Validation API —
@@ -29,156 +220,36 @@ export function initFormValidation({ root } = {}) {
29
220
  if (!hasDom()) return noop;
30
221
  const host = resolveHost(root);
31
222
  if (!host) return noop;
32
- let priorNoValidate = new Map();
33
-
34
- const suppressNativeValidation = (form) => {
35
- if (!priorNoValidate.has(form)) priorNoValidate.set(form, form.noValidate);
36
- form.noValidate = true;
37
- };
38
-
39
- const ensureId = (el, prefix) => {
40
- if (!el.id) el.id = `${prefix}-${nextFieldUid()}`;
41
- return el.id;
42
- };
43
-
44
- // When the field has no dedicated `[data-bronto-error]` node we fall back to
45
- // the shared `.ui-hint` help slot. Snapshot its original help text the first
46
- // time we overwrite it with an error, so the valid branch can RESTORE the help
47
- // rather than blanking it permanently (component-audit C8).
48
- const hintHelp = new WeakMap();
49
-
50
- const slotFor = (control) => {
51
- const field = control.closest('.ui-field');
52
- if (!field) return null;
53
- const dedicated = field.querySelector('[data-bronto-error]');
54
- if (dedicated) return dedicated;
55
- return field.querySelector('.ui-hint');
56
- };
223
+ let state = createValidationState();
57
224
 
58
- const link = (control, slot) => {
59
- const slotId = ensureId(slot, 'bronto-err');
60
- const ids = (control.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
61
- if (!ids.includes(slotId)) {
62
- ids.push(slotId);
63
- control.setAttribute('aria-describedby', ids.join(' '));
64
- }
65
- };
66
-
67
- const unlink = (control, slot) => {
68
- if (!slot.id) return;
69
- const ids = (control.getAttribute('aria-describedby') || '')
70
- .split(/\s+/)
71
- .filter((id) => id && id !== slot.id);
72
- if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));
73
- else control.removeAttribute('aria-describedby');
74
- };
75
-
76
- const validateField = (control) => {
77
- if (!control.willValidate) return true;
78
- const ok = control.validity.valid;
79
- const slot = slotFor(control);
80
- // Decide the slot TYPE by the `[data-bronto-error]` attribute, NOT the
81
- // `.ui-hint` class: the canonical markup is `<p class="ui-hint"
82
- // data-bronto-error>`, which carries BOTH. Keying off `.ui-hint` sent that
83
- // dedicated error node down the help-hint branch, which never unlink()s — so
84
- // the field kept a dangling aria-describedby to an empty error node after it
85
- // was fixed (component-audit C6). Only a *borrowed* plain `.ui-hint` (a help
86
- // slot with no dedicated error node) snapshots/restores its help text and
87
- // stays linked in the valid state.
88
- const dedicated = !!slot?.matches?.('[data-bronto-error]');
89
- const hasHintClass = !!slot?.classList.contains('ui-hint');
90
- const borrowedHint = hasHintClass && !dedicated;
91
- if (ok) {
92
- control.removeAttribute('aria-invalid');
93
- if (slot) {
94
- if (hasHintClass) slot.classList.remove('ui-hint--error');
95
- if (borrowedHint) {
96
- // Restore the snapshotted help text (or clear if there was none); a
97
- // help-bearing hint stays linked via aria-describedby (it describes
98
- // the field in the valid state too).
99
- slot.textContent = hintHelp.get(slot) ?? '';
100
- } else {
101
- // Dedicated error node: clear it and drop the now-stale describedby
102
- // so AT doesn't announce an empty error association.
103
- slot.textContent = '';
104
- unlink(control, slot);
105
- }
106
- }
107
- } else {
108
- control.setAttribute('aria-invalid', 'true');
109
- if (slot) {
110
- if (borrowedHint && !hintHelp.has(slot)) hintHelp.set(slot, slot.textContent);
111
- slot.textContent = control.validationMessage;
112
- if (hasHintClass) slot.classList.add('ui-hint--error');
113
- link(control, slot);
114
- }
115
- }
116
- return ok;
117
- };
118
-
119
- const controlsOf = (form) =>
120
- [...form.elements].filter(
121
- (el) => el.willValidate && el.type !== 'submit' && el.type !== 'button',
122
- );
123
-
124
- const refreshSummary = (form, invalid) => {
125
- const summary = form.querySelector('[data-bronto-error-summary]');
126
- if (!summary) return;
127
- if (!invalid.length) {
128
- summary.hidden = true;
129
- summary.replaceChildren();
130
- return;
131
- }
132
- const title = document.createElement('p');
133
- title.className = 'ui-error-summary__title';
134
- title.textContent = 'There is a problem';
135
- const list = document.createElement('ul');
136
- list.className = 'ui-error-summary__list';
137
- for (const c of invalid) {
138
- const id = ensureId(c, 'bronto-field');
139
- const li = document.createElement('li');
140
- const a = document.createElement('a');
141
- a.href = `#${id}`;
142
- a.textContent = c.validationMessage;
143
- a.addEventListener('click', (e) => {
144
- e.preventDefault();
145
- c.focus();
146
- });
147
- li.appendChild(a);
148
- list.appendChild(li);
149
- }
150
- summary.replaceChildren(title, list);
151
- summary.setAttribute('role', 'alert');
152
- summary.tabIndex = -1;
153
- summary.hidden = false;
154
- };
155
-
156
- const onSubmit = (e) => {
157
- const form = e.target.closest?.('[data-bronto-validate]');
225
+ const onSubmit = (event) => {
226
+ const form = event.target.closest?.('[data-bronto-validate]');
158
227
  if (!form) return;
159
- suppressNativeValidation(form);
160
- const invalid = controlsOf(form).filter((c) => !validateField(c));
161
- refreshSummary(form, invalid);
228
+ suppressNativeValidation(form, state);
229
+ const invalid = controlsOf(form).filter((control) => !validateField(control, state));
230
+ refreshSummary(form, invalid, state);
162
231
  if (invalid.length) {
163
- e.preventDefault();
232
+ event.preventDefault();
164
233
  const summary = form.querySelector('[data-bronto-error-summary]');
165
234
  (summary && !summary.hidden ? summary : invalid[0]).focus();
166
235
  }
167
236
  };
168
237
 
169
- const onBlur = (e) => {
170
- const control = e.target;
238
+ const onBlur = (event) => {
239
+ const control = event.target;
171
240
  if (!control.willValidate) return;
172
241
  const form = control.closest?.('[data-bronto-validate]');
173
242
  if (!form) return;
174
- suppressNativeValidation(form);
175
- validateField(control);
243
+ suppressNativeValidation(form, state);
244
+ validateField(control, state);
176
245
  const summary = form.querySelector('[data-bronto-error-summary]');
177
- if (summary && !summary.hidden)
246
+ if (summary && !summary.hidden) {
178
247
  refreshSummary(
179
248
  form,
180
- controlsOf(form).filter((c) => !c.validity.valid),
249
+ controlsOf(form).filter((candidate) => !candidate.validity.valid),
250
+ state,
181
251
  );
252
+ }
182
253
  };
183
254
 
184
255
  return bindOnce(host, 'formValidation', () => {
@@ -189,17 +260,17 @@ export function initFormValidation({ root } = {}) {
189
260
  // summary — contradicting the documented contract. (Forms added
190
261
  // after init are still covered by the in-handler set.)
191
262
  const forms = collectHosts(host, '[data-bronto-validate]');
192
- priorNoValidate = new Map();
193
- for (const f of forms) {
194
- suppressNativeValidation(f);
263
+ state = createValidationState();
264
+ for (const form of forms) {
265
+ suppressNativeValidation(form, state);
195
266
  }
196
267
  host.addEventListener('submit', onSubmit, true);
197
268
  host.addEventListener('focusout', onBlur);
198
269
  return () => {
199
270
  host.removeEventListener('submit', onSubmit, true);
200
271
  host.removeEventListener('focusout', onBlur);
201
- for (const [f, v] of priorNoValidate) f.noValidate = v;
202
- priorNoValidate.clear();
272
+ restoreValidationState(state);
273
+ state = createValidationState();
203
274
  };
204
275
  });
205
276
  }
@@ -1 +1 @@
1
- {"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAyJ3C"}
1
+ {"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"AAwLA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAwB3C"}