@fusion.dev/helix-headless 0.22.0 → 0.24.0
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/custom-elements.json +48397 -26631
- package/dist/announcement/root/announcement-root.d.ts +19 -5
- package/dist/area-chart/index.d.ts +1 -0
- package/dist/area-chart/index.js +1 -0
- package/dist/area-chart/root/area-chart-root.d.ts +182 -0
- package/dist/area-chart/root/area-chart-root.js +205 -0
- package/dist/carousel/root/carousel-root.d.ts +1 -0
- package/dist/carousel/root/carousel-root.js +18 -2
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/message/chat-message.d.ts +7 -0
- package/dist/chat/message/chat-message.js +2 -1
- package/dist/chat/root/chat-root.d.ts +195 -4
- package/dist/chat/root/chat-root.js +316 -23
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
- package/dist/checkbox-group/root/checkbox-group-root.js +12 -2
- package/dist/code-block/index.d.ts +1 -0
- package/dist/code-block/index.js +1 -0
- package/dist/code-block/patch.d.ts +62 -0
- package/dist/code-block/patch.js +93 -0
- package/dist/code-block/root/code-block-root.d.ts +82 -17
- package/dist/code-block/root/code-block-root.js +83 -6
- package/dist/color-picker/root/color-picker-root.d.ts +4 -3
- package/dist/color-picker/root/color-picker-root.js +4 -1
- package/dist/data-table/root/data-table-root.d.ts +195 -23
- package/dist/data-table/root/data-table-root.js +308 -27
- package/dist/description-list/index.d.ts +2 -0
- package/dist/description-list/index.js +2 -0
- package/dist/description-list/item/description-list-item.d.ts +39 -0
- package/dist/description-list/item/description-list-item.js +26 -0
- package/dist/description-list/root/description-list-root.d.ts +56 -0
- package/dist/description-list/root/description-list-root.js +21 -0
- package/dist/element-events.generated.js +27 -6
- package/dist/filter-input/chip/filter-chip.d.ts +114 -0
- package/dist/filter-input/chip/filter-chip.js +61 -0
- package/dist/filter-input/field/filter-field.d.ts +128 -0
- package/dist/filter-input/field/filter-field.js +70 -0
- package/dist/filter-input/grammar.d.ts +96 -0
- package/dist/filter-input/grammar.js +133 -0
- package/dist/filter-input/index.d.ts +5 -0
- package/dist/filter-input/index.js +5 -0
- package/dist/filter-input/menu/filter-menu.d.ts +153 -0
- package/dist/filter-input/menu/filter-menu.js +164 -0
- package/dist/filter-input/operator/filter-operator.d.ts +105 -0
- package/dist/filter-input/operator/filter-operator.js +39 -0
- package/dist/filter-input/root/filter-input-root.d.ts +118 -9
- package/dist/filter-input/root/filter-input-root.js +96 -2
- package/dist/format-date/root/format-date-root.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/line-chart/root/line-chart-root.d.ts +9 -2
- package/dist/listbox/root/listbox-root.d.ts +8 -7
- package/dist/listbox/root/listbox-root.js +7 -2
- package/dist/menu/item/menu-item.d.ts +54 -6
- package/dist/menu/item/menu-item.js +31 -6
- package/dist/menu/root/menu-root.d.ts +23 -0
- package/dist/menu/root/menu-root.js +13 -2
- package/dist/notifications/group/notification-group.d.ts +129 -0
- package/dist/notifications/group/notification-group.js +106 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.js +1 -0
- package/dist/notifications/notification/notification.d.ts +39 -13
- package/dist/notifications/notification/notification.js +11 -8
- package/dist/notifications/root/notifications-root.d.ts +57 -1
- package/dist/notifications/root/notifications-root.js +35 -4
- package/dist/number-input/root/number-input-root.d.ts +21 -1
- package/dist/number-input/root/number-input-root.js +18 -5
- package/dist/object-inspector/index.d.ts +1 -0
- package/dist/object-inspector/index.js +1 -0
- package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
- package/dist/object-inspector/root/object-inspector-root.js +348 -0
- package/dist/outline/root/outline-root.d.ts +16 -1
- package/dist/outline/root/outline-root.js +27 -9
- package/dist/radio/group/radio-group.d.ts +1 -0
- package/dist/scatter-chart/index.d.ts +1 -0
- package/dist/scatter-chart/index.js +1 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
- package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
- package/dist/skip-link/index.d.ts +1 -0
- package/dist/skip-link/index.js +1 -0
- package/dist/skip-link/root/skip-link-root.d.ts +81 -0
- package/dist/skip-link/root/skip-link-root.js +58 -0
- package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
- package/dist/speed-dial/root/speed-dial-root.js +6 -2
- package/dist/steps/root/steps-root.d.ts +1 -0
- package/dist/table/column/table-column.d.ts +79 -0
- package/dist/table/column/table-column.js +125 -5
- package/dist/table/root/table-root.d.ts +2 -0
- package/dist/table/root/table-root.js +12 -3
- package/dist/time-picker/root/time-picker-root.d.ts +9 -0
- package/dist/time-picker/root/time-picker-root.js +5 -0
- package/dist/toast/root/toast-root.d.ts +2 -0
- package/dist/tooltip/root/tooltip-root.js +1 -1
- package/dist/tour/root/tour-root.d.ts +1 -0
- package/dist/tree/item/tree-item.d.ts +7 -4
- package/dist/tree/item/tree-item.js +4 -1
- package/dist/video/root/video-root.d.ts +23 -1
- package/dist/video/root/video-root.js +42 -22
- package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
- package/dist/virtual-list/root/virtual-list-root.js +10 -4
- package/package.json +5 -5
- package/types/jsx.d.ts +4535 -346
- package/types/svelte.d.ts +11 -0
- package/types/vue.d.ts +737 -39
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { CopyController, customElement, HelixElement, VirtualizeController, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { caretDown, caretRight } from '@fusion.dev/helix-icons';
|
|
9
|
+
import { html, nothing } from 'lit';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
|
+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
|
+
import { html as staticHtml } from 'lit/static-html.js';
|
|
13
|
+
import '../../copy-button/index.js';
|
|
14
|
+
const branchy = (value) => value !== null && typeof value === 'object';
|
|
15
|
+
const entries = (value) => Array.isArray(value) ? value.map((item, at) => [String(at), item]) : Object.entries(value);
|
|
16
|
+
const child = (path, key) => `${path}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`;
|
|
17
|
+
let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends HelixElement {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(...arguments);
|
|
20
|
+
this.defaultDepth = 1;
|
|
21
|
+
this.for = '';
|
|
22
|
+
this.label = '';
|
|
23
|
+
this.noCopy = false;
|
|
24
|
+
this.value = undefined;
|
|
25
|
+
this.windowed = false;
|
|
26
|
+
this.read = undefined;
|
|
27
|
+
this.opened = new Set();
|
|
28
|
+
this.active = 0;
|
|
29
|
+
this.revealing = false;
|
|
30
|
+
this.virtualize = new VirtualizeController(this, {
|
|
31
|
+
container: () => this.windowed ? this.renderRoot?.querySelector('[part="tree"]') : null,
|
|
32
|
+
count: () => this.rows.length,
|
|
33
|
+
estimate: () => 24,
|
|
34
|
+
measured: () => this.windowed,
|
|
35
|
+
rows: () => [...(this.renderRoot?.querySelectorAll('[part="row"]') ?? [])].map((element) => ({ element, index: Number(element.dataset.index) })),
|
|
36
|
+
});
|
|
37
|
+
this.rows = [];
|
|
38
|
+
this.copier = new CopyController(this);
|
|
39
|
+
this.handleKeydown = (event) => {
|
|
40
|
+
const rows = this.rows;
|
|
41
|
+
const row = rows[this.active];
|
|
42
|
+
if (!row) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'c') {
|
|
46
|
+
if (globalThis.getSelection?.()?.toString()) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
void this.copier.copy(this.copyable(row));
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
switch (event.key) {
|
|
54
|
+
case 'ArrowDown':
|
|
55
|
+
this.move(this.active + 1);
|
|
56
|
+
break;
|
|
57
|
+
case 'ArrowUp':
|
|
58
|
+
this.move(this.active - 1);
|
|
59
|
+
break;
|
|
60
|
+
case 'Home':
|
|
61
|
+
this.move(0);
|
|
62
|
+
break;
|
|
63
|
+
case 'End':
|
|
64
|
+
this.move(rows.length - 1);
|
|
65
|
+
break;
|
|
66
|
+
case 'ArrowRight':
|
|
67
|
+
if (row.branch && !row.open) {
|
|
68
|
+
this.toggle(row, true);
|
|
69
|
+
}
|
|
70
|
+
else if (row.open) {
|
|
71
|
+
this.move(this.active + 1);
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
case 'ArrowLeft':
|
|
75
|
+
if (row.open) {
|
|
76
|
+
this.toggle(row, false);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const parent = rows.findLastIndex((candidate, at) => at < this.active && candidate.depth === row.depth - 1);
|
|
80
|
+
if (parent >= 0) {
|
|
81
|
+
this.move(parent);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'Enter':
|
|
86
|
+
case ' ':
|
|
87
|
+
if (row.branch) {
|
|
88
|
+
this.toggle(row);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
get root() {
|
|
98
|
+
return this.value === undefined ? this.read : this.value;
|
|
99
|
+
}
|
|
100
|
+
flatten() {
|
|
101
|
+
const rows = [];
|
|
102
|
+
if (this.root === undefined) {
|
|
103
|
+
return rows;
|
|
104
|
+
}
|
|
105
|
+
const visit = (value, path, depth, position, siblings, key) => {
|
|
106
|
+
const branch = branchy(value);
|
|
107
|
+
const open = branch && this.opened.has(path);
|
|
108
|
+
rows.push({
|
|
109
|
+
branch,
|
|
110
|
+
depth,
|
|
111
|
+
open,
|
|
112
|
+
path,
|
|
113
|
+
position,
|
|
114
|
+
siblings,
|
|
115
|
+
value,
|
|
116
|
+
...(key === undefined ? {} : { key }),
|
|
117
|
+
});
|
|
118
|
+
if (open) {
|
|
119
|
+
const children = entries(value);
|
|
120
|
+
children.forEach(([name, item], at) => {
|
|
121
|
+
visit(item, child(path, name), depth + 1, at + 1, children.length, name);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
visit(this.root, '', 0, 1, 1);
|
|
126
|
+
return rows;
|
|
127
|
+
}
|
|
128
|
+
seed() {
|
|
129
|
+
const opened = new Set();
|
|
130
|
+
const visit = (value, path, depth) => {
|
|
131
|
+
if (!branchy(value) || depth >= this.defaultDepth) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
opened.add(path);
|
|
135
|
+
for (const [name, item] of entries(value)) {
|
|
136
|
+
visit(item, child(path, name), depth + 1);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
visit(this.root, '', 0);
|
|
140
|
+
this.opened = opened;
|
|
141
|
+
this.active = 0;
|
|
142
|
+
}
|
|
143
|
+
willUpdate(changed) {
|
|
144
|
+
super.willUpdate(changed);
|
|
145
|
+
if (this.loadedFor !== this.for &&
|
|
146
|
+
typeof this.ownerDocument?.getElementById === 'function' &&
|
|
147
|
+
!this.hydrating) {
|
|
148
|
+
this.loadedFor = this.for;
|
|
149
|
+
this.load();
|
|
150
|
+
}
|
|
151
|
+
if (changed.has('value') || changed.has('read') || changed.has('defaultDepth')) {
|
|
152
|
+
this.seed();
|
|
153
|
+
}
|
|
154
|
+
if (changed.has('value') ||
|
|
155
|
+
changed.has('read') ||
|
|
156
|
+
changed.has('opened') ||
|
|
157
|
+
changed.has('defaultDepth')) {
|
|
158
|
+
this.rows = this.flatten();
|
|
159
|
+
this.active = Math.min(this.active, Math.max(0, this.rows.length - 1));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
firstUpdated(changed) {
|
|
163
|
+
super.firstUpdated(changed);
|
|
164
|
+
this.finishHydration();
|
|
165
|
+
}
|
|
166
|
+
updated(changed) {
|
|
167
|
+
super.updated?.(changed);
|
|
168
|
+
if (this.windowed) {
|
|
169
|
+
this.virtualize.measure();
|
|
170
|
+
}
|
|
171
|
+
if (this.revealing) {
|
|
172
|
+
this.revealing = false;
|
|
173
|
+
this.reveal();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
load() {
|
|
177
|
+
const text = this.for === '' ? '' : (this.ownerDocument.getElementById(this.for)?.textContent ?? '');
|
|
178
|
+
try {
|
|
179
|
+
this.read = text.trim() === '' ? undefined : JSON.parse(text);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
this.read = undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
reveal(attempts = 8) {
|
|
186
|
+
const tree = this.renderRoot?.querySelector('[part="tree"]');
|
|
187
|
+
const row = tree?.querySelector(`[data-index="${this.active}"]`);
|
|
188
|
+
if (row) {
|
|
189
|
+
row.scrollIntoView({ block: 'nearest' });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const drawn = [...(tree?.querySelectorAll('[part="row"]') ?? [])];
|
|
193
|
+
const first = drawn[0];
|
|
194
|
+
const last = drawn.at(-1);
|
|
195
|
+
if (!tree || !this.windowed || !first || !last || attempts === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const height = first.offsetHeight || 24;
|
|
199
|
+
const from = Number(this.active > Number(last.dataset.index) ? last.dataset.index : first.dataset.index);
|
|
200
|
+
tree.scrollTop += (this.active - from) * height;
|
|
201
|
+
this.virtualize.measure();
|
|
202
|
+
void this.updateComplete.then(() => this.reveal(attempts - 1));
|
|
203
|
+
}
|
|
204
|
+
copyable(row) {
|
|
205
|
+
if (!row) {
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
if (typeof row.value === 'string') {
|
|
209
|
+
return row.value;
|
|
210
|
+
}
|
|
211
|
+
return JSON.stringify(row.value, null, 2) ?? String(row.value);
|
|
212
|
+
}
|
|
213
|
+
toggle(row, open = !row.open) {
|
|
214
|
+
const opened = new Set(this.opened);
|
|
215
|
+
if (open) {
|
|
216
|
+
opened.add(row.path);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
opened.delete(row.path);
|
|
220
|
+
}
|
|
221
|
+
this.opened = opened;
|
|
222
|
+
}
|
|
223
|
+
move(to) {
|
|
224
|
+
const last = this.rows.length - 1;
|
|
225
|
+
this.active = Math.max(0, Math.min(to, last));
|
|
226
|
+
this.revealing = true;
|
|
227
|
+
}
|
|
228
|
+
handleRowClick(index, row) {
|
|
229
|
+
this.active = index;
|
|
230
|
+
if (row.branch) {
|
|
231
|
+
this.toggle(row);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
renderValue(row) {
|
|
235
|
+
const { value } = row;
|
|
236
|
+
if (row.branch) {
|
|
237
|
+
const count = entries(value).length;
|
|
238
|
+
const array = Array.isArray(value);
|
|
239
|
+
const noun = array ? (count === 1 ? 'item' : 'items') : count === 1 ? 'key' : 'keys';
|
|
240
|
+
return html `<span part="summary">${array ? '[…]' : '{…}'}</span>
|
|
241
|
+
<span part="count">${`${count} ${noun}`}</span>`;
|
|
242
|
+
}
|
|
243
|
+
if (typeof value === 'string') {
|
|
244
|
+
return html `<span part="string">${JSON.stringify(value)}</span>`;
|
|
245
|
+
}
|
|
246
|
+
if (typeof value === 'number') {
|
|
247
|
+
return html `<span part="number">${String(value)}</span>`;
|
|
248
|
+
}
|
|
249
|
+
if (typeof value === 'boolean') {
|
|
250
|
+
return html `<span part="boolean">${String(value)}</span>`;
|
|
251
|
+
}
|
|
252
|
+
if (value === null) {
|
|
253
|
+
return html `<span part="null">null</span>`;
|
|
254
|
+
}
|
|
255
|
+
return html `<span part="string">${String(value)}</span>`;
|
|
256
|
+
}
|
|
257
|
+
renderRow(row, index) {
|
|
258
|
+
return html `<div
|
|
259
|
+
part="row"
|
|
260
|
+
role="treeitem"
|
|
261
|
+
id=${`row-${index}`}
|
|
262
|
+
data-index=${index}
|
|
263
|
+
?data-active=${index === this.active}
|
|
264
|
+
aria-level=${row.depth + 1}
|
|
265
|
+
aria-posinset=${row.position}
|
|
266
|
+
aria-setsize=${row.siblings}
|
|
267
|
+
aria-expanded=${row.branch ? String(row.open) : nothing}
|
|
268
|
+
style=${`--object-inspector-level: ${row.depth}`}
|
|
269
|
+
@click=${() => this.handleRowClick(index, row)}
|
|
270
|
+
>
|
|
271
|
+
${row.branch
|
|
272
|
+
? html `<svg
|
|
273
|
+
part="toggle"
|
|
274
|
+
fill="currentColor"
|
|
275
|
+
viewBox=${(row.open ? caretDown : caretRight).viewBox}
|
|
276
|
+
aria-hidden="true"
|
|
277
|
+
>
|
|
278
|
+
${unsafeSVG((row.open ? caretDown : caretRight).content)}
|
|
279
|
+
</svg>`
|
|
280
|
+
: nothing}${row.key === undefined ? nothing : html `<span part="key">${row.key}</span>: `}${this.renderValue(row)}
|
|
281
|
+
</div>`;
|
|
282
|
+
}
|
|
283
|
+
renderCopyButton(row) {
|
|
284
|
+
return staticHtml `<${this.tag('copy-button')}
|
|
285
|
+
part="copy"
|
|
286
|
+
exportparts="button: copy-button, mark: copy-mark, tooltip: copy-tooltip,
|
|
287
|
+
tooltip-arrow: copy-tooltip-arrow"
|
|
288
|
+
label=${row?.key === undefined ? 'Copy value' : `Copy ${row.key}`}
|
|
289
|
+
.value=${this.copyable(row)}
|
|
290
|
+
></${this.tag('copy-button')}>`;
|
|
291
|
+
}
|
|
292
|
+
render() {
|
|
293
|
+
const rows = this.rows;
|
|
294
|
+
const { after, before, first, last } = this.windowed
|
|
295
|
+
? this.virtualize.window
|
|
296
|
+
: { after: 0, before: 0, first: 0, last: rows.length };
|
|
297
|
+
const shown = rows.slice(first, last);
|
|
298
|
+
return html `${this.label ? html `<div part="header"><span part="label">${this.label}</span></div>` : nothing}
|
|
299
|
+
<div part="frame">
|
|
300
|
+
<div
|
|
301
|
+
part="tree"
|
|
302
|
+
role="tree"
|
|
303
|
+
tabindex="0"
|
|
304
|
+
?data-scroller=${this.windowed}
|
|
305
|
+
aria-label=${this.label || 'Value'}
|
|
306
|
+
aria-activedescendant=${rows.length > 0 ? `row-${this.active}` : nothing}
|
|
307
|
+
@keydown=${this.handleKeydown}
|
|
308
|
+
@scroll=${() => this.virtualize.measure()}
|
|
309
|
+
>
|
|
310
|
+
${this.windowed ? html `<div part="before" role="presentation" style="block-size: ${before}px"></div>` : nothing}
|
|
311
|
+
${shown.map((row, at) => this.renderRow(row, first + at))}
|
|
312
|
+
${this.windowed ? html `<div part="after" role="presentation" style="block-size: ${after}px"></div>` : nothing}
|
|
313
|
+
</div>
|
|
314
|
+
${this.noCopy ? nothing : this.renderCopyButton(rows[this.active])}
|
|
315
|
+
</div>`;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
__decorate([
|
|
319
|
+
property({ attribute: 'default-depth', type: Number })
|
|
320
|
+
], HelixHeadlessObjectInspector.prototype, "defaultDepth", void 0);
|
|
321
|
+
__decorate([
|
|
322
|
+
property()
|
|
323
|
+
], HelixHeadlessObjectInspector.prototype, "for", void 0);
|
|
324
|
+
__decorate([
|
|
325
|
+
property()
|
|
326
|
+
], HelixHeadlessObjectInspector.prototype, "label", void 0);
|
|
327
|
+
__decorate([
|
|
328
|
+
property({ attribute: 'no-copy', reflect: true, type: Boolean })
|
|
329
|
+
], HelixHeadlessObjectInspector.prototype, "noCopy", void 0);
|
|
330
|
+
__decorate([
|
|
331
|
+
property({ attribute: false })
|
|
332
|
+
], HelixHeadlessObjectInspector.prototype, "value", void 0);
|
|
333
|
+
__decorate([
|
|
334
|
+
property({ reflect: true, type: Boolean })
|
|
335
|
+
], HelixHeadlessObjectInspector.prototype, "windowed", void 0);
|
|
336
|
+
__decorate([
|
|
337
|
+
state()
|
|
338
|
+
], HelixHeadlessObjectInspector.prototype, "read", void 0);
|
|
339
|
+
__decorate([
|
|
340
|
+
state()
|
|
341
|
+
], HelixHeadlessObjectInspector.prototype, "opened", void 0);
|
|
342
|
+
__decorate([
|
|
343
|
+
state()
|
|
344
|
+
], HelixHeadlessObjectInspector.prototype, "active", void 0);
|
|
345
|
+
HelixHeadlessObjectInspector = __decorate([
|
|
346
|
+
customElement('helix-headless-object-inspector')
|
|
347
|
+
], HelixHeadlessObjectInspector);
|
|
348
|
+
export { HelixHeadlessObjectInspector };
|
|
@@ -58,7 +58,8 @@ export declare class HelixHeadlessOutline extends HelixElement {
|
|
|
58
58
|
*/
|
|
59
59
|
label: string;
|
|
60
60
|
/**
|
|
61
|
-
* How far below the top of
|
|
61
|
+
* How far below the top of what scrolls a heading counts as reached, in
|
|
62
|
+
* pixels: the window, or the panel the sections scroll in.
|
|
62
63
|
*
|
|
63
64
|
* The reading line. A page with a sticky masthead sets this to its height, or
|
|
64
65
|
* a little more: without it the current section changes as a heading slides
|
|
@@ -119,6 +120,20 @@ export declare class HelixHeadlessOutline extends HelixElement {
|
|
|
119
120
|
* measured against.
|
|
120
121
|
*/
|
|
121
122
|
private get atEnd();
|
|
123
|
+
/** The element the page itself scrolls with, which `atEnd` explains. */
|
|
124
|
+
private get page();
|
|
125
|
+
/**
|
|
126
|
+
* What the sections scroll in: the nearest ancestor of the first one that
|
|
127
|
+
* scrolls, or the page when none does.
|
|
128
|
+
*
|
|
129
|
+
* **The window is not always what moves.** A contents rail beside a panel
|
|
130
|
+
* with its own scrollbar, which is a docs layout and every sandbox preview,
|
|
131
|
+
* measured the window. The window never scrolled, so it was always at its end
|
|
132
|
+
* and the last section was always current: the events example named `api`
|
|
133
|
+
* at rest with `install` on screen, a wheel over the panel changed nothing,
|
|
134
|
+
* and a press on `install` marked it for a moment and snapped back.
|
|
135
|
+
*/
|
|
136
|
+
private get scroller();
|
|
122
137
|
/** The frame a mark is already scheduled for, so a scroll asks for one. */
|
|
123
138
|
private frame;
|
|
124
139
|
/** Where a click said to go, while the page is still on its way there. */
|
|
@@ -47,10 +47,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
47
47
|
const arrive = () => {
|
|
48
48
|
clearTimeout(this.landed);
|
|
49
49
|
this.landing = undefined;
|
|
50
|
-
globalThis.removeEventListener
|
|
50
|
+
globalThis.document?.removeEventListener('scrollend', arrive, true);
|
|
51
51
|
this.mark();
|
|
52
52
|
};
|
|
53
|
-
globalThis.addEventListener
|
|
53
|
+
globalThis.document?.addEventListener('scrollend', arrive, { capture: true, once: true });
|
|
54
54
|
this.landed = setTimeout(arrive, 1200);
|
|
55
55
|
if (this.behavior === 'auto') {
|
|
56
56
|
return;
|
|
@@ -97,7 +97,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
97
97
|
if (sections.length === 0) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
const
|
|
100
|
+
const scroller = this.scroller;
|
|
101
|
+
const page = scroller === this.page;
|
|
102
|
+
const origin = page ? 0 : scroller.getBoundingClientRect().top;
|
|
103
|
+
const passed = sections.filter(({ target }) => target.getBoundingClientRect().top - origin - this.offset <= 1);
|
|
101
104
|
const active = this.atEnd ? sections.at(-1) : (passed.at(-1) ?? sections[0]);
|
|
102
105
|
this.markTo(active);
|
|
103
106
|
}
|
|
@@ -117,12 +120,24 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
get atEnd() {
|
|
120
|
-
const doc =
|
|
121
|
-
if (!doc) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
123
|
+
const doc = this.scroller;
|
|
124
124
|
return Math.ceil(doc.scrollTop + doc.clientHeight) >= doc.scrollHeight - 1;
|
|
125
125
|
}
|
|
126
|
+
get page() {
|
|
127
|
+
const doc = globalThis.document;
|
|
128
|
+
return (doc.scrollingElement ?? doc.documentElement);
|
|
129
|
+
}
|
|
130
|
+
get scroller() {
|
|
131
|
+
const doc = globalThis.document;
|
|
132
|
+
const first = this.sections[0]?.target;
|
|
133
|
+
for (let node = first?.parentElement; node && node !== doc.body && node !== doc.documentElement; node = node.parentElement) {
|
|
134
|
+
const { overflowY } = getComputedStyle(node);
|
|
135
|
+
if (/auto|scroll|overlay/.test(overflowY) && node.scrollHeight > node.clientHeight) {
|
|
136
|
+
return node;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return this.page;
|
|
140
|
+
}
|
|
126
141
|
reach() {
|
|
127
142
|
for (const { target } of this.sections) {
|
|
128
143
|
const element = target;
|
|
@@ -147,7 +162,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
147
162
|
disconnectedCallback() {
|
|
148
163
|
super.disconnectedCallback();
|
|
149
164
|
this.removeEventListener('click', this.handleClick);
|
|
150
|
-
globalThis.removeEventListener
|
|
165
|
+
globalThis.document?.removeEventListener('scroll', this.handleScroll, true);
|
|
151
166
|
if (this.frame !== 0) {
|
|
152
167
|
globalThis.cancelAnimationFrame(this.frame);
|
|
153
168
|
this.frame = 0;
|
|
@@ -162,7 +177,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
162
177
|
connectedCallback() {
|
|
163
178
|
super.connectedCallback();
|
|
164
179
|
this.addEventListener('click', this.handleClick);
|
|
165
|
-
globalThis.addEventListener
|
|
180
|
+
globalThis.document?.addEventListener('scroll', this.handleScroll, {
|
|
181
|
+
capture: true,
|
|
182
|
+
passive: true,
|
|
183
|
+
});
|
|
166
184
|
if (this.hasUpdated) {
|
|
167
185
|
this.watch();
|
|
168
186
|
this.resize = new ResizeObserver(() => this.indicator.place());
|
|
@@ -36,6 +36,7 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
|
|
|
36
36
|
*
|
|
37
37
|
* @event {Event} change - Announced once the value has moved.
|
|
38
38
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired when the answer changes, and cancelable.
|
|
39
|
+
* @event {HelixSelectEvent} helix-select - Fired when an answer is chosen. Dispatched by the radio and listened for here, because the group is what a consumer holds. Cancelable: refusing it takes the decision and nothing moves.
|
|
39
40
|
* @event {Event} input - Announced once the value has moved.
|
|
40
41
|
*
|
|
41
42
|
* @csspart feedback - What is wrong with it.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/scatter-chart-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/scatter-chart-root.js';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
3
|
+
import '../../tooltip/index.js';
|
|
4
|
+
/** One row drawn as a point: which row, which series, and where, in percent of the plot. */
|
|
5
|
+
export interface ScatterPoint {
|
|
6
|
+
depth: number;
|
|
7
|
+
row: number;
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
/** The table as the chart reads it: points, both ranges and what each axis is called. */
|
|
12
|
+
export interface ScatterField {
|
|
13
|
+
points: ScatterPoint[];
|
|
14
|
+
/** Each series' readings as pairs, for the summary. */
|
|
15
|
+
pairs: Array<Array<[number, number]>>;
|
|
16
|
+
x: [number, number];
|
|
17
|
+
xName: string;
|
|
18
|
+
y: [number, number];
|
|
19
|
+
yNames: string[];
|
|
20
|
+
}
|
|
21
|
+
declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
22
|
+
/**
|
|
23
|
+
* @summary Whether two measures move together, one point per row.
|
|
24
|
+
*
|
|
25
|
+
* @description
|
|
26
|
+
* The chart for a relation rather than a series. The table's first numeric
|
|
27
|
+
* column is measured across and every other numeric column is measured up, one
|
|
28
|
+
* point per row, so a table of countries with a column of income and a column
|
|
29
|
+
* of life expectancy draws one dot for each country. More numeric columns are
|
|
30
|
+
* more series against the same across, each in its own color.
|
|
31
|
+
*
|
|
32
|
+
* **Both axes are measured, so both are labeled.** A line chart's readings are
|
|
33
|
+
* named along the bottom and its values are read from the tooltip; here the
|
|
34
|
+
* horizontal position is itself a value, and a point with no scale under it says
|
|
35
|
+
* nothing about how far along it is. The ticks sit at round numbers and the
|
|
36
|
+
* range is widened to them, so the plot's edges are numbers a reader can name.
|
|
37
|
+
*
|
|
38
|
+
* **What a reader without the picture is told is how strongly the two relate**,
|
|
39
|
+
* because that is the question a scatter is drawn to answer and the one a table
|
|
40
|
+
* of the same numbers makes hardest to see. The summary gives each series'
|
|
41
|
+
* correlation coefficient and how many points it rests on; the table has every
|
|
42
|
+
* number, and the drawing is `aria-hidden`.
|
|
43
|
+
*
|
|
44
|
+
* **The points are elements, not shapes in the drawing.** The drawing stretches
|
|
45
|
+
* to its box, and a circle in a stretched space is an ellipse, so each point is
|
|
46
|
+
* a `<span>` placed by percentage over it, which is how `LineChart` draws its
|
|
47
|
+
* termini and for the same reason.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
* @status experimental
|
|
51
|
+
* @category charts
|
|
52
|
+
* @aliases Scatter Plot, XY Chart, Correlation Chart, Dot Plot
|
|
53
|
+
* @documentation https://helix.fusion.dev/components/scatter-chart
|
|
54
|
+
*
|
|
55
|
+
* @csspart gridline - One rule across the plot, under `gridlines`.
|
|
56
|
+
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
57
|
+
* @csspart plot - The drawing.
|
|
58
|
+
* @csspart point - One row, as a point.
|
|
59
|
+
* @csspart summary - How strongly the measures relate, for a reader who cannot see the points.
|
|
60
|
+
* @csspart svg - The drawing surface the rules sit on.
|
|
61
|
+
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
62
|
+
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
63
|
+
* @csspart tooltip-cell - One column's name and value inside the tooltip.
|
|
64
|
+
* @csspart tooltip-label - The row's own name inside the tooltip.
|
|
65
|
+
* @csspart tooltip-name - A column's name inside the tooltip.
|
|
66
|
+
* @csspart tooltip-value - A column's value inside the tooltip.
|
|
67
|
+
* @csspart x-axis - The levels measured across, under the plot.
|
|
68
|
+
* @csspart x-name - What is measured across.
|
|
69
|
+
* @csspart x-tick - One labeled level across. A part of its own rather than a shared `tick`, because `::part()` cannot tell which axis a tick is on.
|
|
70
|
+
* @csspart y-axis - The levels measured up, beside the plot.
|
|
71
|
+
* @csspart y-name - What is measured up.
|
|
72
|
+
* @csspart y-tick - One labeled level up.
|
|
73
|
+
*
|
|
74
|
+
* @cssproperty --chart-point-x - Where a point sits across, as a percentage. Written by the component.
|
|
75
|
+
* @cssproperty --chart-point-y - Where a point sits up, as a percentage from the top. Written by the component.
|
|
76
|
+
* @cssproperty --chart-tick - Where a tick sits along its axis, as a percentage. Written by the component.
|
|
77
|
+
*/
|
|
78
|
+
export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart_base {
|
|
79
|
+
/**
|
|
80
|
+
* What to call the strength of the relation in the summary.
|
|
81
|
+
*/
|
|
82
|
+
correlationLabel: string;
|
|
83
|
+
/**
|
|
84
|
+
* Which rules are drawn across the plot, if any.
|
|
85
|
+
*
|
|
86
|
+
* Both axes are measured, so the values name an axis rather than a role:
|
|
87
|
+
* `x` rules the levels across, `y` the levels up. A bare `gridlines`, or
|
|
88
|
+
* anything unrecognized, means `both`, since neither axis is the one a scatter
|
|
89
|
+
* is read along.
|
|
90
|
+
*/
|
|
91
|
+
gridlines?: 'both' | 'x' | 'y';
|
|
92
|
+
/**
|
|
93
|
+
* What to call a row in the summary, which counts them.
|
|
94
|
+
*/
|
|
95
|
+
pointsLabel: string;
|
|
96
|
+
/** The points as last drawn, which the pointer searches. */
|
|
97
|
+
private drawn;
|
|
98
|
+
/** The point a pointer is resting nearest, which the tooltip sits on. */
|
|
99
|
+
private nearest;
|
|
100
|
+
connectedCallback(): void;
|
|
101
|
+
disconnectedCallback(): void;
|
|
102
|
+
/** Whether the levels across are ruled. */
|
|
103
|
+
protected get rulesX(): boolean;
|
|
104
|
+
/** Whether the levels up are ruled. */
|
|
105
|
+
protected get rulesY(): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The table as points.
|
|
108
|
+
*
|
|
109
|
+
* Read from the rows rather than from `series`, because a point needs both of
|
|
110
|
+
* its numbers from the same row, and `series` numbers each column's readings
|
|
111
|
+
* on its own once a cell is blank. A row missing either number is not drawn:
|
|
112
|
+
* a point with one coordinate has nowhere to be.
|
|
113
|
+
*/
|
|
114
|
+
protected get field(): ScatterField;
|
|
115
|
+
protected render(): TemplateResult;
|
|
116
|
+
/** The rules and the points, behind a seam the styled tier can move. */
|
|
117
|
+
protected renderPlot(field: ScatterField, along: (value: number) => number, up: (value: number) => number): TemplateResult;
|
|
118
|
+
/**
|
|
119
|
+
* What a reader who cannot see the points is told.
|
|
120
|
+
*
|
|
121
|
+
* One clause per series, each naming what is measured up against what is
|
|
122
|
+
* measured across, how many rows it rests on and its correlation, which is
|
|
123
|
+
* absent where there are fewer than three points or no spread to measure.
|
|
124
|
+
*/
|
|
125
|
+
protected renderSummary(field: ScatterField): TemplateResult;
|
|
126
|
+
/** The row a pointer is resting on, over the point it is nearest. */
|
|
127
|
+
protected renderTooltip(): TemplateResult | typeof nothing;
|
|
128
|
+
/**
|
|
129
|
+
* The point a pointer is nearest, within reach.
|
|
130
|
+
*
|
|
131
|
+
* Measured in the plot's pixels rather than in percent, because the plot is
|
|
132
|
+
* rarely square and a percent across is a different distance from a percent
|
|
133
|
+
* up. A mouse or a pen only, as every chart here: a finger has no hover to
|
|
134
|
+
* rest on a point with.
|
|
135
|
+
*/
|
|
136
|
+
private readonly handleMove;
|
|
137
|
+
private readonly handleLeave;
|
|
138
|
+
}
|
|
139
|
+
declare global {
|
|
140
|
+
interface HTMLElementTagNameMap {
|
|
141
|
+
'helix-headless-scatter-chart': HelixHeadlessScatterChart;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export {};
|