@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/tabs.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
hasDom,
|
|
3
|
+
resolveHost,
|
|
4
|
+
noop,
|
|
5
|
+
bindOnce,
|
|
6
|
+
nextFieldUid,
|
|
7
|
+
collectHosts,
|
|
8
|
+
closestSafe,
|
|
9
|
+
} from './internal.js';
|
|
2
10
|
|
|
3
11
|
/**
|
|
4
12
|
* Wire `[data-bronto-tabs]` groups for full keyboard a11y. The framework
|
|
@@ -24,6 +32,23 @@ export function initTabs({ root } = {}) {
|
|
|
24
32
|
if (!host) return noop;
|
|
25
33
|
const cleanups = [];
|
|
26
34
|
const groups = collectHosts(host, '[data-bronto-tabs]');
|
|
35
|
+
const snapshotAttrs = (el, names) => {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
out[name] = {
|
|
39
|
+
had: el.hasAttribute(name),
|
|
40
|
+
value: el.getAttribute(name),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
};
|
|
45
|
+
const restoreAttrs = (el, attrs) => {
|
|
46
|
+
for (const [name, attr] of Object.entries(attrs)) {
|
|
47
|
+
if (attr.had) el.setAttribute(name, attr.value);
|
|
48
|
+
else el.removeAttribute(name);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
27
52
|
for (const group of groups) {
|
|
28
53
|
// Own group only — a tab/panel inside a nested [data-bronto-tabs]
|
|
29
54
|
// belongs to that inner group, not this one.
|
|
@@ -31,20 +56,43 @@ export function initTabs({ root } = {}) {
|
|
|
31
56
|
const tabs = [...group.querySelectorAll('.ui-tab')].filter(owned);
|
|
32
57
|
const panels = [...group.querySelectorAll('.ui-tabs__panel')].filter(owned);
|
|
33
58
|
if (!tabs.length) continue;
|
|
34
|
-
const list = group.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
const list = [...group.querySelectorAll('.ui-tabs__list')].find(owned);
|
|
60
|
+
const rememberState = () => ({
|
|
61
|
+
list: list ? snapshotAttrs(list, ['role']) : null,
|
|
62
|
+
tabs: new Map(
|
|
63
|
+
tabs.map((tab) => [
|
|
64
|
+
tab,
|
|
65
|
+
{
|
|
66
|
+
active: tab.classList.contains('is-active'),
|
|
67
|
+
attrs: snapshotAttrs(tab, ['id', 'role', 'aria-selected', 'aria-controls', 'tabindex']),
|
|
68
|
+
},
|
|
69
|
+
]),
|
|
70
|
+
),
|
|
71
|
+
panels: new Map(
|
|
72
|
+
panels.map((panel) => [
|
|
73
|
+
panel,
|
|
74
|
+
{
|
|
75
|
+
hidden: panel.hidden,
|
|
76
|
+
attrs: snapshotAttrs(panel, ['id', 'role', 'aria-labelledby', 'tabindex']),
|
|
77
|
+
},
|
|
78
|
+
]),
|
|
79
|
+
),
|
|
80
|
+
});
|
|
81
|
+
const restoreState = (state) => {
|
|
82
|
+
if (list && state.list) restoreAttrs(list, state.list);
|
|
83
|
+
for (const tab of tabs) {
|
|
84
|
+
const tabState = state.tabs.get(tab);
|
|
85
|
+
if (!tabState) continue;
|
|
86
|
+
tab.classList.toggle('is-active', tabState.active);
|
|
87
|
+
restoreAttrs(tab, tabState.attrs);
|
|
88
|
+
}
|
|
89
|
+
for (const panel of panels) {
|
|
90
|
+
const panelState = state.panels.get(panel);
|
|
91
|
+
if (!panelState) continue;
|
|
92
|
+
panel.hidden = panelState.hidden;
|
|
93
|
+
restoreAttrs(panel, panelState.attrs);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
48
96
|
|
|
49
97
|
const select = (tab) => {
|
|
50
98
|
for (const t of tabs) {
|
|
@@ -70,14 +118,15 @@ export function initTabs({ root } = {}) {
|
|
|
70
118
|
const onClick = (e) => {
|
|
71
119
|
// `tabs` is filtered to this group, so membership (not mere DOM
|
|
72
120
|
// containment) is what isolates nested [data-bronto-tabs] groups.
|
|
73
|
-
const tab = e.target
|
|
121
|
+
const tab = closestSafe(e.target, '.ui-tab');
|
|
74
122
|
if (tab && tabs.includes(tab)) {
|
|
123
|
+
e.preventDefault();
|
|
75
124
|
select(tab);
|
|
76
125
|
tab.focus();
|
|
77
126
|
}
|
|
78
127
|
};
|
|
79
128
|
const onKey = (e) => {
|
|
80
|
-
const i = tabs.indexOf(e.target
|
|
129
|
+
const i = tabs.indexOf(closestSafe(e.target, '.ui-tab'));
|
|
81
130
|
if (i < 0) return;
|
|
82
131
|
let n = i;
|
|
83
132
|
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') n = (i + 1) % tabs.length;
|
|
@@ -90,14 +139,29 @@ export function initTabs({ root } = {}) {
|
|
|
90
139
|
select(tabs[n]);
|
|
91
140
|
tabs[n].focus();
|
|
92
141
|
};
|
|
93
|
-
select(tabs.find((t) => t.classList.contains('is-active')) || tabs[0]);
|
|
94
142
|
cleanups.push(
|
|
95
143
|
bindOnce(group, 'tabs', () => {
|
|
144
|
+
const state = rememberState();
|
|
145
|
+
if (list) list.setAttribute('role', 'tablist');
|
|
146
|
+
|
|
147
|
+
// APG: bind each tab to its panel (aria-controls) and back
|
|
148
|
+
// (aria-labelledby), minting stable ids only where absent.
|
|
149
|
+
for (const t of tabs) {
|
|
150
|
+
const p = panels.find((x) => x.dataset.panel === t.dataset.tab);
|
|
151
|
+
if (!p) continue;
|
|
152
|
+
const n = nextFieldUid();
|
|
153
|
+
if (!t.id) t.id = `bronto-tab-${n}`;
|
|
154
|
+
if (!p.id) p.id = `bronto-tabpanel-${n}`;
|
|
155
|
+
t.setAttribute('aria-controls', p.id);
|
|
156
|
+
p.setAttribute('aria-labelledby', t.id);
|
|
157
|
+
}
|
|
158
|
+
select(tabs.find((t) => t.classList.contains('is-active')) || tabs[0]);
|
|
96
159
|
group.addEventListener('click', onClick);
|
|
97
160
|
group.addEventListener('keydown', onKey);
|
|
98
161
|
return () => {
|
|
99
162
|
group.removeEventListener('click', onClick);
|
|
100
163
|
group.removeEventListener('keydown', onKey);
|
|
164
|
+
restoreState(state);
|
|
101
165
|
};
|
|
102
166
|
}),
|
|
103
167
|
);
|
package/behaviors/theme.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/behaviors/theme.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
|
|
1
|
+
import { hasDom, resolveHost, noop, bindOnce, collectHosts, closestSafe } from './internal.js';
|
|
2
2
|
|
|
3
3
|
const THEMES = ['light', 'dark'];
|
|
4
4
|
|
|
@@ -53,7 +53,18 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
|
|
|
53
53
|
if (!hasDom()) return noop;
|
|
54
54
|
const host = resolveHost(root);
|
|
55
55
|
if (!host) return noop;
|
|
56
|
-
const
|
|
56
|
+
const doc = host.nodeType === 9 ? host : host.ownerDocument || document;
|
|
57
|
+
const docEl = doc.documentElement;
|
|
58
|
+
const toggleStates = new Map();
|
|
59
|
+
|
|
60
|
+
const rememberToggle = (el) => {
|
|
61
|
+
if (!toggleStates.has(el)) {
|
|
62
|
+
toggleStates.set(el, {
|
|
63
|
+
had: el.hasAttribute('aria-pressed'),
|
|
64
|
+
value: el.getAttribute('aria-pressed'),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
57
68
|
|
|
58
69
|
const prefersDark = () =>
|
|
59
70
|
typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches;
|
|
@@ -67,6 +78,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
|
|
|
67
78
|
const reflect = () => {
|
|
68
79
|
const c = current();
|
|
69
80
|
collectHosts(host, '[data-bronto-theme-toggle]').forEach((el) => {
|
|
81
|
+
rememberToggle(el);
|
|
70
82
|
const forced = el.getAttribute('data-bronto-theme-toggle');
|
|
71
83
|
// A forced control is "pressed" when its theme is the active one;
|
|
72
84
|
// a plain toggle reflects whether dark is active.
|
|
@@ -76,8 +88,9 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
|
|
|
76
88
|
};
|
|
77
89
|
|
|
78
90
|
const onClick = (e) => {
|
|
79
|
-
const trigger = e.target
|
|
91
|
+
const trigger = closestSafe(e.target, '[data-bronto-theme-toggle]');
|
|
80
92
|
if (!trigger || !host.contains(trigger)) return;
|
|
93
|
+
e.preventDefault();
|
|
81
94
|
const forced = trigger.getAttribute('data-bronto-theme-toggle');
|
|
82
95
|
const next = THEMES.includes(forced) ? forced : current() === 'dark' ? 'light' : 'dark';
|
|
83
96
|
docEl.setAttribute('data-theme', next);
|
|
@@ -92,10 +105,17 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
|
|
|
92
105
|
);
|
|
93
106
|
};
|
|
94
107
|
|
|
95
|
-
applyStoredTheme({ storageKey });
|
|
108
|
+
applyStoredTheme({ storageKey, root: docEl });
|
|
96
109
|
reflect();
|
|
97
110
|
return bindOnce(host, 'themeToggle', () => {
|
|
98
111
|
host.addEventListener('click', onClick);
|
|
99
|
-
return () =>
|
|
112
|
+
return () => {
|
|
113
|
+
host.removeEventListener('click', onClick);
|
|
114
|
+
for (const [el, state] of toggleStates) {
|
|
115
|
+
if (state.had) el.setAttribute('aria-pressed', state.value);
|
|
116
|
+
else el.removeAttribute('aria-pressed');
|
|
117
|
+
}
|
|
118
|
+
toggleStates.clear();
|
|
119
|
+
};
|
|
100
120
|
});
|
|
101
121
|
}
|
package/classes/index.d.ts
CHANGED
|
@@ -959,9 +959,22 @@ export interface ValueAttrs {
|
|
|
959
959
|
'aria-valuemax': number;
|
|
960
960
|
style: { '--value': number };
|
|
961
961
|
}
|
|
962
|
+
export interface MeterValueAttrs extends ValueAttrs {
|
|
963
|
+
role: 'meter';
|
|
964
|
+
}
|
|
965
|
+
export interface ProgressValueAttrs extends ValueAttrs {
|
|
966
|
+
role: 'progressbar';
|
|
967
|
+
}
|
|
968
|
+
export interface IndeterminateProgressAttrs {
|
|
969
|
+
role: 'progressbar';
|
|
970
|
+
'aria-busy': 'true';
|
|
971
|
+
}
|
|
962
972
|
export interface Attrs {
|
|
963
|
-
meter(value: number, opts?: ValueRangeOpts):
|
|
964
|
-
progress(value: number, opts?: ValueRangeOpts):
|
|
973
|
+
meter(value: number, opts?: ValueRangeOpts): MeterValueAttrs;
|
|
974
|
+
progress(value: number, opts?: ValueRangeOpts): ProgressValueAttrs;
|
|
975
|
+
progress(value?: undefined, opts?: ValueRangeOpts): IndeterminateProgressAttrs;
|
|
976
|
+
dotbar(value: number, opts?: ValueRangeOpts): ProgressValueAttrs;
|
|
977
|
+
dotbar(value?: undefined, opts?: ValueRangeOpts): IndeterminateProgressAttrs;
|
|
965
978
|
}
|
|
966
979
|
/** Set the painted value AND its ARIA together so they cannot drift. */
|
|
967
980
|
export declare const attrs: Attrs;
|
package/classes/index.js
CHANGED
|
@@ -467,7 +467,6 @@ export const cls = Object.freeze({
|
|
|
467
467
|
crosshairLineX: 'ui-crosshair__line--x',
|
|
468
468
|
crosshairLineY: 'ui-crosshair__line--y',
|
|
469
469
|
crosshairBadge: 'ui-crosshair__badge',
|
|
470
|
-
readout: 'ui-readout',
|
|
471
470
|
// selection-state vocabulary (cross-cutting — css/selection.css)
|
|
472
471
|
sel: 'ui-sel',
|
|
473
472
|
selOn: 'ui-sel--on',
|
package/connectors/index.d.ts
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a numeric option with an optional fallback.
|
|
3
|
+
* @param {string} name
|
|
4
|
+
* @param {number | null | undefined} value
|
|
5
|
+
* @param {number | null | undefined} [fallback]
|
|
6
|
+
* @returns {number}
|
|
7
|
+
*/
|
|
8
|
+
export function finite(name: string, value: number | null | undefined, fallback?: number | null | undefined): number;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a non-negative numeric option with an optional fallback.
|
|
11
|
+
* @param {string} name
|
|
12
|
+
* @param {number | null | undefined} value
|
|
13
|
+
* @param {number | null | undefined} [fallback]
|
|
14
|
+
* @returns {number}
|
|
15
|
+
*/
|
|
16
|
+
export function dimension(name: string, value: number | null | undefined, fallback?: number | null | undefined): number;
|
|
17
|
+
/**
|
|
18
|
+
* @param {number} value
|
|
19
|
+
* @returns {number}
|
|
20
|
+
*/
|
|
21
|
+
export function roundNumber(value: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* @param {number} value
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
export function fmt(value: number): string;
|
|
27
|
+
/**
|
|
28
|
+
* @param {number} x
|
|
29
|
+
* @param {number} y
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function point(x: number, y: number): string;
|
|
33
|
+
/**
|
|
34
|
+
* @param {number} value
|
|
35
|
+
* @param {number} min
|
|
36
|
+
* @param {number} max
|
|
37
|
+
* @returns {number}
|
|
38
|
+
*/
|
|
39
|
+
export function clamp(value: number, min: number, max: number): number;
|
|
7
40
|
/**
|
|
8
41
|
* A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
|
|
9
42
|
* @param {Rect} rect
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAkDA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAkDA;;;;;;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;AAKD;;;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;AAID;;;;;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;AAjWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAMH,wBAAyB,IAAI,CAAC;oBAhCjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;mBACxB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;mBACvD,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ;6BAC9C,UAAU,GAAG,OAAO,GAAG,OAAO;;UAG7B,KAAK;QACL,KAAK;;;;;;;;;;;;cAML,IAAI;YACJ,IAAI;;;;;;;;;;;;;;OAQJ,MAAM;UACN,KAAK;QACL,KAAK;;;;WACL,MAAM"}
|
package/connectors/index.js
CHANGED
|
@@ -48,12 +48,26 @@
|
|
|
48
48
|
// builders below.
|
|
49
49
|
export const PRECISION = 1000;
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a numeric option with an optional fallback.
|
|
53
|
+
* @param {string} name
|
|
54
|
+
* @param {number | null | undefined} value
|
|
55
|
+
* @param {number | null | undefined} [fallback]
|
|
56
|
+
* @returns {number}
|
|
57
|
+
*/
|
|
51
58
|
export function finite(name, value, fallback) {
|
|
52
59
|
const v = value ?? fallback;
|
|
53
60
|
if (!Number.isFinite(v)) throw new TypeError(`${name} must be a finite number`);
|
|
54
61
|
return v;
|
|
55
62
|
}
|
|
56
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a non-negative numeric option with an optional fallback.
|
|
66
|
+
* @param {string} name
|
|
67
|
+
* @param {number | null | undefined} value
|
|
68
|
+
* @param {number | null | undefined} [fallback]
|
|
69
|
+
* @returns {number}
|
|
70
|
+
*/
|
|
57
71
|
export function dimension(name, value, fallback) {
|
|
58
72
|
const v = finite(name, value, fallback);
|
|
59
73
|
if (v < 0) throw new RangeError(`${name} must be greater than or equal to 0`);
|
|
@@ -63,26 +77,64 @@ export function dimension(name, value, fallback) {
|
|
|
63
77
|
// Round to PRECISION, normalising -0 → 0, and return the NUMBER (the numeric
|
|
64
78
|
// core `fmt` stringifies). Shared with the annotations layer for the rounded
|
|
65
79
|
// coordinates it echoes back to the host. (code-quality audit Q5.)
|
|
80
|
+
/**
|
|
81
|
+
* @param {number} value
|
|
82
|
+
* @returns {number}
|
|
83
|
+
*/
|
|
66
84
|
export function roundNumber(value) {
|
|
67
85
|
const rounded = Math.round((Object.is(value, -0) ? 0 : value) * PRECISION) / PRECISION;
|
|
68
86
|
return Object.is(rounded, -0) ? 0 : rounded;
|
|
69
87
|
}
|
|
70
88
|
|
|
89
|
+
/**
|
|
90
|
+
* @param {number} value
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
71
93
|
export function fmt(value) {
|
|
72
94
|
return String(roundNumber(value));
|
|
73
95
|
}
|
|
74
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {number} x
|
|
99
|
+
* @param {number} y
|
|
100
|
+
* @returns {string}
|
|
101
|
+
*/
|
|
75
102
|
export function point(x, y) {
|
|
76
103
|
return `${fmt(x)},${fmt(y)}`;
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
// Guarded form (returns min when the range is inverted) — the reconciled body;
|
|
80
107
|
// connectors only ever calls clamp(v, 0, 1) so this is output-identical here.
|
|
108
|
+
/**
|
|
109
|
+
* @param {number} value
|
|
110
|
+
* @param {number} min
|
|
111
|
+
* @param {number} max
|
|
112
|
+
* @returns {number}
|
|
113
|
+
*/
|
|
81
114
|
export function clamp(value, min, max) {
|
|
82
115
|
if (max < min) return min;
|
|
83
116
|
return Math.min(max, Math.max(min, value));
|
|
84
117
|
}
|
|
85
118
|
|
|
119
|
+
function connectorShape(value) {
|
|
120
|
+
const shape = value ?? 'straight';
|
|
121
|
+
if (shape === 'straight' || shape === 'elbow' || shape === 'curve') return shape;
|
|
122
|
+
throw new TypeError('shape must be "straight", "elbow" or "curve"');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function sideValue(value) {
|
|
126
|
+
const side = value ?? 'center';
|
|
127
|
+
if (
|
|
128
|
+
side === 'top' ||
|
|
129
|
+
side === 'right' ||
|
|
130
|
+
side === 'bottom' ||
|
|
131
|
+
side === 'left' ||
|
|
132
|
+
side === 'center'
|
|
133
|
+
)
|
|
134
|
+
return side;
|
|
135
|
+
throw new TypeError('side must be "top", "right", "bottom", "left" or "center"');
|
|
136
|
+
}
|
|
137
|
+
|
|
86
138
|
/**
|
|
87
139
|
* A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
|
|
88
140
|
* @param {Rect} rect
|
|
@@ -90,11 +142,11 @@ export function clamp(value, min, max) {
|
|
|
90
142
|
* @returns {Point}
|
|
91
143
|
*/
|
|
92
144
|
export function anchorPoint(rect, side = 'center') {
|
|
93
|
-
const x = finite('rect.x', rect?.x
|
|
94
|
-
const y = finite('rect.y', rect?.y
|
|
95
|
-
const w = dimension('rect.width', rect?.width
|
|
96
|
-
const h = dimension('rect.height', rect?.height
|
|
97
|
-
switch (side) {
|
|
145
|
+
const x = finite('rect.x', rect?.x);
|
|
146
|
+
const y = finite('rect.y', rect?.y);
|
|
147
|
+
const w = dimension('rect.width', rect?.width);
|
|
148
|
+
const h = dimension('rect.height', rect?.height);
|
|
149
|
+
switch (sideValue(side)) {
|
|
98
150
|
case 'top':
|
|
99
151
|
return { x: x + w / 2, y };
|
|
100
152
|
case 'bottom':
|
|
@@ -185,7 +237,8 @@ export function curvePath(from, to, opts = {}) {
|
|
|
185
237
|
* @returns {string}
|
|
186
238
|
*/
|
|
187
239
|
export function connectorPath(opts = {}) {
|
|
188
|
-
const { from, to
|
|
240
|
+
const { from, to } = opts;
|
|
241
|
+
const shape = connectorShape(opts.shape);
|
|
189
242
|
if (shape === 'elbow') return elbowPath(from, to, opts);
|
|
190
243
|
if (shape === 'curve') return curvePath(from, to, opts);
|
|
191
244
|
return straightPath(from, to);
|
|
@@ -270,7 +323,8 @@ export function autoSides(fromRect, toRect) {
|
|
|
270
323
|
* @returns {number}
|
|
271
324
|
*/
|
|
272
325
|
export function endTangentAngle(from, to, shape = 'straight') {
|
|
273
|
-
|
|
326
|
+
const resolved = connectorShape(shape);
|
|
327
|
+
if (resolved === 'straight') return angleBetween(from, to);
|
|
274
328
|
const dx = finite('to.x', to?.x) - finite('from.x', from?.x);
|
|
275
329
|
const dy = finite('to.y', to?.y) - finite('from.y', from?.y);
|
|
276
330
|
if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? 0 : Math.PI;
|
|
@@ -285,10 +339,14 @@ export function endTangentAngle(from, to, shape = 'straight') {
|
|
|
285
339
|
* @returns {ConnectRectsResult}
|
|
286
340
|
*/
|
|
287
341
|
export function connectRects(opts = {}) {
|
|
288
|
-
const { fromRect, toRect,
|
|
342
|
+
const { fromRect, toRect, curvature, mid } = opts;
|
|
343
|
+
const shape = connectorShape(opts.shape);
|
|
289
344
|
// Honor each side override independently; auto-pick whichever is unset.
|
|
290
345
|
const auto = autoSides(fromRect, toRect);
|
|
291
|
-
const sides = {
|
|
346
|
+
const sides = {
|
|
347
|
+
from: opts.fromSide == null ? auto.from : sideValue(opts.fromSide),
|
|
348
|
+
to: opts.toSide == null ? auto.to : sideValue(opts.toSide),
|
|
349
|
+
};
|
|
292
350
|
const from = anchorPoint(fromRect, sides.from);
|
|
293
351
|
const to = anchorPoint(toRect, sides.to);
|
|
294
352
|
const d = connectorPath({ from, to, shape, curvature, mid });
|
package/css/annotations.css
CHANGED
|
@@ -278,8 +278,20 @@
|
|
|
278
278
|
animation: none !important;
|
|
279
279
|
opacity: 1;
|
|
280
280
|
stroke-dashoffset: 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.ui-annotation__subject,
|
|
284
|
+
.ui-annotation__connector,
|
|
285
|
+
.ui-annotation__note-line,
|
|
286
|
+
.ui-annotation__badge {
|
|
281
287
|
transform: none;
|
|
282
288
|
}
|
|
289
|
+
|
|
290
|
+
.ui-annotation--draw .ui-annotation__connector,
|
|
291
|
+
.ui-annotation--draw .ui-annotation__note-line {
|
|
292
|
+
stroke-dasharray: none;
|
|
293
|
+
stroke-dashoffset: 0;
|
|
294
|
+
}
|
|
283
295
|
}
|
|
284
296
|
|
|
285
297
|
@media (forced-colors: active) {
|
package/css/crosshair.css
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
--crosshair-x: 0;
|
|
14
14
|
--crosshair-y: 0;
|
|
15
15
|
--crosshair-color: var(--accent);
|
|
16
|
+
--crosshair-readout-gap: 0.35rem;
|
|
17
|
+
--crosshair-readout-x: var(--crosshair-readout-gap);
|
|
18
|
+
--crosshair-readout-y: var(--crosshair-readout-gap);
|
|
16
19
|
|
|
17
20
|
inset: 0;
|
|
18
21
|
opacity: 0;
|
|
@@ -25,6 +28,22 @@
|
|
|
25
28
|
opacity: 1;
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
.ui-crosshair[data-readout-inline='before'] {
|
|
32
|
+
--crosshair-readout-x: calc(-100% - var(--crosshair-readout-gap));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ui-crosshair[data-readout-block='above'] {
|
|
36
|
+
--crosshair-readout-y: calc(-100% - var(--crosshair-readout-gap));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ui-crosshair:dir(rtl) {
|
|
40
|
+
--crosshair-readout-x: calc(100% + var(--crosshair-readout-gap));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ui-crosshair:dir(rtl)[data-readout-inline='before'] {
|
|
44
|
+
--crosshair-readout-x: calc(-1 * var(--crosshair-readout-gap));
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
@media (prefers-reduced-motion: reduce) {
|
|
29
48
|
.ui-crosshair {
|
|
30
49
|
transition: none;
|
|
@@ -76,8 +95,9 @@
|
|
|
76
95
|
color: var(--text);
|
|
77
96
|
}
|
|
78
97
|
|
|
79
|
-
/* A pinned readout chip — host fills the content; it follows the crosshair.
|
|
80
|
-
|
|
98
|
+
/* A pinned readout chip — host fills the content; it follows the crosshair.
|
|
99
|
+
Scoped so report-kit/crosshair imports do not restyle standalone dot readouts. */
|
|
100
|
+
.ui-crosshair .ui-readout {
|
|
81
101
|
background: var(--panel);
|
|
82
102
|
border: 1px solid var(--line);
|
|
83
103
|
border-radius: var(--radius-sm);
|
|
@@ -87,10 +107,15 @@
|
|
|
87
107
|
font-size: var(--text-xs);
|
|
88
108
|
inset-block-start: var(--crosshair-y);
|
|
89
109
|
inset-inline-start: var(--crosshair-x);
|
|
110
|
+
max-inline-size: calc(100% - var(--crosshair-readout-gap) * 2);
|
|
111
|
+
overflow: hidden;
|
|
90
112
|
padding-block: 0.2rem;
|
|
91
113
|
padding-inline: 0.4rem;
|
|
92
114
|
pointer-events: none;
|
|
93
115
|
position: absolute;
|
|
116
|
+
text-overflow: ellipsis;
|
|
117
|
+
transform: translate(var(--crosshair-readout-x), var(--crosshair-readout-y));
|
|
118
|
+
white-space: nowrap;
|
|
94
119
|
}
|
|
95
120
|
|
|
96
121
|
@media (forced-colors: active) {
|
package/css/feedback.css
CHANGED
|
@@ -335,9 +335,7 @@
|
|
|
335
335
|
position: absolute;
|
|
336
336
|
text-transform: uppercase;
|
|
337
337
|
transform: translate(-50%, 4px);
|
|
338
|
-
transition:
|
|
339
|
-
opacity var(--duration-fast) var(--ease-standard),
|
|
340
|
-
transform var(--duration-fast) var(--ease-standard);
|
|
338
|
+
transition: opacity var(--duration-fast) var(--ease-standard);
|
|
341
339
|
white-space: nowrap;
|
|
342
340
|
z-index: var(--z-popover);
|
|
343
341
|
}
|
|
@@ -348,33 +346,6 @@
|
|
|
348
346
|
transform: translate(-50%, 0);
|
|
349
347
|
}
|
|
350
348
|
|
|
351
|
-
/* Progressive enhancement: where CSS anchor positioning exists, lift
|
|
352
|
-
the bubble out of the normal flow so it can't be clipped by an
|
|
353
|
-
ancestor's overflow/scroll and auto-flips at the viewport edge.
|
|
354
|
-
Unsupported browsers keep the absolutely-positioned fallback above
|
|
355
|
-
(fine for short labels; use .ui-popover + initPopover for rich or
|
|
356
|
-
edge-critical content). */
|
|
357
|
-
@supports (anchor-name: --x) {
|
|
358
|
-
.ui-tooltip {
|
|
359
|
-
anchor-name: --ui-tooltip;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.ui-tooltip__bubble {
|
|
363
|
-
inset: auto;
|
|
364
|
-
margin-block-end: 0.5rem;
|
|
365
|
-
position: fixed;
|
|
366
|
-
position-anchor: --ui-tooltip;
|
|
367
|
-
position-area: block-start center;
|
|
368
|
-
position-try-fallbacks: flip-block;
|
|
369
|
-
transform: translateY(4px);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.ui-tooltip:hover .ui-tooltip__bubble,
|
|
373
|
-
.ui-tooltip:focus-within .ui-tooltip__bubble {
|
|
374
|
-
transform: translateY(0);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
349
|
/* Popover surface — a top-layer panel positioned by initPopover (JS
|
|
379
350
|
collision-aware, dependency-free). Uses the native [popover] top
|
|
380
351
|
layer when available so it never clips; the class styles it either
|
|
@@ -389,6 +360,7 @@
|
|
|
389
360
|
inline-size: max-content;
|
|
390
361
|
margin: 0;
|
|
391
362
|
max-inline-size: min(22rem, calc(100vw - 2rem));
|
|
363
|
+
overflow: auto;
|
|
392
364
|
padding: var(--space-sm) var(--space-md);
|
|
393
365
|
position: fixed;
|
|
394
366
|
z-index: var(--z-popover);
|
package/css/navigation.css
CHANGED
|
@@ -70,6 +70,18 @@
|
|
|
70
70
|
transform: translateX(0.6rem);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
@media (prefers-color-scheme: dark) {
|
|
74
|
+
:root:not([data-theme='light']) .ui-themetoggle__thumb {
|
|
75
|
+
background: var(--accent);
|
|
76
|
+
transform: translateX(0.6rem);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
:root[dir='rtl']:not([data-theme='light']) .ui-themetoggle__thumb,
|
|
80
|
+
:root:not([data-theme='light']) [dir='rtl'] .ui-themetoggle__thumb {
|
|
81
|
+
transform: translateX(-0.6rem);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
73
85
|
[dir='rtl'][data-theme='dark'] .ui-themetoggle__thumb,
|
|
74
86
|
[dir='rtl'] [data-theme='dark'] .ui-themetoggle__thumb {
|
|
75
87
|
transform: translateX(-0.6rem);
|
package/css/tokens.css
CHANGED
|
@@ -360,9 +360,25 @@
|
|
|
360
360
|
--line: #d9d9d9;
|
|
361
361
|
--line-strong: #b3b3b3;
|
|
362
362
|
--success: #2f7d4f;
|
|
363
|
+
--success-soft: rgb(47, 125, 79, 0.12);
|
|
363
364
|
--danger: #c01622;
|
|
365
|
+
--danger-soft: rgb(192, 22, 34, 0.1);
|
|
364
366
|
--warning: #806414;
|
|
367
|
+
--warning-soft: rgb(128, 100, 20, 0.13);
|
|
365
368
|
--info: #1f63c4;
|
|
369
|
+
--info-soft: rgb(31, 99, 196, 0.12);
|
|
366
370
|
--accent: #d71921;
|
|
371
|
+
--bg-accent: color-mix(in srgb, var(--accent) 6%, transparent);
|
|
372
|
+
--accent-ramp-end: #ffffff;
|
|
373
|
+
--accent-strong: color-mix(in srgb, var(--accent) 83%, #000);
|
|
374
|
+
--accent-text: var(--accent-strong);
|
|
375
|
+
--accent-soft: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
376
|
+
--button-text: #ffffff;
|
|
377
|
+
--on-accent: var(--button-text);
|
|
378
|
+
--field-dot: rgb(17, 17, 17, 0.16);
|
|
379
|
+
--field-dot-hot: rgb(17, 17, 17, 0.4);
|
|
380
|
+
--field-dot-accent: color-mix(in srgb, var(--accent) 78%, transparent);
|
|
381
|
+
--focus-ring: var(--accent);
|
|
382
|
+
--code-bg: rgb(17, 17, 17, 0.05);
|
|
367
383
|
}
|
|
368
384
|
}
|