@ponchia/ui 0.6.7 → 0.6.8
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.
- package/CHANGELOG.md +70 -0
- package/README.md +3 -3
- package/annotations/index.d.ts.map +1 -1
- package/annotations/index.js +21 -3
- package/behaviors/carousel.d.ts.map +1 -1
- package/behaviors/carousel.js +91 -35
- package/behaviors/combobox.d.ts.map +1 -1
- package/behaviors/combobox.js +117 -43
- package/behaviors/command.d.ts.map +1 -1
- package/behaviors/command.js +74 -14
- package/behaviors/connectors.d.ts.map +1 -1
- package/behaviors/connectors.js +92 -9
- package/behaviors/crosshair.d.ts.map +1 -1
- package/behaviors/crosshair.js +47 -1
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +24 -9
- package/behaviors/disclosure.d.ts.map +1 -1
- package/behaviors/disclosure.js +33 -3
- package/behaviors/dismissible.d.ts.map +1 -1
- package/behaviors/dismissible.js +3 -2
- package/behaviors/forms.d.ts.map +1 -1
- package/behaviors/forms.js +67 -0
- package/behaviors/glyph.d.ts.map +1 -1
- package/behaviors/glyph.js +17 -2
- package/behaviors/inert.js +3 -2
- package/behaviors/internal.d.ts.map +1 -1
- package/behaviors/internal.js +2 -1
- package/behaviors/legend.d.ts +0 -5
- package/behaviors/legend.d.ts.map +1 -1
- package/behaviors/legend.js +45 -13
- package/behaviors/menu.d.ts.map +1 -1
- package/behaviors/menu.js +13 -8
- package/behaviors/modal.d.ts.map +1 -1
- package/behaviors/modal.js +77 -19
- package/behaviors/popover.d.ts +4 -3
- package/behaviors/popover.d.ts.map +1 -1
- package/behaviors/popover.js +89 -9
- package/behaviors/sources.d.ts.map +1 -1
- package/behaviors/sources.js +14 -2
- package/behaviors/splitter.d.ts.map +1 -1
- package/behaviors/splitter.js +149 -110
- package/behaviors/spotlight.d.ts.map +1 -1
- package/behaviors/spotlight.js +28 -2
- package/behaviors/table.d.ts.map +1 -1
- package/behaviors/table.js +103 -11
- package/behaviors/tabs.d.ts.map +1 -1
- package/behaviors/tabs.js +82 -18
- package/behaviors/theme.d.ts.map +1 -1
- package/behaviors/theme.js +25 -5
- package/classes/index.d.ts +15 -2
- package/classes/index.js +0 -1
- package/connectors/index.d.ts +39 -6
- package/connectors/index.d.ts.map +1 -1
- package/connectors/index.js +67 -9
- package/css/annotations.css +12 -0
- package/css/crosshair.css +27 -2
- package/css/feedback.css +2 -30
- package/css/navigation.css +12 -0
- package/css/tokens.css +16 -0
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/annotations.css +1 -1
- package/dist/css/crosshair.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/navigation.css +1 -1
- package/dist/css/report-kit.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/docs/adr/0001-color-system.md +3 -2
- package/docs/annotations.md +12 -1
- package/docs/architecture.md +46 -13
- package/docs/command.md +4 -1
- package/docs/connectors.md +16 -0
- package/docs/crosshair.md +1 -1
- package/docs/dots.md +4 -1
- package/docs/glyphs.md +11 -0
- package/docs/migrations/0.2-to-0.3.md +1 -1
- package/docs/package-contract.md +5 -5
- package/docs/reporting.md +23 -12
- package/docs/stability.md +18 -2
- package/docs/theming.md +2 -2
- package/docs/usage.md +16 -2
- package/docs/vega.md +4 -4
- package/llms.txt +10 -5
- package/package.json +20 -4
- package/svelte/index.d.ts +71 -45
- package/svelte/index.d.ts.map +1 -1
- package/svelte/index.js +29 -2
- package/vue/index.d.ts +42 -5
- package/vue/index.d.ts.map +1 -1
- package/vue/index.js +32 -1
package/behaviors/command.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
collectHosts,
|
|
8
8
|
scrollIntoViewSafe,
|
|
9
9
|
wrapIndex,
|
|
10
|
+
closestSafe,
|
|
10
11
|
} from './internal.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -46,6 +47,24 @@ export function initCommand({ root } = {}) {
|
|
|
46
47
|
const palettes = collectHosts(host, '[data-bronto-command]');
|
|
47
48
|
const cleanups = [];
|
|
48
49
|
|
|
50
|
+
const snapshotAttrs = (el, names) => {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const name of names) {
|
|
53
|
+
out[name] = {
|
|
54
|
+
had: el.hasAttribute(name),
|
|
55
|
+
value: el.getAttribute(name),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const restoreAttrs = (el, attrs) => {
|
|
62
|
+
for (const [name, attr] of Object.entries(attrs)) {
|
|
63
|
+
if (attr.had) el.setAttribute(name, attr.value);
|
|
64
|
+
else el.removeAttribute(name);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
49
68
|
for (const box of palettes) {
|
|
50
69
|
const input = box.querySelector('.ui-command__input, input');
|
|
51
70
|
const list = box.querySelector('.ui-command__list, [role="listbox"]');
|
|
@@ -54,18 +73,44 @@ export function initCommand({ root } = {}) {
|
|
|
54
73
|
const items = [...list.querySelectorAll('.ui-command__item, [role="option"]')];
|
|
55
74
|
const groups = [...list.querySelectorAll('.ui-command__group')];
|
|
56
75
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
const rememberState = () => ({
|
|
77
|
+
input: snapshotAttrs(input, [
|
|
78
|
+
'role',
|
|
79
|
+
'aria-controls',
|
|
80
|
+
'aria-autocomplete',
|
|
81
|
+
'aria-expanded',
|
|
82
|
+
'aria-activedescendant',
|
|
83
|
+
'autocomplete',
|
|
84
|
+
]),
|
|
85
|
+
list: snapshotAttrs(list, ['id', 'role']),
|
|
86
|
+
empty: empty ? { hidden: empty.hidden } : null,
|
|
87
|
+
groups: groups.map((g) => ({
|
|
88
|
+
el: g,
|
|
89
|
+
hidden: g.hidden,
|
|
90
|
+
attrs: snapshotAttrs(g, ['role']),
|
|
91
|
+
})),
|
|
92
|
+
items: items.map((it) => ({
|
|
93
|
+
el: it,
|
|
94
|
+
hidden: it.hidden,
|
|
95
|
+
active: it.classList.contains('is-active'),
|
|
96
|
+
attrs: snapshotAttrs(it, ['id', 'role', 'aria-selected']),
|
|
97
|
+
})),
|
|
61
98
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
|
|
100
|
+
const restoreState = (state) => {
|
|
101
|
+
restoreAttrs(input, state.input);
|
|
102
|
+
restoreAttrs(list, state.list);
|
|
103
|
+
if (empty && state.empty) empty.hidden = state.empty.hidden;
|
|
104
|
+
for (const group of state.groups) {
|
|
105
|
+
group.el.hidden = group.hidden;
|
|
106
|
+
restoreAttrs(group.el, group.attrs);
|
|
107
|
+
}
|
|
108
|
+
for (const item of state.items) {
|
|
109
|
+
item.el.hidden = item.hidden;
|
|
110
|
+
item.el.classList.toggle('is-active', item.active);
|
|
111
|
+
restoreAttrs(item.el, item.attrs);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
69
114
|
|
|
70
115
|
let active = -1;
|
|
71
116
|
const visible = () => items.filter((it) => !it.hidden);
|
|
@@ -176,22 +221,37 @@ export function initCommand({ root } = {}) {
|
|
|
176
221
|
}
|
|
177
222
|
};
|
|
178
223
|
const onClick = (e) => {
|
|
179
|
-
const item = e.target
|
|
224
|
+
const item = closestSafe(e.target, '.ui-command__item, [role="option"]');
|
|
180
225
|
if (item && list.contains(item)) choose(item);
|
|
181
226
|
};
|
|
182
227
|
|
|
183
228
|
const bound = bindOnce(box, 'command', () => {
|
|
229
|
+
const state = rememberState();
|
|
230
|
+
const listId = list.id || (list.id = `bronto-cmd-${nextFieldUid()}`);
|
|
231
|
+
items.forEach((it, i) => {
|
|
232
|
+
if (!it.id) it.id = `${listId}-opt-${i}`;
|
|
233
|
+
it.setAttribute('role', 'option');
|
|
234
|
+
});
|
|
235
|
+
groups.forEach((g) => g.setAttribute('role', 'presentation'));
|
|
236
|
+
list.setAttribute('role', 'listbox');
|
|
237
|
+
input.setAttribute('role', 'combobox');
|
|
238
|
+
input.setAttribute('aria-controls', listId);
|
|
239
|
+
input.setAttribute('aria-autocomplete', 'list');
|
|
240
|
+
input.setAttribute('aria-expanded', 'true');
|
|
241
|
+
input.setAttribute('autocomplete', 'off');
|
|
184
242
|
input.addEventListener('input', onInput);
|
|
185
243
|
input.addEventListener('keydown', onKey);
|
|
186
244
|
list.addEventListener('click', onClick);
|
|
245
|
+
// Seed the initial active item (first visible).
|
|
246
|
+
filter();
|
|
187
247
|
return () => {
|
|
188
248
|
input.removeEventListener('input', onInput);
|
|
189
249
|
input.removeEventListener('keydown', onKey);
|
|
190
250
|
list.removeEventListener('click', onClick);
|
|
251
|
+
restoreState(state);
|
|
252
|
+
active = -1;
|
|
191
253
|
};
|
|
192
254
|
});
|
|
193
|
-
// Seed the initial active item (first visible).
|
|
194
|
-
filter();
|
|
195
255
|
cleanups.push(bound);
|
|
196
256
|
}
|
|
197
257
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["connectors.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["connectors.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;;;;;GAcG;AACH,0CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA4H3C"}
|
package/behaviors/connectors.js
CHANGED
|
@@ -3,6 +3,44 @@ import { connectRects, arrowHead, dotMark } from '../connectors/index.js';
|
|
|
3
3
|
|
|
4
4
|
const SVGNS = 'http://www.w3.org/2000/svg';
|
|
5
5
|
|
|
6
|
+
const snapshotAttrs = (el) =>
|
|
7
|
+
Array.from(el.attributes, ({ name, value }) => ({
|
|
8
|
+
name,
|
|
9
|
+
value,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const restoreAttrs = (el, attrs) => {
|
|
13
|
+
for (const { name } of Array.from(el.attributes)) el.removeAttribute(name);
|
|
14
|
+
for (const { name, value } of attrs) el.setAttribute(name, value);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const snapshotPart = (svg, selector) => {
|
|
18
|
+
const node = svg.querySelector(selector);
|
|
19
|
+
if (!node) return { node: null };
|
|
20
|
+
return {
|
|
21
|
+
node,
|
|
22
|
+
attrs: snapshotAttrs(node),
|
|
23
|
+
parent: node.parentNode,
|
|
24
|
+
nextSibling: node.nextSibling,
|
|
25
|
+
textContent: node.textContent,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const restorePart = (svg, selector, state) => {
|
|
30
|
+
if (!state.node) {
|
|
31
|
+
svg.querySelector(selector)?.remove();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const parent = state.parent || svg;
|
|
36
|
+
if (state.node.parentNode !== parent) {
|
|
37
|
+
const before = state.nextSibling?.parentNode === parent ? state.nextSibling : null;
|
|
38
|
+
parent.insertBefore(state.node, before);
|
|
39
|
+
}
|
|
40
|
+
restoreAttrs(state.node, state.attrs);
|
|
41
|
+
state.node.textContent = state.textContent;
|
|
42
|
+
};
|
|
43
|
+
|
|
6
44
|
/**
|
|
7
45
|
* Draw + keep leader lines in sync. Each `[data-bronto-connector]` is an
|
|
8
46
|
* `.ui-connector` SVG overlaying a positioned container; `data-from`/`data-to`
|
|
@@ -22,26 +60,60 @@ export function initConnectors({ root } = {}) {
|
|
|
22
60
|
if (!hasDom()) return noop;
|
|
23
61
|
const host = resolveHost(root);
|
|
24
62
|
if (!host) return noop;
|
|
25
|
-
|
|
26
|
-
|
|
63
|
+
|
|
64
|
+
const fallbackRect = (svg, el) => {
|
|
65
|
+
const origin = svg.getBoundingClientRect();
|
|
66
|
+
const r = el.getBoundingClientRect();
|
|
67
|
+
const scaleX = origin.width ? (svg.clientWidth || origin.width) / origin.width : 1;
|
|
68
|
+
const scaleY = origin.height ? (svg.clientHeight || origin.height) / origin.height : 1;
|
|
69
|
+
return {
|
|
70
|
+
x: (r.left - origin.left) * scaleX,
|
|
71
|
+
y: (r.top - origin.top) * scaleY,
|
|
72
|
+
width: r.width * scaleX,
|
|
73
|
+
height: r.height * scaleY,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const rectInSvg = (svg, el) => {
|
|
78
|
+
const r = el.getBoundingClientRect();
|
|
79
|
+
const matrix = svg.getScreenCTM?.();
|
|
80
|
+
const view = svg.ownerDocument?.defaultView;
|
|
81
|
+
const Point = view?.DOMPoint;
|
|
82
|
+
if (!matrix || !Point) return fallbackRect(svg, el);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const inverse = matrix.inverse();
|
|
86
|
+
const corners = [
|
|
87
|
+
new Point(r.left, r.top),
|
|
88
|
+
new Point(r.right, r.top),
|
|
89
|
+
new Point(r.right, r.bottom),
|
|
90
|
+
new Point(r.left, r.bottom),
|
|
91
|
+
].map((point) => point.matrixTransform(inverse));
|
|
92
|
+
const xs = corners.map((point) => point.x);
|
|
93
|
+
const ys = corners.map((point) => point.y);
|
|
94
|
+
const left = Math.min(...xs);
|
|
95
|
+
const right = Math.max(...xs);
|
|
96
|
+
const top = Math.min(...ys);
|
|
97
|
+
const bottom = Math.max(...ys);
|
|
98
|
+
return { x: left, y: top, width: right - left, height: bottom - top };
|
|
99
|
+
} catch {
|
|
100
|
+
return fallbackRect(svg, el);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
27
103
|
|
|
28
104
|
const draw = () => {
|
|
105
|
+
const connectors = collectHosts(host, '[data-bronto-connector]');
|
|
29
106
|
for (const svg of connectors) {
|
|
30
107
|
const from = byIdInHost(host, svg.dataset.from);
|
|
31
108
|
const to = byIdInHost(host, svg.dataset.to);
|
|
32
109
|
if (!from || !to) continue;
|
|
33
|
-
const o = svg.getBoundingClientRect();
|
|
34
|
-
const rel = (el) => {
|
|
35
|
-
const r = el.getBoundingClientRect();
|
|
36
|
-
return { x: r.left - o.left, y: r.top - o.top, width: r.width, height: r.height };
|
|
37
|
-
};
|
|
38
110
|
const {
|
|
39
111
|
d,
|
|
40
112
|
to: end,
|
|
41
113
|
angle,
|
|
42
114
|
} = connectRects({
|
|
43
|
-
fromRect:
|
|
44
|
-
toRect:
|
|
115
|
+
fromRect: rectInSvg(svg, from),
|
|
116
|
+
toRect: rectInSvg(svg, to),
|
|
45
117
|
shape: svg.dataset.shape || 'straight',
|
|
46
118
|
fromSide: svg.dataset.fromSide || undefined,
|
|
47
119
|
toSide: svg.dataset.toSide || undefined,
|
|
@@ -74,6 +146,13 @@ export function initConnectors({ root } = {}) {
|
|
|
74
146
|
};
|
|
75
147
|
|
|
76
148
|
return bindOnce(host, 'connectors', () => {
|
|
149
|
+
const connectors = collectHosts(host, '[data-bronto-connector]');
|
|
150
|
+
if (!connectors.length) return noop;
|
|
151
|
+
const states = connectors.map((svg) => ({
|
|
152
|
+
svg,
|
|
153
|
+
path: snapshotPart(svg, '.ui-connector__path'),
|
|
154
|
+
end: snapshotPart(svg, '.ui-connector__end'),
|
|
155
|
+
}));
|
|
77
156
|
draw();
|
|
78
157
|
const view = host.defaultView || host.ownerDocument?.defaultView || null;
|
|
79
158
|
const RO = view?.ResizeObserver;
|
|
@@ -93,6 +172,10 @@ export function initConnectors({ root } = {}) {
|
|
|
93
172
|
ro?.disconnect();
|
|
94
173
|
view?.removeEventListener('resize', draw);
|
|
95
174
|
view?.removeEventListener('scroll', draw, true);
|
|
175
|
+
for (const state of states) {
|
|
176
|
+
restorePart(state.svg, '.ui-connector__path', state.path);
|
|
177
|
+
restorePart(state.svg, '.ui-connector__end', state.end);
|
|
178
|
+
}
|
|
96
179
|
};
|
|
97
180
|
});
|
|
98
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crosshair.d.ts","sourceRoot":"","sources":["crosshair.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AACH,yCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"crosshair.d.ts","sourceRoot":"","sources":["crosshair.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;GAcG;AACH,yCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAiG3C;;;;;OApHa,MAAM;;;;OACN,MAAM;;;;QACN,MAAM;;;;QACN,MAAM"}
|
package/behaviors/crosshair.js
CHANGED
|
@@ -33,8 +33,49 @@ export function initCrosshair({ root } = {}) {
|
|
|
33
33
|
const cleanups = [];
|
|
34
34
|
for (const plot of plots) {
|
|
35
35
|
const overlay = plot.querySelector('.ui-crosshair');
|
|
36
|
-
|
|
36
|
+
let overlayState = null;
|
|
37
|
+
const rememberOverlay = () => {
|
|
38
|
+
if (!overlay || overlayState) return;
|
|
39
|
+
overlayState = {
|
|
40
|
+
active: overlay.classList.contains('is-active'),
|
|
41
|
+
x: {
|
|
42
|
+
value: overlay.style.getPropertyValue('--crosshair-x'),
|
|
43
|
+
priority: overlay.style.getPropertyPriority('--crosshair-x'),
|
|
44
|
+
},
|
|
45
|
+
y: {
|
|
46
|
+
value: overlay.style.getPropertyValue('--crosshair-y'),
|
|
47
|
+
priority: overlay.style.getPropertyPriority('--crosshair-y'),
|
|
48
|
+
},
|
|
49
|
+
inline: {
|
|
50
|
+
had: overlay.hasAttribute('data-readout-inline'),
|
|
51
|
+
value: overlay.getAttribute('data-readout-inline'),
|
|
52
|
+
},
|
|
53
|
+
block: {
|
|
54
|
+
had: overlay.hasAttribute('data-readout-block'),
|
|
55
|
+
value: overlay.getAttribute('data-readout-block'),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const restoreData = (name, state) => {
|
|
60
|
+
if (state.had) overlay.setAttribute(name, state.value);
|
|
61
|
+
else overlay.removeAttribute(name);
|
|
62
|
+
};
|
|
63
|
+
const restoreOverlay = () => {
|
|
64
|
+
if (!overlay || !overlayState) return;
|
|
65
|
+
overlay.classList.toggle('is-active', overlayState.active);
|
|
66
|
+
if (overlayState.x.value)
|
|
67
|
+
overlay.style.setProperty('--crosshair-x', overlayState.x.value, overlayState.x.priority);
|
|
68
|
+
else overlay.style.removeProperty('--crosshair-x');
|
|
69
|
+
if (overlayState.y.value)
|
|
70
|
+
overlay.style.setProperty('--crosshair-y', overlayState.y.value, overlayState.y.priority);
|
|
71
|
+
else overlay.style.removeProperty('--crosshair-y');
|
|
72
|
+
restoreData('data-readout-inline', overlayState.inline);
|
|
73
|
+
restoreData('data-readout-block', overlayState.block);
|
|
74
|
+
overlayState = null;
|
|
75
|
+
};
|
|
37
76
|
const onMove = (e) => {
|
|
77
|
+
if (!overlay) return;
|
|
78
|
+
rememberOverlay();
|
|
38
79
|
const r = plot.getBoundingClientRect();
|
|
39
80
|
if (!r.width || !r.height) return;
|
|
40
81
|
const x = e.clientX - r.left;
|
|
@@ -48,6 +89,8 @@ export function initCrosshair({ root } = {}) {
|
|
|
48
89
|
const rtl = getComputedStyle(plot).direction === 'rtl';
|
|
49
90
|
overlay.style.setProperty('--crosshair-x', `${rtl ? r.right - e.clientX : x}px`);
|
|
50
91
|
overlay.style.setProperty('--crosshair-y', `${y}px`);
|
|
92
|
+
overlay.dataset.readoutInline = x / r.width > 0.5 ? 'before' : 'after';
|
|
93
|
+
overlay.dataset.readoutBlock = y / r.height > 0.5 ? 'above' : 'below';
|
|
51
94
|
overlay.classList.add('is-active');
|
|
52
95
|
plot.dispatchEvent(
|
|
53
96
|
new CustomEvent('bronto:crosshair:move', {
|
|
@@ -57,16 +100,19 @@ export function initCrosshair({ root } = {}) {
|
|
|
57
100
|
);
|
|
58
101
|
};
|
|
59
102
|
const onLeave = () => {
|
|
103
|
+
if (!overlay) return;
|
|
60
104
|
overlay.classList.remove('is-active');
|
|
61
105
|
plot.dispatchEvent(new CustomEvent('bronto:crosshair:leave', { bubbles: true }));
|
|
62
106
|
};
|
|
63
107
|
cleanups.push(
|
|
64
108
|
bindOnce(plot, 'crosshair', () => {
|
|
109
|
+
if (!overlay) return noop;
|
|
65
110
|
plot.addEventListener('pointermove', onMove);
|
|
66
111
|
plot.addEventListener('pointerleave', onLeave);
|
|
67
112
|
return () => {
|
|
68
113
|
plot.removeEventListener('pointermove', onMove);
|
|
69
114
|
plot.removeEventListener('pointerleave', onLeave);
|
|
115
|
+
restoreOverlay();
|
|
70
116
|
};
|
|
71
117
|
}),
|
|
72
118
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["dialog.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,sCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["dialog.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,sCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA+E3C"}
|
package/behaviors/dialog.js
CHANGED
|
@@ -28,20 +28,32 @@ export function initDialog({ root } = {}) {
|
|
|
28
28
|
|
|
29
29
|
const openFrom = (opener) => {
|
|
30
30
|
const dlg = byIdInHost(host, opener.getAttribute('data-bronto-open'));
|
|
31
|
-
if (!dlg || typeof dlg.showModal !== 'function' || dlg.open) return;
|
|
32
|
-
|
|
31
|
+
if (!dlg || typeof dlg.showModal !== 'function' || dlg.open) return false;
|
|
32
|
+
const previous = focusRestorers.get(dlg);
|
|
33
|
+
if (previous) {
|
|
34
|
+
dlg.removeEventListener('close', previous);
|
|
35
|
+
focusRestorers.delete(dlg);
|
|
36
|
+
}
|
|
33
37
|
const restoreFocus = () => {
|
|
34
38
|
focusRestorers.delete(dlg);
|
|
35
39
|
if (opener.isConnected && typeof opener.focus === 'function') opener.focus();
|
|
36
40
|
};
|
|
41
|
+
try {
|
|
42
|
+
dlg.showModal();
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
managedDialogs.add(dlg);
|
|
37
47
|
focusRestorers.set(dlg, restoreFocus);
|
|
38
48
|
dlg.addEventListener('close', restoreFocus, { once: true });
|
|
39
|
-
|
|
49
|
+
return true;
|
|
40
50
|
};
|
|
41
51
|
|
|
42
52
|
const closeFrom = (closer) => {
|
|
43
53
|
const dlg = closer.closest('dialog');
|
|
44
|
-
if (dlg
|
|
54
|
+
if (!dlg || !dlg.open || !canManageDialog(dlg, closer)) return false;
|
|
55
|
+
dlg.close();
|
|
56
|
+
return true;
|
|
45
57
|
};
|
|
46
58
|
|
|
47
59
|
const lightDismiss = (dlg) => {
|
|
@@ -52,23 +64,26 @@ export function initDialog({ root } = {}) {
|
|
|
52
64
|
canManageDialog(dlg, dlg)
|
|
53
65
|
) {
|
|
54
66
|
dlg.close();
|
|
67
|
+
return true;
|
|
55
68
|
}
|
|
69
|
+
return false;
|
|
56
70
|
};
|
|
57
71
|
|
|
58
72
|
const onClick = (e) => {
|
|
59
|
-
const
|
|
73
|
+
const target = e.target;
|
|
74
|
+
const opener = target?.closest?.('[data-bronto-open]');
|
|
60
75
|
if (opener && host.contains(opener)) {
|
|
61
|
-
openFrom(opener);
|
|
76
|
+
if (openFrom(opener)) e.preventDefault();
|
|
62
77
|
return;
|
|
63
78
|
}
|
|
64
|
-
const closer =
|
|
79
|
+
const closer = target?.closest?.('[data-bronto-close]');
|
|
65
80
|
if (closer) {
|
|
66
|
-
closeFrom(closer);
|
|
81
|
+
if (closeFrom(closer)) e.preventDefault();
|
|
67
82
|
return;
|
|
68
83
|
}
|
|
69
84
|
// Light-dismiss: a click whose target is the <dialog> itself is the
|
|
70
85
|
// backdrop (content sits in child elements).
|
|
71
|
-
lightDismiss(e.
|
|
86
|
+
if (lightDismiss(target)) e.preventDefault();
|
|
72
87
|
};
|
|
73
88
|
return bindOnce(host, 'dialog', () => {
|
|
74
89
|
document.addEventListener('click', onClick);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disclosure.d.ts","sourceRoot":"","sources":["disclosure.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"disclosure.d.ts","sourceRoot":"","sources":["disclosure.js"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,0CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAwC3C"}
|
package/behaviors/disclosure.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import { hasDom, resolveHost, noop, bindOnce, byIdInHost } from './internal.js';
|
|
1
|
+
import { hasDom, resolveHost, noop, bindOnce, byIdInHost, closestSafe } from './internal.js';
|
|
2
|
+
|
|
3
|
+
const snapshotAttr = (el, name) => ({
|
|
4
|
+
had: el.hasAttribute(name),
|
|
5
|
+
value: el.getAttribute(name),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const restoreAttr = (el, name, state) => {
|
|
9
|
+
if (state.had) el.setAttribute(name, state.value);
|
|
10
|
+
else el.removeAttribute(name);
|
|
11
|
+
};
|
|
2
12
|
|
|
3
13
|
/**
|
|
4
14
|
* Disclosure: a `[data-bronto-disclosure]` trigger toggles the element
|
|
@@ -12,18 +22,38 @@ export function initDisclosure({ root } = {}) {
|
|
|
12
22
|
if (!hasDom()) return noop;
|
|
13
23
|
const host = resolveHost(root);
|
|
14
24
|
if (!host) return noop;
|
|
25
|
+
const triggerStates = new Map();
|
|
26
|
+
const panelStates = new Map();
|
|
27
|
+
|
|
28
|
+
const remember = (trigger, panel) => {
|
|
29
|
+
if (!triggerStates.has(trigger)) {
|
|
30
|
+
triggerStates.set(trigger, snapshotAttr(trigger, 'aria-expanded'));
|
|
31
|
+
}
|
|
32
|
+
if (!panelStates.has(panel)) {
|
|
33
|
+
panelStates.set(panel, snapshotAttr(panel, 'hidden'));
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
15
37
|
const onClick = (e) => {
|
|
16
|
-
const trigger = e.target
|
|
38
|
+
const trigger = closestSafe(e.target, '[data-bronto-disclosure]');
|
|
17
39
|
if (!trigger || !host.contains(trigger)) return;
|
|
18
40
|
const id = trigger.getAttribute('aria-controls');
|
|
19
41
|
const panel = byIdInHost(host, id);
|
|
20
42
|
if (!panel) return;
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
remember(trigger, panel);
|
|
21
45
|
const open = trigger.getAttribute('aria-expanded') === 'true';
|
|
22
46
|
trigger.setAttribute('aria-expanded', String(!open));
|
|
23
47
|
panel.hidden = open;
|
|
24
48
|
};
|
|
25
49
|
return bindOnce(host, 'disclosure', () => {
|
|
26
50
|
host.addEventListener('click', onClick);
|
|
27
|
-
return () =>
|
|
51
|
+
return () => {
|
|
52
|
+
host.removeEventListener('click', onClick);
|
|
53
|
+
for (const [trigger, state] of triggerStates) restoreAttr(trigger, 'aria-expanded', state);
|
|
54
|
+
triggerStates.clear();
|
|
55
|
+
for (const [panel, state] of panelStates) restoreAttr(panel, 'hidden', state);
|
|
56
|
+
panelStates.clear();
|
|
57
|
+
};
|
|
28
58
|
});
|
|
29
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dismissible.d.ts","sourceRoot":"","sources":["dismissible.js"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,uCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"dismissible.d.ts","sourceRoot":"","sources":["dismissible.js"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,uCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAoB3C"}
|
package/behaviors/dismissible.js
CHANGED
|
@@ -13,11 +13,12 @@ export function dismissible({ root } = {}) {
|
|
|
13
13
|
const host = resolveHost(root);
|
|
14
14
|
if (!host) return noop;
|
|
15
15
|
const onClick = (e) => {
|
|
16
|
-
const btn = e.target
|
|
16
|
+
const btn = closestSafe(e.target, '[data-bronto-dismiss]');
|
|
17
17
|
if (!btn || !host.contains(btn)) return;
|
|
18
18
|
const sel = btn.getAttribute('data-bronto-dismiss');
|
|
19
|
-
const target = sel ? closestSafe(btn, sel) : btn
|
|
19
|
+
const target = sel ? closestSafe(btn, sel) : closestSafe(btn, '[data-bronto-dismissible]');
|
|
20
20
|
if (!target) return;
|
|
21
|
+
e.preventDefault();
|
|
21
22
|
const ev = new CustomEvent('bronto:dismiss', { bubbles: true, cancelable: true });
|
|
22
23
|
if (target.dispatchEvent(ev)) target.remove();
|
|
23
24
|
};
|
package/behaviors/forms.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["forms.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,8CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["forms.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,8CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAsP3C"}
|
package/behaviors/forms.js
CHANGED
|
@@ -30,12 +30,59 @@ export function initFormValidation({ root } = {}) {
|
|
|
30
30
|
const host = resolveHost(root);
|
|
31
31
|
if (!host) return noop;
|
|
32
32
|
let priorNoValidate = new Map();
|
|
33
|
+
let controlState = new Map();
|
|
34
|
+
let slotState = new Map();
|
|
35
|
+
let summaryState = new Map();
|
|
33
36
|
|
|
34
37
|
const suppressNativeValidation = (form) => {
|
|
35
38
|
if (!priorNoValidate.has(form)) priorNoValidate.set(form, form.noValidate);
|
|
36
39
|
form.noValidate = true;
|
|
37
40
|
};
|
|
38
41
|
|
|
42
|
+
const snapshotAttrs = (el, names) => {
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const name of names) {
|
|
45
|
+
out[name] = {
|
|
46
|
+
had: el.hasAttribute(name),
|
|
47
|
+
value: el.getAttribute(name),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const restoreAttrs = (el, attrs) => {
|
|
54
|
+
for (const [name, attr] of Object.entries(attrs)) {
|
|
55
|
+
if (attr.had) el.setAttribute(name, attr.value);
|
|
56
|
+
else el.removeAttribute(name);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const rememberControl = (control) => {
|
|
61
|
+
if (!controlState.has(control)) {
|
|
62
|
+
controlState.set(control, snapshotAttrs(control, ['aria-invalid', 'aria-describedby', 'id']));
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const rememberSlot = (slot) => {
|
|
67
|
+
if (!slotState.has(slot)) {
|
|
68
|
+
slotState.set(slot, {
|
|
69
|
+
attrs: snapshotAttrs(slot, ['id']),
|
|
70
|
+
text: slot.textContent,
|
|
71
|
+
hadErrorClass: slot.classList.contains('ui-hint--error'),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const rememberSummary = (summary) => {
|
|
77
|
+
if (!summaryState.has(summary)) {
|
|
78
|
+
summaryState.set(summary, {
|
|
79
|
+
attrs: snapshotAttrs(summary, ['role', 'tabindex']),
|
|
80
|
+
children: [...summary.childNodes],
|
|
81
|
+
hidden: summary.hidden,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
39
86
|
const ensureId = (el, prefix) => {
|
|
40
87
|
if (!el.id) el.id = `${prefix}-${nextFieldUid()}`;
|
|
41
88
|
return el.id;
|
|
@@ -75,8 +122,10 @@ export function initFormValidation({ root } = {}) {
|
|
|
75
122
|
|
|
76
123
|
const validateField = (control) => {
|
|
77
124
|
if (!control.willValidate) return true;
|
|
125
|
+
rememberControl(control);
|
|
78
126
|
const ok = control.validity.valid;
|
|
79
127
|
const slot = slotFor(control);
|
|
128
|
+
if (slot) rememberSlot(slot);
|
|
80
129
|
// Decide the slot TYPE by the `[data-bronto-error]` attribute, NOT the
|
|
81
130
|
// `.ui-hint` class: the canonical markup is `<p class="ui-hint"
|
|
82
131
|
// data-bronto-error>`, which carries BOTH. Keying off `.ui-hint` sent that
|
|
@@ -124,6 +173,7 @@ export function initFormValidation({ root } = {}) {
|
|
|
124
173
|
const refreshSummary = (form, invalid) => {
|
|
125
174
|
const summary = form.querySelector('[data-bronto-error-summary]');
|
|
126
175
|
if (!summary) return;
|
|
176
|
+
rememberSummary(summary);
|
|
127
177
|
if (!invalid.length) {
|
|
128
178
|
summary.hidden = true;
|
|
129
179
|
summary.replaceChildren();
|
|
@@ -190,6 +240,9 @@ export function initFormValidation({ root } = {}) {
|
|
|
190
240
|
// after init are still covered by the in-handler set.)
|
|
191
241
|
const forms = collectHosts(host, '[data-bronto-validate]');
|
|
192
242
|
priorNoValidate = new Map();
|
|
243
|
+
controlState = new Map();
|
|
244
|
+
slotState = new Map();
|
|
245
|
+
summaryState = new Map();
|
|
193
246
|
for (const f of forms) {
|
|
194
247
|
suppressNativeValidation(f);
|
|
195
248
|
}
|
|
@@ -200,6 +253,20 @@ export function initFormValidation({ root } = {}) {
|
|
|
200
253
|
host.removeEventListener('focusout', onBlur);
|
|
201
254
|
for (const [f, v] of priorNoValidate) f.noValidate = v;
|
|
202
255
|
priorNoValidate.clear();
|
|
256
|
+
for (const [summary, state] of summaryState) {
|
|
257
|
+
summary.replaceChildren(...state.children);
|
|
258
|
+
summary.hidden = state.hidden;
|
|
259
|
+
restoreAttrs(summary, state.attrs);
|
|
260
|
+
}
|
|
261
|
+
summaryState.clear();
|
|
262
|
+
for (const [slot, state] of slotState) {
|
|
263
|
+
slot.textContent = state.text;
|
|
264
|
+
slot.classList.toggle('ui-hint--error', state.hadErrorClass);
|
|
265
|
+
restoreAttrs(slot, state.attrs);
|
|
266
|
+
}
|
|
267
|
+
slotState.clear();
|
|
268
|
+
for (const [control, attrs] of controlState) restoreAttrs(control, attrs);
|
|
269
|
+
controlState.clear();
|
|
203
270
|
};
|
|
204
271
|
});
|
|
205
272
|
}
|
package/behaviors/glyph.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"AA6BA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA0J3C"}
|