@fusion.dev/helix-headless 0.147.0 → 0.148.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 +4512 -790
- package/dist/accordion/item/accordion-item.d.ts +5 -2
- package/dist/accordion/item/accordion-item.js +11 -7
- package/dist/accordion/root/accordion-root.js +4 -6
- package/dist/announcement/root/announcement-root.d.ts +56 -5
- package/dist/announcement/root/announcement-root.js +124 -21
- package/dist/area-chart/root/area-chart-root.js +3 -4
- package/dist/badge/count.d.ts +14 -0
- package/dist/badge/count.js +20 -0
- package/dist/badge/root/badge-root.js +8 -22
- package/dist/blockquote/root/blockquote-root.d.ts +66 -8
- package/dist/blockquote/root/blockquote-root.js +85 -8
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +2 -2
- package/dist/button-group/arrows.d.ts +14 -0
- package/dist/button-group/arrows.js +10 -0
- package/dist/button-group/root/button-group-root.d.ts +141 -3
- package/dist/button-group/root/button-group-root.js +142 -2
- package/dist/canvas/edge-pan.d.ts +61 -0
- package/dist/canvas/edge-pan.js +63 -0
- package/dist/canvas/node/canvas-node.d.ts +12 -2
- package/dist/canvas/node/canvas-node.js +6 -2
- package/dist/canvas/root/canvas-document.d.ts +10 -0
- package/dist/canvas/root/canvas-document.js +1 -0
- package/dist/canvas/root/canvas-root.d.ts +107 -5
- package/dist/canvas/root/canvas-root.js +140 -36
- package/dist/canvas/snap.d.ts +42 -0
- package/dist/canvas/snap.js +32 -0
- package/dist/carousel/root/carousel-root.d.ts +4 -3
- package/dist/carousel/root/carousel-root.js +2 -6
- package/dist/chat/root/chat-root.d.ts +13 -3
- package/dist/chat/root/chat-root.js +2 -6
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
- package/dist/checkbox-group/root/checkbox-group-root.js +18 -10
- package/dist/checkbox-group/select-all.d.ts +11 -0
- package/dist/checkbox-group/select-all.js +5 -0
- package/dist/code-block/root/code-block-root.d.ts +8 -6
- package/dist/code-block/root/code-block-root.js +4 -3
- package/dist/color-picker/root/color-picker-root.js +6 -12
- package/dist/color-picker/wheel.d.ts +11 -0
- package/dist/color-picker/wheel.js +9 -0
- package/dist/combobox/root/combobox-root.d.ts +70 -11
- package/dist/combobox/root/combobox-root.js +68 -18
- package/dist/command/root/command-root.d.ts +4 -3
- package/dist/data-table/root/data-table-root.js +17 -44
- package/dist/description-list/item/description-list-item.d.ts +4 -0
- package/dist/description-list/item/description-list-item.js +7 -1
- package/dist/divider/root/divider-root.d.ts +45 -16
- package/dist/divider/root/divider-root.js +28 -4
- package/dist/drilldown/item/drilldown-item.js +3 -4
- package/dist/element-events.generated.js +1 -0
- package/dist/empty/root/empty-root.d.ts +17 -3
- package/dist/empty/root/empty-root.js +29 -5
- package/dist/file-input/root/file-input-root.js +3 -4
- package/dist/file-input/status.js +2 -1
- package/dist/filter-input/chip/filter-chip.js +2 -3
- package/dist/format-date/fields.d.ts +20 -0
- package/dist/format-date/fields.js +14 -0
- package/dist/format-date/options.d.ts +2 -1
- package/dist/format-date/options.js +9 -38
- package/dist/format-date/root/format-date-root.d.ts +35 -3
- package/dist/format-date/root/format-date-root.js +24 -32
- package/dist/graph/root/graph-root.d.ts +23 -5
- package/dist/graph/root/graph-root.js +17 -20
- package/dist/heatmap/root/heatmap-root.d.ts +12 -0
- package/dist/heatmap/root/heatmap-root.js +26 -11
- package/dist/image/root/image-root.d.ts +11 -0
- package/dist/kbd/root/kbd-root.js +1 -2
- package/dist/legend/root/legend-root.js +4 -17
- package/dist/list/root/list-root.js +8 -22
- package/dist/listbox/root/listbox-root.d.ts +119 -24
- package/dist/listbox/root/listbox-root.js +130 -132
- package/dist/map/marker/map-marker.d.ts +2 -1
- package/dist/map/root/map-root.d.ts +40 -2
- package/dist/map/root/map-root.js +64 -23
- package/dist/megamenu/root/megamenu-root.js +4 -11
- package/dist/menu/root/menu-root.d.ts +2 -2
- package/dist/navigation/root/navigation-root.js +5 -8
- package/dist/notifications/group/notification-group.d.ts +8 -1
- package/dist/notifications/group/notification-group.js +12 -6
- package/dist/notifications/notification/notification.d.ts +12 -1
- package/dist/notifications/notification/notification.js +3 -1
- package/dist/notifications/root/notifications-root.d.ts +41 -5
- package/dist/notifications/root/notifications-root.js +27 -7
- package/dist/object-inspector/root/object-inspector-root.d.ts +5 -1
- package/dist/object-inspector/root/object-inspector-root.js +21 -11
- package/dist/pagination/size/pagination-size.d.ts +1 -0
- package/dist/pagination/size/pagination-size.js +1 -1
- package/dist/pin-input/code.d.ts +23 -0
- package/dist/pin-input/code.js +25 -0
- package/dist/pin-input/root/pin-input-root.js +9 -23
- package/dist/qr-code/root/qr-code-root.d.ts +14 -3
- package/dist/qr-code/root/qr-code-root.js +19 -5
- package/dist/scatter-chart/measures.d.ts +22 -0
- package/dist/scatter-chart/measures.js +11 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +2 -6
- package/dist/scatter-chart/root/scatter-chart-root.js +6 -19
- package/dist/select/root/select-root.d.ts +56 -8
- package/dist/select/root/select-root.js +33 -9
- package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
- package/dist/speed-dial/group/speed-dial-group.js +43 -0
- package/dist/speed-dial/index.d.ts +1 -0
- package/dist/speed-dial/index.js +1 -0
- package/dist/speed-dial/item/speed-dial-item.d.ts +26 -1
- package/dist/speed-dial/item/speed-dial-item.js +41 -3
- package/dist/speed-dial/long-press.d.ts +50 -0
- package/dist/speed-dial/long-press.js +48 -0
- package/dist/speed-dial/root/speed-dial-root.d.ts +80 -1
- package/dist/speed-dial/root/speed-dial-root.js +114 -5
- package/dist/splitter/root/splitter-root.d.ts +7 -3
- package/dist/splitter/root/splitter-root.js +16 -3
- package/dist/table/cell/table-cell.d.ts +8 -1
- package/dist/table/cell/table-cell.js +6 -2
- package/dist/table/grid-controller.d.ts +117 -0
- package/dist/table/grid-controller.js +293 -0
- package/dist/table/root/table-root.d.ts +47 -3
- package/dist/table/root/table-root.js +81 -24
- package/dist/table/row/table-row.d.ts +13 -1
- package/dist/table/row/table-row.js +20 -6
- package/dist/tag/group/tag-group.js +6 -15
- package/dist/time-picker/root/time-picker-root.d.ts +1 -0
- package/dist/time-picker/root/time-picker-root.js +3 -0
- package/dist/toast/item/toast-item.d.ts +6 -3
- package/dist/toast/item/toast-item.js +1 -0
- package/dist/toast/root/toast-root.d.ts +13 -1
- package/dist/toast/root/toast-root.js +20 -14
- package/dist/toggle/group/toggle-group.js +3 -5
- package/dist/toolbar/root/toolbar-root.d.ts +31 -0
- package/dist/toolbar/root/toolbar-root.js +39 -8
- package/dist/tooltip/root/tooltip-root.js +1 -2
- package/dist/video/root/video-root.d.ts +11 -0
- package/dist/video/root/video-root.js +15 -1
- package/dist/virtual-list/root/virtual-list-root.js +6 -18
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +2005 -266
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +338 -62
- package/dist/kbd/keys.d.ts +0 -29
- package/dist/kbd/keys.js +0 -52
- package/dist/tooltip/focus-target.d.ts +0 -6
- package/dist/tooltip/focus-target.js +0 -9
|
@@ -4,20 +4,31 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
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
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { anchorElement, customElement, drawIcon, HelixElement, KeyboardNavigationController, MixinControllerOpen, PopoverController, slottedWords, tellDisclosure,
|
|
7
|
+
import { anchorElement, customElement, drawIcon, focusTarget, HelixElement, KeyboardNavigationController, linkDescription, MixinControllerOpen, PopoverController, slottedWords, tellDisclosure, typeaheadByWords, unlinkDescription, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { plus } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
11
|
+
import { LongPress } from '../long-press.js';
|
|
12
|
+
const navigable = (item) => ({
|
|
13
|
+
contains: (node) => item.contains(node),
|
|
14
|
+
disabled: item.disabled,
|
|
15
|
+
focusItem: () => item.focusItem(),
|
|
16
|
+
select: () => item.select(),
|
|
17
|
+
selected: item.active,
|
|
18
|
+
words: slottedWords(item, ['']),
|
|
19
|
+
});
|
|
11
20
|
let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControllerOpen(HelixElement) {
|
|
12
21
|
constructor() {
|
|
13
22
|
super(...arguments);
|
|
14
23
|
this.label = 'Actions';
|
|
24
|
+
this.longPressLabel = 'Long press or press Alt+Down to show the actions';
|
|
15
25
|
this.offset = 12;
|
|
26
|
+
this.openOn = 'click';
|
|
16
27
|
this.placement = 'top-end';
|
|
17
28
|
this.keys = new KeyboardNavigationController(this, {
|
|
18
|
-
items: () => (this.open ? this.items : []),
|
|
29
|
+
items: () => (this.open ? this.items.map(navigable) : []),
|
|
19
30
|
selects: () => false,
|
|
20
|
-
typeahead:
|
|
31
|
+
typeahead: typeaheadByWords(),
|
|
21
32
|
});
|
|
22
33
|
this.focusOnOpen = false;
|
|
23
34
|
this.panel = new PopoverController(this, {
|
|
@@ -29,13 +40,48 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
29
40
|
placement: () => this.placement,
|
|
30
41
|
surface: () => this.partNamed('actions'),
|
|
31
42
|
});
|
|
43
|
+
this.longPress = new LongPress(this, {
|
|
44
|
+
onLongPress: () => {
|
|
45
|
+
if (!this.open) {
|
|
46
|
+
this.focusOnOpen = this.requestOpen(true);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
32
50
|
this.handleTriggerClick = () => {
|
|
33
51
|
if (this.open) {
|
|
34
52
|
this.requestOpen(false);
|
|
35
53
|
return;
|
|
36
54
|
}
|
|
55
|
+
if (this.pressHeld) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
37
58
|
this.focusOnOpen = this.requestOpen(true);
|
|
38
59
|
};
|
|
60
|
+
this.handleHeldClick = (event) => {
|
|
61
|
+
if (this.pressHeld && this.longPress.takeClick()) {
|
|
62
|
+
event.stopImmediatePropagation();
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
this.handleTriggerPointerDown = (event) => {
|
|
67
|
+
if (this.pressHeld && !this.open) {
|
|
68
|
+
this.longPress.down(event);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
this.handleTriggerPointerMove = (event) => {
|
|
72
|
+
this.longPress.move(event);
|
|
73
|
+
};
|
|
74
|
+
this.handleTriggerPointerUp = () => {
|
|
75
|
+
this.longPress.up();
|
|
76
|
+
};
|
|
77
|
+
this.handleTriggerPointerCancel = () => {
|
|
78
|
+
this.longPress.cancel();
|
|
79
|
+
};
|
|
80
|
+
this.handleTriggerContextMenu = (event) => {
|
|
81
|
+
if (this.pressHeld) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
39
85
|
this.handleSelect = (event) => {
|
|
40
86
|
queueMicrotask(() => {
|
|
41
87
|
if (!event.defaultPrevented) {
|
|
@@ -45,6 +91,12 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
45
91
|
};
|
|
46
92
|
this.handleKeydown = (event) => {
|
|
47
93
|
if (!this.open) {
|
|
94
|
+
if (this.pressHeld &&
|
|
95
|
+
event.altKey &&
|
|
96
|
+
(event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
this.focusOnOpen = this.requestOpen(true);
|
|
99
|
+
}
|
|
48
100
|
return;
|
|
49
101
|
}
|
|
50
102
|
if (event.key === 'Tab') {
|
|
@@ -58,13 +110,26 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
58
110
|
this.close();
|
|
59
111
|
event.preventDefault();
|
|
60
112
|
};
|
|
113
|
+
this.handleTriggerFocusIn = () => {
|
|
114
|
+
if (this.pressHeld) {
|
|
115
|
+
this.describeTrigger(true);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
61
118
|
}
|
|
62
119
|
get trigger() {
|
|
63
120
|
return anchorElement(this.renderRoot?.querySelector('slot[name="trigger"]'), this.partNamed('trigger') ?? this);
|
|
64
121
|
}
|
|
65
122
|
get items() {
|
|
66
123
|
const slot = this.renderRoot?.querySelector('slot:not([name])');
|
|
67
|
-
|
|
124
|
+
const isItem = (one) => one.isSpeedDialItem === true;
|
|
125
|
+
return (slot?.assignedElements?.() ?? []).flatMap((one) => isItem(one)
|
|
126
|
+
? [one]
|
|
127
|
+
: one.isSpeedDialGroup === true
|
|
128
|
+
? [...one.children].filter(isItem)
|
|
129
|
+
: []);
|
|
130
|
+
}
|
|
131
|
+
get pressHeld() {
|
|
132
|
+
return this.openOn === 'long-press';
|
|
68
133
|
}
|
|
69
134
|
focus(options) {
|
|
70
135
|
const trigger = this.trigger;
|
|
@@ -84,11 +149,18 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
84
149
|
super.connectedCallback();
|
|
85
150
|
this.addEventListener('helix-select', this.handleSelect);
|
|
86
151
|
this.addEventListener('keydown', this.handleKeydown);
|
|
152
|
+
this.addEventListener('click', this.handleHeldClick, true);
|
|
87
153
|
}
|
|
88
154
|
disconnectedCallback() {
|
|
89
155
|
super.disconnectedCallback();
|
|
156
|
+
if (this.described) {
|
|
157
|
+
unlinkDescription(this.described, this);
|
|
158
|
+
this.described = undefined;
|
|
159
|
+
this.internals.ariaLabel = null;
|
|
160
|
+
}
|
|
90
161
|
this.removeEventListener('helix-select', this.handleSelect);
|
|
91
162
|
this.removeEventListener('keydown', this.handleKeydown);
|
|
163
|
+
this.removeEventListener('click', this.handleHeldClick, true);
|
|
92
164
|
}
|
|
93
165
|
announce() {
|
|
94
166
|
const trigger = this.trigger;
|
|
@@ -97,9 +169,27 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
97
169
|
}
|
|
98
170
|
tellDisclosure(trigger, this.open, 'menu');
|
|
99
171
|
}
|
|
172
|
+
describeTrigger(light = false) {
|
|
173
|
+
const trigger = this.trigger;
|
|
174
|
+
const slotted = this.pressHeld && trigger !== this && trigger !== this.partNamed('trigger');
|
|
175
|
+
const control = slotted ? focusTarget(trigger) : undefined;
|
|
176
|
+
if (this.described && this.described !== control) {
|
|
177
|
+
unlinkDescription(this.described, this);
|
|
178
|
+
this.described = undefined;
|
|
179
|
+
}
|
|
180
|
+
if (control && control !== this.described && linkDescription(control, this, light)) {
|
|
181
|
+
this.described = control;
|
|
182
|
+
}
|
|
183
|
+
this.internals.ariaLabel = this.described ? this.longPressLabel : null;
|
|
184
|
+
}
|
|
185
|
+
firstUpdated(changed) {
|
|
186
|
+
super.firstUpdated(changed);
|
|
187
|
+
requestAnimationFrame(() => this.describeTrigger());
|
|
188
|
+
}
|
|
100
189
|
updated(changed) {
|
|
101
190
|
super.updated?.(changed);
|
|
102
191
|
this.announce();
|
|
192
|
+
this.describeTrigger();
|
|
103
193
|
if (this.open && this.focusOnOpen) {
|
|
104
194
|
this.focusOnOpen = false;
|
|
105
195
|
this.updateComplete.then(() => {
|
|
@@ -110,10 +200,22 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
110
200
|
}
|
|
111
201
|
}
|
|
112
202
|
renderTrigger() {
|
|
113
|
-
|
|
203
|
+
const held = this.pressHeld;
|
|
204
|
+
return html `<slot
|
|
205
|
+
name="trigger"
|
|
206
|
+
@click=${this.handleTriggerClick}
|
|
207
|
+
@contextmenu=${this.handleTriggerContextMenu}
|
|
208
|
+
@focusin=${this.handleTriggerFocusIn}
|
|
209
|
+
@pointercancel=${this.handleTriggerPointerCancel}
|
|
210
|
+
@pointerdown=${this.handleTriggerPointerDown}
|
|
211
|
+
@pointermove=${this.handleTriggerPointerMove}
|
|
212
|
+
@pointerup=${this.handleTriggerPointerUp}
|
|
213
|
+
@slotchange=${() => this.describeTrigger()}
|
|
214
|
+
>
|
|
114
215
|
<button
|
|
115
216
|
part="trigger"
|
|
116
217
|
type="button"
|
|
218
|
+
aria-describedby=${held ? 'long-press' : nothing}
|
|
117
219
|
aria-expanded=${this.open ? 'true' : 'false'}
|
|
118
220
|
aria-haspopup="menu"
|
|
119
221
|
aria-label=${this.label}
|
|
@@ -121,6 +223,7 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
121
223
|
${''}
|
|
122
224
|
${drawIcon(plus)}
|
|
123
225
|
</button>
|
|
226
|
+
${held ? html `<span id="long-press" hidden>${this.longPressLabel}</span>` : nothing}
|
|
124
227
|
</slot>`;
|
|
125
228
|
}
|
|
126
229
|
render() {
|
|
@@ -139,9 +242,15 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
139
242
|
__decorate([
|
|
140
243
|
property()
|
|
141
244
|
], HelixHeadlessSpeedDial.prototype, "label", void 0);
|
|
245
|
+
__decorate([
|
|
246
|
+
property({ attribute: 'long-press-label' })
|
|
247
|
+
], HelixHeadlessSpeedDial.prototype, "longPressLabel", void 0);
|
|
142
248
|
__decorate([
|
|
143
249
|
property({ type: Number })
|
|
144
250
|
], HelixHeadlessSpeedDial.prototype, "offset", void 0);
|
|
251
|
+
__decorate([
|
|
252
|
+
property({ attribute: 'open-on' })
|
|
253
|
+
], HelixHeadlessSpeedDial.prototype, "openOn", void 0);
|
|
145
254
|
__decorate([
|
|
146
255
|
property()
|
|
147
256
|
], HelixHeadlessSpeedDial.prototype, "placement", void 0);
|
|
@@ -13,8 +13,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
13
13
|
* added at `firstUpdated` and is not in the server markup. Before it appears,
|
|
14
14
|
* both dividers work on their own.
|
|
15
15
|
*
|
|
16
|
-
* The divider has `role="separator"` and `aria-valuenow`,
|
|
17
|
-
*
|
|
16
|
+
* The divider has `role="separator"` and `aria-valuenow`, with
|
|
17
|
+
* `aria-valuetext` saying the start pane's share as a percentage in the
|
|
18
|
+
* reader's locale, and `aria-controls` names the start pane. Drag it with a pointer or move it with the arrow keys,
|
|
18
19
|
* press `Home` or `End` to move it to its bounds, and press `Enter` to collapse
|
|
19
20
|
* the start pane and open it again. A drag in progress is tracked as a state, so both dividers a
|
|
20
21
|
* corner moves stay lit for as long as they move.
|
|
@@ -36,13 +37,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
36
37
|
* @documentation https://fusion.dev/helix/components/splitter
|
|
37
38
|
*
|
|
38
39
|
* @slot end - The pane after the divider.
|
|
40
|
+
* @slot handle - A mark drawn at the middle of the divider, such as a grip icon. It takes no focus and no pointer of its own: a press on it is a press on the divider.
|
|
39
41
|
* @slot start - The pane before the divider.
|
|
40
42
|
*
|
|
41
43
|
* @event {HelixBeforeCollapseEvent} helix-before-collapse - Fired before a pane shuts or reopens, whether or not the resize that caused it was allowed. It carries its own position, because a shut pane sits at `collapsed-size`. Cancelable: canceling it leaves the pane as it was and the divider where the gesture put it.
|
|
42
44
|
* @event {HelixBeforeResizeEvent} helix-before-resize - Fired before the divider settles, and only when the gesture moved it. A press that moved nothing, a drag that came back, an arrow key with no room left, and a corner that moved only the other divider do not fire it. Cancelable: canceling it leaves the position to you and keeps the rest of the gesture, so a collapse the gesture reached still fires `helix-before-collapse`, which you can also cancel. A dragged divider stays where it was dropped until you set your own `position`.
|
|
43
45
|
* @event {HelixCollapseEvent} helix-collapse - Fired after a pane shuts or reopens, with the side that is shut, or empty where both are open.
|
|
44
46
|
* @event {HelixResizedEvent} helix-resized - Fired after the divider settles somewhere new, with the new position.
|
|
45
|
-
* @event {Event} input - Fired each time
|
|
47
|
+
* @event {Event} input - Fired each time the divider moves, as a range input does: on each step of a drag, and before `helix-resized` for a key, a double click or a corner. Read `position` for where it is now.
|
|
46
48
|
*
|
|
47
49
|
* @csspart corner - The handle where two dividers cross, added on the client.
|
|
48
50
|
* @csspart end - The box that holds the second pane.
|
|
@@ -52,7 +54,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
52
54
|
* @cssproperty --splitter-corner-at - Where a nested splitter crosses this one, as a percentage.
|
|
53
55
|
* @cssproperty --splitter-position - Where the divider sits, as a percentage of the box.
|
|
54
56
|
*
|
|
57
|
+
* @cssstate collapsed - While a pane is shut. `data-collapsed` on the host says which.
|
|
55
58
|
* @cssstate dragging - While the pointer or a corner holds the divider.
|
|
59
|
+
* @cssstate vertical - While the panes are one above the other, whether `orientation` says so or `auto` turned them.
|
|
56
60
|
*/
|
|
57
61
|
export declare class HelixHeadlessSplitter extends HelixElement {
|
|
58
62
|
/**
|
|
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
var HelixHeadlessSplitter_1;
|
|
8
|
-
import { AriaForwardController, capturePointer, closestInstance, commaList, customElement, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixCollapseEvent, HelixElement, HelixResizedEvent, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { AriaForwardController, capturePointer, closestInstance, commaList, customElement, formatPercent, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixCollapseEvent, HelixElement, HelixResizedEvent, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { snapTo } from '../snap.js';
|
|
@@ -144,6 +144,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
144
144
|
const previous = this.position;
|
|
145
145
|
this.positionState = value;
|
|
146
146
|
this.requestUpdate('position', previous);
|
|
147
|
+
if (value !== previous) {
|
|
148
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
149
|
+
}
|
|
147
150
|
this.dispatchEvent(new HelixResizedEvent({ value: String(value) }));
|
|
148
151
|
},
|
|
149
152
|
event: (value) => new HelixBeforeResizeEvent({ value: String(value) }),
|
|
@@ -447,6 +450,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
447
450
|
super.firstUpdated(changed);
|
|
448
451
|
this.watchWidth();
|
|
449
452
|
this.findOuter();
|
|
453
|
+
this.finishHydration();
|
|
450
454
|
}
|
|
451
455
|
updated(changed) {
|
|
452
456
|
super.updated(changed);
|
|
@@ -473,6 +477,8 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
473
477
|
willUpdate(changed) {
|
|
474
478
|
super.willUpdate(changed);
|
|
475
479
|
this.toggleState('dragging', this.dragging);
|
|
480
|
+
this.toggleState('collapsed', this.collapsed !== undefined);
|
|
481
|
+
this.toggleState('vertical', this.vertical);
|
|
476
482
|
}
|
|
477
483
|
render() {
|
|
478
484
|
const position = this.showing;
|
|
@@ -494,7 +500,11 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
494
500
|
aria-valuemax=${this.collapsed ? 100 : this.clamp(this.maxPercent)}
|
|
495
501
|
aria-valuemin=${this.collapsed ? 0 : this.clamp(this.minPercent)}
|
|
496
502
|
aria-valuenow=${Math.round(position)}
|
|
497
|
-
aria-valuetext=${this.collapsed
|
|
503
|
+
aria-valuetext=${this.collapsed
|
|
504
|
+
? this.collapsedLabel
|
|
505
|
+
: this.hydrating
|
|
506
|
+
? nothing
|
|
507
|
+
: formatPercent(Math.round(position) / 100)}
|
|
498
508
|
?data-dragging=${this.dragging}
|
|
499
509
|
?data-flush=${this.flush}
|
|
500
510
|
@dblclick=${this.handleDoubleClick}
|
|
@@ -503,7 +513,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
503
513
|
@pointerdown=${this.handlePointerDown}
|
|
504
514
|
@pointermove=${this.handlePointerMove}
|
|
505
515
|
@pointerup=${this.handlePointerUp}
|
|
506
|
-
|
|
516
|
+
>
|
|
517
|
+
<slot name="handle"></slot>
|
|
518
|
+
</div>
|
|
507
519
|
<div part="end" ?data-dragging=${this.dragging}>
|
|
508
520
|
<slot name="end"></slot>
|
|
509
521
|
</div>
|
|
@@ -516,6 +528,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
516
528
|
aria-valuemax="100"
|
|
517
529
|
aria-valuemin="0"
|
|
518
530
|
aria-valuenow=${Math.round(crossing.at)}
|
|
531
|
+
aria-valuetext=${formatPercent(Math.round(crossing.at) / 100)}
|
|
519
532
|
data-cross=${index}
|
|
520
533
|
style="--splitter-corner-at: ${crossing.at}%; --splitter-position: ${position}%"
|
|
521
534
|
@dblclick=${this.handleCornerDoubleClick}
|
|
@@ -14,7 +14,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
14
14
|
* table with no `TableColumn` heads its columns with.
|
|
15
15
|
*
|
|
16
16
|
* The role is on the host so the cell stays a child of its row in the flat
|
|
17
|
-
* tree, which keeps the table structure intact.
|
|
17
|
+
* tree, which keeps the table structure intact. In a `grid` table a value is
|
|
18
|
+
* a `gridcell`, and the table gives the cell a `tabindex` so focus can rest on
|
|
19
|
+
* it.
|
|
18
20
|
*
|
|
19
21
|
* @since 0.1.0
|
|
20
22
|
* @status stable
|
|
@@ -33,6 +35,11 @@ export declare class HelixHeadlessTableCell extends HelixElement {
|
|
|
33
35
|
* column in a row of headers alone.
|
|
34
36
|
*/
|
|
35
37
|
header: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* @internal Whether the cell is a cell of a `grid` table, which moves focus
|
|
40
|
+
* between its cells, written by that table. Not a consumer's to set.
|
|
41
|
+
*/
|
|
42
|
+
gridCell: boolean;
|
|
36
43
|
/**
|
|
37
44
|
* @internal Read by the row and by the cells beside it, to tell a header
|
|
38
45
|
* cell from a value. Not a consumer's to read or set.
|
|
@@ -6,11 +6,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessTableCell = class HelixHeadlessTableCell extends HelixElement {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.header = false;
|
|
14
|
+
this.gridCell = false;
|
|
14
15
|
this.isTableCell = true;
|
|
15
16
|
this.handleSlotChange = () => {
|
|
16
17
|
const text = [...this.childNodes].some((node) => node.nodeType === Node.TEXT_NODE && (node.textContent ?? '').trim() !== '');
|
|
@@ -27,7 +28,7 @@ let HelixHeadlessTableCell = class HelixHeadlessTableCell extends HelixElement {
|
|
|
27
28
|
willUpdate(changed) {
|
|
28
29
|
super.willUpdate(changed);
|
|
29
30
|
const header = this.headsRow ? 'rowheader' : 'columnheader';
|
|
30
|
-
hostAria(this.internals, { role: this.header ? header : 'cell' });
|
|
31
|
+
hostAria(this.internals, { role: this.header ? header : this.gridCell ? 'gridcell' : 'cell' });
|
|
31
32
|
}
|
|
32
33
|
updated(changed) {
|
|
33
34
|
super.updated(changed);
|
|
@@ -47,6 +48,9 @@ let HelixHeadlessTableCell = class HelixHeadlessTableCell extends HelixElement {
|
|
|
47
48
|
__decorate([
|
|
48
49
|
property({ reflect: true, type: Boolean })
|
|
49
50
|
], HelixHeadlessTableCell.prototype, "header", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
state()
|
|
53
|
+
], HelixHeadlessTableCell.prototype, "gridCell", void 0);
|
|
50
54
|
HelixHeadlessTableCell = __decorate([
|
|
51
55
|
customElement('helix-headless-table-cell')
|
|
52
56
|
], HelixHeadlessTableCell);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type GridPlace } from '@fusion.dev/helix-core';
|
|
2
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
3
|
+
import type { HelixHeadlessTableColumn } from './column/table-column.js';
|
|
4
|
+
/** A place in the grid with the element that holds focus there. */
|
|
5
|
+
export interface GridCell extends GridPlace {
|
|
6
|
+
cell: HTMLElement;
|
|
7
|
+
}
|
|
8
|
+
export interface TableGridOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Acts on a cell for Enter, Space or F2, and says whether it did. A key it
|
|
11
|
+
* leaves alone moves into the cell's controls, Enter and F2 only.
|
|
12
|
+
*/
|
|
13
|
+
activate: (place: GridCell, event: KeyboardEvent) => boolean;
|
|
14
|
+
/** The headings that are drawn, in order. */
|
|
15
|
+
columns: () => HelixHeadlessTableColumn[];
|
|
16
|
+
/** The element in the table's own shadow root holding a part of that name. */
|
|
17
|
+
part: (name: string) => HTMLElement | null;
|
|
18
|
+
/** The rows in the order they are drawn. */
|
|
19
|
+
rows: () => Element[];
|
|
20
|
+
/** Selects every row for Control or Command with A, and says whether it did. */
|
|
21
|
+
selectAll: () => boolean;
|
|
22
|
+
/** Whether a checkbox column leads. */
|
|
23
|
+
selectable: () => boolean;
|
|
24
|
+
/** Whether the table is a grid now. */
|
|
25
|
+
on: () => boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The keyboard of a `grid` table: one tab stop among its cells, the arrow
|
|
29
|
+
* keys, Home, End and the Page keys between them, and Enter or F2 into a
|
|
30
|
+
* cell's controls.
|
|
31
|
+
*
|
|
32
|
+
* **Tab leaves the grid.** The controls in a cell are the consumer's, so their
|
|
33
|
+
* tab order is left as written: Tab on a cell, or past the last control of a
|
|
34
|
+
* cell, focuses an empty box at the grid's end (Shift: its start) and lets the
|
|
35
|
+
* key go on from there, as React Aria's collections do. A control in a cell
|
|
36
|
+
* that focus reaches from outside by the keyboard hands it to the stop, so the
|
|
37
|
+
* grid is one stop from either side. A press, or a script focusing a cell, is
|
|
38
|
+
* left where it lands, and the stop follows it.
|
|
39
|
+
*/
|
|
40
|
+
export declare class TableGridController implements ReactiveController {
|
|
41
|
+
private readonly host;
|
|
42
|
+
private readonly options;
|
|
43
|
+
/** The row holding the stop, `head` for the headings, or nothing for the first row. */
|
|
44
|
+
private stopRow;
|
|
45
|
+
/** The column holding the stop. */
|
|
46
|
+
private stopColumn;
|
|
47
|
+
/** Where the stop's row was drawn when last marked, for the row that takes over when it goes. */
|
|
48
|
+
private stopIndex;
|
|
49
|
+
/** The row focus is in, while it is in the grid's rows. */
|
|
50
|
+
private focusRow;
|
|
51
|
+
/** Whether the next update focuses the stop, after a key moved it. */
|
|
52
|
+
private focusWanted;
|
|
53
|
+
/** Whether focus is arriving from a press, which keeps its place. */
|
|
54
|
+
private pointing;
|
|
55
|
+
/** The cells given a `tabindex`, so a table that stops being a grid takes it back. */
|
|
56
|
+
private readonly given;
|
|
57
|
+
constructor(host: ReactiveControllerHost & HTMLElement, options: TableGridOptions);
|
|
58
|
+
hostConnected(): void;
|
|
59
|
+
hostDisconnected(): void;
|
|
60
|
+
/**
|
|
61
|
+
* A row taken away while it held the stop hands it to the row drawn in its
|
|
62
|
+
* place, or to the row before where it was the last, at the same column.
|
|
63
|
+
* Where focus was in that row and went with it, focus follows the stop, as
|
|
64
|
+
* React Aria's collections move it to a neighbor; focus that had already
|
|
65
|
+
* left the grid is left where it is. Before the render, so the cells the
|
|
66
|
+
* table draws itself are drawn with the stop where it now is.
|
|
67
|
+
*/
|
|
68
|
+
hostUpdate(): void;
|
|
69
|
+
/** How many columns lead the consumer's cells: the checkbox column, where there is one. */
|
|
70
|
+
private get offset();
|
|
71
|
+
/** The first row: the headings where there are any, and the first row of cells otherwise. */
|
|
72
|
+
private get top();
|
|
73
|
+
/** A row's cells that are drawn, in order. */
|
|
74
|
+
private cellsOf;
|
|
75
|
+
/** How many columns a row has. */
|
|
76
|
+
private widthOf;
|
|
77
|
+
/** The element that takes focus at a place, or nothing where there is none. */
|
|
78
|
+
cellAt({ column, row }: GridPlace): HTMLElement | null;
|
|
79
|
+
/** The place holding the stop now, held to the grid as it is. */
|
|
80
|
+
current(): GridPlace;
|
|
81
|
+
/** Whether the stop is at a place, for a cell the table draws itself. */
|
|
82
|
+
isStop(place: GridPlace): boolean;
|
|
83
|
+
/** The place an event came from or through, with the element that holds focus there. */
|
|
84
|
+
private placeOf;
|
|
85
|
+
/** Moves the stop to a place, and focus with it on the next update. */
|
|
86
|
+
private moveTo;
|
|
87
|
+
/**
|
|
88
|
+
* Writes the stops: a `tabindex` on every cell, `0` on the one holding the
|
|
89
|
+
* stop, and the grid's state on the rows and headings. Takes it all back
|
|
90
|
+
* once the table stops being a grid. Called by the table after each update.
|
|
91
|
+
*/
|
|
92
|
+
mark(): void;
|
|
93
|
+
/** Focuses the stop where a key asked for it. Called by the table after `mark`. */
|
|
94
|
+
place(): void;
|
|
95
|
+
/** The controls in a cell, in order: what Enter and F2 move into, and Tab moves between. */
|
|
96
|
+
private controlsIn;
|
|
97
|
+
/**
|
|
98
|
+
* Hands focus to the empty box at the grid's end, or its start, so the Tab
|
|
99
|
+
* under way goes on from there and out of the grid.
|
|
100
|
+
*/
|
|
101
|
+
private leave;
|
|
102
|
+
/** How many rows a Page key moves from a cell. */
|
|
103
|
+
private pageFrom;
|
|
104
|
+
private readonly handleKeydown;
|
|
105
|
+
/** The keys the grid takes while focus is on a control inside a cell. */
|
|
106
|
+
private handleControlKey;
|
|
107
|
+
/**
|
|
108
|
+
* Focus arriving on a cell, or inside one. From outside the grid by the
|
|
109
|
+
* keyboard, anywhere but the stop is handed to the stop; otherwise the stop
|
|
110
|
+
* moves to where focus went.
|
|
111
|
+
*/
|
|
112
|
+
private readonly handleFocusIn;
|
|
113
|
+
/** Focus leaving for somewhere outside the grid's host is no longer in a row. */
|
|
114
|
+
private readonly handleFocusOut;
|
|
115
|
+
/** A press is under way, so the focus it brings stays where it lands. */
|
|
116
|
+
private readonly handlePointerDown;
|
|
117
|
+
}
|