@ponchia/ui 0.6.5 → 0.6.7
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 +170 -0
- package/README.md +43 -23
- package/behaviors/carousel.d.ts.map +1 -1
- package/behaviors/carousel.js +3 -0
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +14 -8
- package/behaviors/forms.d.ts.map +1 -1
- package/behaviors/forms.js +11 -5
- package/behaviors/index.d.ts +2 -0
- package/behaviors/index.d.ts.map +1 -1
- package/behaviors/index.js +2 -0
- package/behaviors/internal.d.ts +2 -1
- package/behaviors/internal.d.ts.map +1 -1
- package/behaviors/internal.js +23 -3
- package/behaviors/legend.d.ts.map +1 -1
- package/behaviors/legend.js +41 -9
- package/behaviors/splitter.d.ts +26 -0
- package/behaviors/splitter.d.ts.map +1 -0
- package/behaviors/splitter.js +200 -0
- package/behaviors/table.js +3 -3
- package/behaviors/theme.js +2 -2
- package/classes/classes.json +230 -4
- package/classes/index.d.ts +49 -1
- package/classes/index.js +56 -1
- package/classes/vscode.css-custom-data.json +1 -1
- package/css/analytical.css +3 -1
- package/css/app.css +4 -4
- package/css/clamp.css +92 -0
- package/css/figure.css +102 -0
- package/css/highlights.css +50 -0
- package/css/interval.css +90 -0
- package/css/primitives.css +2 -3
- package/css/report-kit.css +38 -0
- package/css/report.css +51 -4
- package/css/sidenote.css +12 -2
- package/css/site.css +2 -1
- package/css/sources.css +5 -0
- package/css/state.css +120 -1
- package/css/table.css +4 -0
- package/css/tokens.css +9 -9
- package/css/workbench.css +101 -8
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/clamp.css +1 -0
- package/dist/css/figure.css +1 -0
- package/dist/css/highlights.css +1 -0
- package/dist/css/interval.css +1 -0
- package/dist/css/primitives.css +1 -1
- package/dist/css/report-kit.css +1 -0
- package/dist/css/report.css +1 -1
- package/dist/css/sidenote.css +1 -1
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -1
- package/dist/css/state.css +1 -1
- package/dist/css/table.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0002-scope-and-2026-baseline.md +1 -1
- package/docs/architecture.md +67 -43
- package/docs/clamp.md +49 -0
- package/docs/contrast.md +34 -24
- package/docs/d2.md +37 -0
- package/docs/figure.md +71 -0
- package/docs/frontier-primitives.md +48 -23
- package/docs/highlights.md +52 -0
- package/docs/interop/tailwind.md +148 -0
- package/docs/interval.md +55 -0
- package/docs/legends.md +3 -2
- package/docs/mermaid.md +6 -0
- package/docs/migrations/0.2-to-0.3.md +80 -0
- package/docs/migrations/0.3-to-0.4.md +48 -0
- package/docs/migrations/0.4-to-0.5.md +96 -0
- package/docs/migrations/0.5-to-0.6.md +82 -0
- package/docs/package-contract.md +40 -2
- package/docs/reference.md +79 -6
- package/docs/reporting.md +132 -56
- package/docs/sidenote.md +7 -1
- package/docs/sources.md +1 -1
- package/docs/stability.md +5 -3
- package/docs/state.md +67 -10
- package/docs/theming.md +10 -2
- package/docs/usage.md +31 -11
- package/docs/workbench.md +59 -18
- package/llms.txt +82 -14
- package/package.json +68 -6
- package/qwik/index.d.ts +1 -0
- package/qwik/index.d.ts.map +1 -1
- package/qwik/index.js +26 -21
- package/react/index.d.ts +1 -0
- package/react/index.d.ts.map +1 -1
- package/react/index.js +4 -1
- package/schemas/report-claims.v1.schema.json +137 -0
- package/solid/index.d.ts +2 -0
- package/solid/index.d.ts.map +1 -1
- package/solid/index.js +3 -0
- package/svelte/index.d.ts +88 -0
- package/svelte/index.d.ts.map +1 -0
- package/svelte/index.js +166 -0
- package/tailwind.css +87 -0
- package/tokens/figma.variables.json +2241 -0
- package/tokens/index.js +1 -1
- package/tokens/index.json +2 -2
- package/tokens/resolved.json +3 -3
- package/tokens/tokens.dtcg.json +1 -1
- package/vue/index.d.ts +79 -0
- package/vue/index.d.ts.map +1 -0
- package/vue/index.js +197 -0
package/behaviors/legend.js
CHANGED
|
@@ -29,14 +29,18 @@ export function initLegend({ root } = {}) {
|
|
|
29
29
|
const host = resolveHost(root);
|
|
30
30
|
if (!host) return noop;
|
|
31
31
|
const isButton = (el) => el.tagName === 'BUTTON' || el.getAttribute('role') === 'button';
|
|
32
|
-
const
|
|
33
|
-
const item = e.target.closest('.ui-legend__item');
|
|
32
|
+
const legendFor = (item) => {
|
|
34
33
|
if (!item || !host.contains(item)) return;
|
|
35
34
|
const legend = item.closest('[data-bronto-legend]');
|
|
36
35
|
if (!legend || !host.contains(legend)) return;
|
|
36
|
+
return legend;
|
|
37
|
+
};
|
|
38
|
+
const toggle = (item) => {
|
|
39
|
+
const legend = legendFor(item);
|
|
40
|
+
if (!legend) return;
|
|
37
41
|
// The contract requires a real `<button>` (keyboard-operable, focusable). A
|
|
38
|
-
// non-button item is mouse-only
|
|
39
|
-
//
|
|
42
|
+
// non-button item is mouse-only unless role=button is keyboard-normalized
|
|
43
|
+
// below — refuse anything else rather than ship a pointer-only control.
|
|
40
44
|
if (!isButton(item)) return;
|
|
41
45
|
const active = item.getAttribute('aria-pressed') !== 'false';
|
|
42
46
|
const next = !active;
|
|
@@ -54,23 +58,51 @@ export function initLegend({ root } = {}) {
|
|
|
54
58
|
}),
|
|
55
59
|
);
|
|
56
60
|
};
|
|
61
|
+
const onClick = (e) => {
|
|
62
|
+
toggle(e.target.closest('.ui-legend__item'));
|
|
63
|
+
};
|
|
64
|
+
const onKey = (e) => {
|
|
65
|
+
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
66
|
+
const item = e.target.closest('.ui-legend__item');
|
|
67
|
+
if (!item || item.tagName === 'BUTTON' || item.getAttribute('role') !== 'button') return;
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
toggle(item);
|
|
70
|
+
};
|
|
57
71
|
return bindOnce(host, 'legend', () => {
|
|
58
|
-
//
|
|
59
|
-
//
|
|
72
|
+
// Normalize role=button entries and warn once per unsupported non-button
|
|
73
|
+
// item present at bind. A real <button> remains the recommended markup.
|
|
74
|
+
const legends = [...(host.querySelectorAll?.('[data-bronto-legend]') ?? [])];
|
|
75
|
+
for (const legend of legends) {
|
|
76
|
+
for (const el of legend.querySelectorAll('.ui-legend__item')) {
|
|
77
|
+
if (el.closest('[data-bronto-legend]') !== legend) continue;
|
|
78
|
+
if (el.tagName === 'BUTTON' && !el.hasAttribute('type')) el.type = 'button';
|
|
79
|
+
if (
|
|
80
|
+
el.tagName !== 'BUTTON' &&
|
|
81
|
+
el.getAttribute('role') === 'button' &&
|
|
82
|
+
!el.hasAttribute('tabindex')
|
|
83
|
+
) {
|
|
84
|
+
el.tabIndex = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
60
88
|
if (typeof console !== 'undefined') {
|
|
61
|
-
for (const legend of
|
|
89
|
+
for (const legend of legends) {
|
|
62
90
|
const stray = [...legend.querySelectorAll('.ui-legend__item')].some(
|
|
63
91
|
(el) => el.closest('[data-bronto-legend]') === legend && !isButton(el),
|
|
64
92
|
);
|
|
65
93
|
if (stray) {
|
|
66
94
|
console.warn(
|
|
67
|
-
'[bronto] initLegend(): interactive legend entries must be <button>
|
|
95
|
+
'[bronto] initLegend(): interactive legend entries must be <button> or role="button" — unsupported .ui-legend__item controls are ignored.',
|
|
68
96
|
);
|
|
69
97
|
break;
|
|
70
98
|
}
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
host.addEventListener('click', onClick);
|
|
74
|
-
|
|
102
|
+
host.addEventListener('keydown', onKey);
|
|
103
|
+
return () => {
|
|
104
|
+
host.removeEventListener('click', onClick);
|
|
105
|
+
host.removeEventListener('keydown', onKey);
|
|
106
|
+
};
|
|
75
107
|
});
|
|
76
108
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire focusable ARIA splitters. Each `[data-bronto-splitter]` host contains
|
|
3
|
+
* two `.ui-splitter__pane` elements separated by one `.ui-splitter__handle`
|
|
4
|
+
* (`role="separator"`). The behavior keeps `--splitter-pos` and
|
|
5
|
+
* `aria-valuenow` in sync for keyboard and pointer resizing, then dispatches
|
|
6
|
+
* `bronto:splitter:resize` with `{ value, orientation }`.
|
|
7
|
+
*
|
|
8
|
+
* Bronto owns the control affordance only. The host owns pane content,
|
|
9
|
+
* persistence, min/max policy, collapse behavior, and any saved layout state.
|
|
10
|
+
* SSR-safe and idempotent per splitter; returns a cleanup function.
|
|
11
|
+
*
|
|
12
|
+
* @param {import('./internal.js').DelegateOpts} [opts]
|
|
13
|
+
* @returns {import('./internal.js').Cleanup}
|
|
14
|
+
*/
|
|
15
|
+
export function initSplitter({ root }?: import("./internal.js").DelegateOpts): import("./internal.js").Cleanup;
|
|
16
|
+
export type SplitterResizeDetail = {
|
|
17
|
+
/**
|
|
18
|
+
* The first pane size as a 0..100 percentage.
|
|
19
|
+
*/
|
|
20
|
+
value: number;
|
|
21
|
+
/**
|
|
22
|
+
* Splitter orientation.
|
|
23
|
+
*/
|
|
24
|
+
orientation: "vertical" | "horizontal";
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=splitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["splitter.js"],"names":[],"mappings":"AAiLA;;;;;;;;;;;;;GAaG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAU3C;;;;;WA3La,MAAM;;;;iBACN,UAAU,GAAG,YAAY"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
|
|
2
|
+
|
|
3
|
+
const SELECTOR = '[data-bronto-splitter]';
|
|
4
|
+
const HANDLE_SELECTOR = '.ui-splitter__handle';
|
|
5
|
+
const DEFAULT_MIN = 20;
|
|
6
|
+
const DEFAULT_MAX = 80;
|
|
7
|
+
const DEFAULT_VALUE = 50;
|
|
8
|
+
const STEP = 2;
|
|
9
|
+
const LARGE_STEP = 10;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} SplitterResizeDetail
|
|
13
|
+
* @property {number} value The first pane size as a 0..100 percentage.
|
|
14
|
+
* @property {'vertical' | 'horizontal'} orientation Splitter orientation.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const num = (v, fallback) => {
|
|
18
|
+
const n = Number.parseFloat(String(v ?? '').trim());
|
|
19
|
+
return Number.isFinite(n) ? n : fallback;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const fmt = (v) => String(Math.round(v * 10) / 10);
|
|
23
|
+
|
|
24
|
+
const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
|
|
25
|
+
|
|
26
|
+
const readCssValue = (splitter) => splitter.style.getPropertyValue('--splitter-pos');
|
|
27
|
+
|
|
28
|
+
const readOrientation = (splitter, handle) => {
|
|
29
|
+
const data = splitter.getAttribute('data-bronto-splitter');
|
|
30
|
+
if (data === 'horizontal' || data === 'vertical') return data;
|
|
31
|
+
if (splitter.classList.contains('ui-splitter--horizontal')) return 'horizontal';
|
|
32
|
+
if (splitter.classList.contains('ui-splitter--vertical')) return 'vertical';
|
|
33
|
+
return handle.getAttribute('aria-orientation') === 'horizontal' ? 'horizontal' : 'vertical';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getView = (el) => el.ownerDocument?.defaultView || null;
|
|
37
|
+
|
|
38
|
+
const dispatchResize = (splitter, detail) => {
|
|
39
|
+
splitter.dispatchEvent(
|
|
40
|
+
new CustomEvent('bronto:splitter:resize', {
|
|
41
|
+
bubbles: true,
|
|
42
|
+
detail,
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function wireSplitter(splitter) {
|
|
48
|
+
const handle = splitter.querySelector(HANDLE_SELECTOR);
|
|
49
|
+
if (!handle) return noop;
|
|
50
|
+
|
|
51
|
+
const orientation = readOrientation(splitter, handle);
|
|
52
|
+
const min = num(handle.getAttribute('aria-valuemin'), DEFAULT_MIN);
|
|
53
|
+
const max = Math.max(min, num(handle.getAttribute('aria-valuemax'), DEFAULT_MAX));
|
|
54
|
+
let value = clamp(
|
|
55
|
+
num(handle.getAttribute('aria-valuenow'), num(readCssValue(splitter), DEFAULT_VALUE)),
|
|
56
|
+
min,
|
|
57
|
+
max,
|
|
58
|
+
);
|
|
59
|
+
let activePointer = null;
|
|
60
|
+
|
|
61
|
+
const apply = (next, { emit = true } = {}) => {
|
|
62
|
+
value = clamp(next, min, max);
|
|
63
|
+
const label = fmt(value);
|
|
64
|
+
splitter.style.setProperty('--splitter-pos', `${label}%`);
|
|
65
|
+
handle.setAttribute('aria-valuenow', label);
|
|
66
|
+
if (emit) dispatchResize(splitter, { value, orientation });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (!handle.hasAttribute('role')) handle.setAttribute('role', 'separator');
|
|
70
|
+
if (!handle.hasAttribute('tabindex')) handle.tabIndex = 0;
|
|
71
|
+
if (!handle.hasAttribute('aria-orientation'))
|
|
72
|
+
handle.setAttribute('aria-orientation', orientation);
|
|
73
|
+
if (!handle.hasAttribute('aria-valuemin')) handle.setAttribute('aria-valuemin', fmt(min));
|
|
74
|
+
if (!handle.hasAttribute('aria-valuemax')) handle.setAttribute('aria-valuemax', fmt(max));
|
|
75
|
+
apply(value, { emit: false });
|
|
76
|
+
|
|
77
|
+
const fromPointer = (event) => {
|
|
78
|
+
const rect = splitter.getBoundingClientRect();
|
|
79
|
+
const size = orientation === 'horizontal' ? rect.height : rect.width;
|
|
80
|
+
if (!size) return value;
|
|
81
|
+
if (orientation === 'horizontal') {
|
|
82
|
+
return ((event.clientY - rect.top) / size) * 100;
|
|
83
|
+
}
|
|
84
|
+
const view = getView(splitter);
|
|
85
|
+
const dir = view?.getComputedStyle?.(splitter).direction;
|
|
86
|
+
const x = dir === 'rtl' ? rect.right - event.clientX : event.clientX - rect.left;
|
|
87
|
+
return (x / size) * 100;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const capturePointer = (pointerId) => {
|
|
91
|
+
if (pointerId === undefined || pointerId === null) return;
|
|
92
|
+
try {
|
|
93
|
+
handle.setPointerCapture?.(pointerId);
|
|
94
|
+
} catch {
|
|
95
|
+
/* Pointer capture is an affordance; drag still works through document listeners. */
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const releasePointer = (pointerId = activePointer) => {
|
|
100
|
+
if (pointerId === undefined || pointerId === null) return;
|
|
101
|
+
try {
|
|
102
|
+
if (!handle.hasPointerCapture || handle.hasPointerCapture(pointerId)) {
|
|
103
|
+
handle.releasePointerCapture?.(pointerId);
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
/* The element may have been removed or capture may already be gone. */
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const onKeydown = (event) => {
|
|
111
|
+
let next = value;
|
|
112
|
+
if (event.key === 'Home') next = min;
|
|
113
|
+
else if (event.key === 'End') next = max;
|
|
114
|
+
else if (event.key === 'PageUp') next += LARGE_STEP;
|
|
115
|
+
else if (event.key === 'PageDown') next -= LARGE_STEP;
|
|
116
|
+
else if (event.key === 'ArrowRight' || event.key === 'ArrowDown')
|
|
117
|
+
next += event.shiftKey ? LARGE_STEP : STEP;
|
|
118
|
+
else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp')
|
|
119
|
+
next -= event.shiftKey ? LARGE_STEP : STEP;
|
|
120
|
+
else return;
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
apply(next);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const onPointerMove = (event) => {
|
|
126
|
+
if (
|
|
127
|
+
activePointer !== null &&
|
|
128
|
+
event.pointerId !== undefined &&
|
|
129
|
+
event.pointerId !== activePointer
|
|
130
|
+
)
|
|
131
|
+
return;
|
|
132
|
+
apply(fromPointer(event));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const onPointerUp = (event) => {
|
|
136
|
+
if (
|
|
137
|
+
activePointer !== null &&
|
|
138
|
+
event.pointerId !== undefined &&
|
|
139
|
+
event.pointerId !== activePointer
|
|
140
|
+
)
|
|
141
|
+
return;
|
|
142
|
+
releasePointer(event.pointerId);
|
|
143
|
+
activePointer = null;
|
|
144
|
+
handle.classList.remove('is-active');
|
|
145
|
+
splitter.ownerDocument.removeEventListener('pointermove', onPointerMove);
|
|
146
|
+
splitter.ownerDocument.removeEventListener('pointerup', onPointerUp);
|
|
147
|
+
splitter.ownerDocument.removeEventListener('pointercancel', onPointerUp);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const onPointerDown = (event) => {
|
|
151
|
+
if (event.button !== undefined && event.button !== 0) return;
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
activePointer = event.pointerId ?? null;
|
|
154
|
+
capturePointer(activePointer);
|
|
155
|
+
handle.classList.add('is-active');
|
|
156
|
+
apply(fromPointer(event));
|
|
157
|
+
splitter.ownerDocument.addEventListener('pointermove', onPointerMove);
|
|
158
|
+
splitter.ownerDocument.addEventListener('pointerup', onPointerUp);
|
|
159
|
+
splitter.ownerDocument.addEventListener('pointercancel', onPointerUp);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return bindOnce(splitter, 'splitter', () => {
|
|
163
|
+
handle.addEventListener('keydown', onKeydown);
|
|
164
|
+
handle.addEventListener('pointerdown', onPointerDown);
|
|
165
|
+
return () => {
|
|
166
|
+
handle.removeEventListener('keydown', onKeydown);
|
|
167
|
+
handle.removeEventListener('pointerdown', onPointerDown);
|
|
168
|
+
splitter.ownerDocument.removeEventListener('pointermove', onPointerMove);
|
|
169
|
+
splitter.ownerDocument.removeEventListener('pointerup', onPointerUp);
|
|
170
|
+
splitter.ownerDocument.removeEventListener('pointercancel', onPointerUp);
|
|
171
|
+
releasePointer();
|
|
172
|
+
handle.classList.remove('is-active');
|
|
173
|
+
activePointer = null;
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Wire focusable ARIA splitters. Each `[data-bronto-splitter]` host contains
|
|
180
|
+
* two `.ui-splitter__pane` elements separated by one `.ui-splitter__handle`
|
|
181
|
+
* (`role="separator"`). The behavior keeps `--splitter-pos` and
|
|
182
|
+
* `aria-valuenow` in sync for keyboard and pointer resizing, then dispatches
|
|
183
|
+
* `bronto:splitter:resize` with `{ value, orientation }`.
|
|
184
|
+
*
|
|
185
|
+
* Bronto owns the control affordance only. The host owns pane content,
|
|
186
|
+
* persistence, min/max policy, collapse behavior, and any saved layout state.
|
|
187
|
+
* SSR-safe and idempotent per splitter; returns a cleanup function.
|
|
188
|
+
*
|
|
189
|
+
* @param {import('./internal.js').DelegateOpts} [opts]
|
|
190
|
+
* @returns {import('./internal.js').Cleanup}
|
|
191
|
+
*/
|
|
192
|
+
export function initSplitter({ root } = {}) {
|
|
193
|
+
if (!hasDom()) return noop;
|
|
194
|
+
const host = resolveHost(root);
|
|
195
|
+
if (!host) return noop;
|
|
196
|
+
const splitters = collectHosts(host, SELECTOR);
|
|
197
|
+
if (!splitters.length) return noop;
|
|
198
|
+
const cleanups = splitters.map(wireSplitter).filter(Boolean);
|
|
199
|
+
return () => cleanups.forEach((fn) => fn());
|
|
200
|
+
}
|
package/behaviors/table.js
CHANGED
|
@@ -49,6 +49,7 @@ export function initTableSort({ root } = {}) {
|
|
|
49
49
|
// announces the column as sortable from the start (it was unset until the
|
|
50
50
|
// first click — C10).
|
|
51
51
|
for (const sort of table.querySelectorAll('.ui-table__sort')) {
|
|
52
|
+
if (sort.tagName === 'BUTTON' && !sort.hasAttribute('type')) sort.type = 'button';
|
|
52
53
|
const th = sort.closest('th');
|
|
53
54
|
if (th && !th.hasAttribute('aria-sort')) th.setAttribute('aria-sort', 'none');
|
|
54
55
|
}
|
|
@@ -110,9 +111,8 @@ export function initTableSort({ root } = {}) {
|
|
|
110
111
|
return cmp * sign;
|
|
111
112
|
});
|
|
112
113
|
// Re-parent in document order: sorted data rows, then any empty/sentinel
|
|
113
|
-
// row last.
|
|
114
|
-
|
|
115
|
-
for (const r of [...rows, ...emptyRows]) tbody.appendChild(r);
|
|
114
|
+
// row last. These are existing <tr> nodes being moved; no markup is parsed.
|
|
115
|
+
tbody.append(...rows, ...emptyRows);
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
const allBox = table.querySelector('[data-bronto-select-all]');
|
package/behaviors/theme.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasDom, resolveHost, noop, bindOnce } from './internal.js';
|
|
1
|
+
import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
|
|
2
2
|
|
|
3
3
|
const THEMES = ['light', 'dark'];
|
|
4
4
|
|
|
@@ -66,7 +66,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
|
|
|
66
66
|
|
|
67
67
|
const reflect = () => {
|
|
68
68
|
const c = current();
|
|
69
|
-
host
|
|
69
|
+
collectHosts(host, '[data-bronto-theme-toggle]').forEach((el) => {
|
|
70
70
|
const forced = el.getAttribute('data-bronto-theme-toggle');
|
|
71
71
|
// A forced control is "pressed" when its theme is the active one;
|
|
72
72
|
// a plain toggle reflects whether dark is active.
|