@fusion.dev/helix-headless 0.143.0 → 0.144.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 +8551 -375
- package/dist/area-chart/root/area-chart-root.d.ts +12 -4
- package/dist/area-chart/root/area-chart-root.js +24 -7
- package/dist/avatar/root/avatar-root.d.ts +14 -0
- package/dist/avatar/root/avatar-root.js +23 -9
- package/dist/badge/root/badge-root.d.ts +11 -7
- package/dist/badge/root/badge-root.js +1 -1
- package/dist/bar-chart/root/bar-chart-root.d.ts +12 -3
- package/dist/bar-chart/root/bar-chart-root.js +17 -10
- package/dist/button/root/button-root.d.ts +47 -1
- package/dist/button/root/button-root.js +38 -4
- package/dist/callout/root/callout-root.d.ts +18 -1
- package/dist/callout/root/callout-root.js +15 -4
- package/dist/card/root/card-root.d.ts +30 -1
- package/dist/card/root/card-root.js +38 -5
- package/dist/checkbox/root/checkbox-root.js +2 -0
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +53 -3
- package/dist/checkbox-group/root/checkbox-group-root.js +71 -22
- package/dist/code-block/lines.d.ts +13 -0
- package/dist/code-block/lines.js +2 -0
- package/dist/code-block/root/code-block-root.d.ts +30 -0
- package/dist/code-block/root/code-block-root.js +23 -2
- package/dist/color-input/root/color-input-root.d.ts +34 -1
- package/dist/color-input/root/color-input-root.js +23 -4
- package/dist/color-picker/channels.d.ts +26 -0
- package/dist/color-picker/channels.js +84 -0
- package/dist/color-picker/root/color-picker-root.d.ts +81 -5
- package/dist/color-picker/root/color-picker-root.js +103 -10
- package/dist/combobox/root/combobox-root.d.ts +38 -3
- package/dist/combobox/root/combobox-root.js +64 -2
- package/dist/command/root/command-root.d.ts +30 -4
- package/dist/command/root/command-root.js +33 -3
- package/dist/copy-button/from.d.ts +22 -0
- package/dist/copy-button/from.js +28 -0
- package/dist/copy-button/root/copy-button-root.d.ts +27 -1
- package/dist/copy-button/root/copy-button-root.js +43 -7
- package/dist/date-input/root/date-input-root.d.ts +73 -4
- package/dist/date-input/root/date-input-root.js +93 -43
- package/dist/date-picker/root/date-picker-root.d.ts +145 -1
- package/dist/date-picker/root/date-picker-root.js +239 -48
- package/dist/dialog/description.d.ts +9 -0
- package/dist/dialog/description.js +14 -0
- package/dist/dialog/root/dialog-root.d.ts +64 -1
- package/dist/dialog/root/dialog-root.js +49 -23
- package/dist/donut-chart/root/donut-chart-root.d.ts +25 -4
- package/dist/donut-chart/root/donut-chart-root.js +21 -6
- package/dist/drawer/root/drawer-root.d.ts +63 -2
- package/dist/drawer/root/drawer-root.js +95 -19
- package/dist/drawer/swipe-start.d.ts +9 -0
- package/dist/drawer/swipe-start.js +12 -0
- package/dist/element-events.generated.js +8 -5
- package/dist/empty/root/empty-root.d.ts +1 -1
- package/dist/file-input/root/file-input-root.d.ts +47 -0
- package/dist/file-input/root/file-input-root.js +60 -22
- package/dist/file-input/screen.d.ts +35 -0
- package/dist/file-input/screen.js +49 -0
- package/dist/filter-input/root/filter-input-root.d.ts +38 -4
- package/dist/filter-input/root/filter-input-root.js +67 -3
- package/dist/filter-input/suggest.d.ts +17 -0
- package/dist/filter-input/suggest.js +23 -0
- package/dist/format-date/options.d.ts +33 -0
- package/dist/format-date/options.js +42 -0
- package/dist/format-date/root/format-date-root.d.ts +51 -0
- package/dist/format-date/root/format-date-root.js +58 -38
- package/dist/heatmap/root/heatmap-root.d.ts +23 -6
- package/dist/heatmap/root/heatmap-root.js +24 -13
- package/dist/icon/index.d.ts +2 -0
- package/dist/icon/index.js +2 -0
- package/dist/icon/registry.d.ts +37 -0
- package/dist/icon/registry.js +83 -0
- package/dist/icon/root/icon-root.d.ts +30 -4
- package/dist/icon/root/icon-root.js +41 -6
- package/dist/icon/root/index.d.ts +2 -0
- package/dist/icon/root/index.js +2 -0
- package/dist/icon/set.d.ts +1 -0
- package/dist/icon/set.js +3 -0
- package/dist/image/root/image-root.d.ts +14 -0
- package/dist/image/root/image-root.js +15 -2
- package/dist/input/root/input-root.d.ts +11 -0
- package/dist/input/root/input-root.js +15 -0
- package/dist/kbd/root/kbd-root.d.ts +20 -13
- package/dist/kbd/root/kbd-root.js +35 -37
- package/dist/kbd/shortcut.d.ts +18 -0
- package/dist/kbd/shortcut.js +35 -0
- package/dist/legend/hidden-series.d.ts +8 -0
- package/dist/legend/hidden-series.js +13 -0
- package/dist/legend/root/legend-root.d.ts +56 -0
- package/dist/legend/root/legend-root.js +54 -5
- package/dist/lightbox/item/lightbox-item.d.ts +15 -0
- package/dist/lightbox/item/lightbox-item.js +10 -1
- package/dist/lightbox/root/lightbox-root.d.ts +61 -1
- package/dist/lightbox/root/lightbox-root.js +94 -4
- package/dist/line-chart/root/line-chart-root.d.ts +11 -3
- package/dist/line-chart/root/line-chart-root.js +19 -5
- package/dist/listbox/range.d.ts +17 -0
- package/dist/listbox/range.js +5 -0
- package/dist/listbox/root/listbox-root.d.ts +74 -3
- package/dist/listbox/root/listbox-root.js +87 -9
- package/dist/loader/busy.d.ts +17 -0
- package/dist/loader/busy.js +24 -0
- package/dist/loader/root/loader-root.d.ts +37 -0
- package/dist/loader/root/loader-root.js +52 -3
- package/dist/meter/root/meter-root.d.ts +7 -0
- package/dist/meter/root/meter-root.js +6 -3
- package/dist/notifications/feed-keys.d.ts +10 -0
- package/dist/notifications/feed-keys.js +14 -0
- package/dist/notifications/notification/notification.d.ts +17 -0
- package/dist/notifications/notification/notification.js +21 -2
- package/dist/notifications/root/notifications-root.d.ts +42 -0
- package/dist/notifications/root/notifications-root.js +60 -1
- package/dist/number-input/root/number-input-root.d.ts +47 -0
- package/dist/number-input/root/number-input-root.js +58 -22
- package/dist/object-inspector/root/object-inspector-root.d.ts +1 -0
- package/dist/object-inspector/root/object-inspector-root.js +1 -1
- package/dist/option/root/option-root.d.ts +54 -5
- package/dist/option/root/option-root.js +34 -5
- package/dist/pin-input/format.d.ts +7 -0
- package/dist/pin-input/format.js +16 -0
- package/dist/pin-input/root/pin-input-root.d.ts +21 -0
- package/dist/pin-input/root/pin-input-root.js +29 -2
- package/dist/popover/root/popover-root.d.ts +62 -4
- package/dist/popover/root/popover-root.js +53 -5
- package/dist/progress/root/progress-root.d.ts +7 -0
- package/dist/progress/root/progress-root.js +9 -3
- package/dist/qr-code/export.d.ts +31 -0
- package/dist/qr-code/export.js +10 -0
- package/dist/qr-code/root/qr-code-root.d.ts +26 -0
- package/dist/qr-code/root/qr-code-root.js +49 -2
- package/dist/radio/group/radio-group.d.ts +23 -0
- package/dist/radio/group/radio-group.js +18 -0
- package/dist/rating/root/rating-root.d.ts +25 -0
- package/dist/rating/root/rating-root.js +45 -3
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
- package/dist/scatter-chart/root/scatter-chart-root.js +25 -7
- package/dist/select/root/select-root.d.ts +24 -0
- package/dist/select/root/select-root.js +20 -6
- package/dist/skeleton/root/skeleton-root.d.ts +25 -0
- package/dist/skeleton/root/skeleton-root.js +17 -2
- package/dist/slider/root/slider-root.d.ts +39 -0
- package/dist/slider/root/slider-root.js +45 -9
- package/dist/stat/root/stat-root.d.ts +2 -0
- package/dist/stat/root/stat-root.js +1 -1
- package/dist/steps/step/step.d.ts +1 -0
- package/dist/steps/step/step.js +1 -1
- package/dist/switch/root/switch-root.d.ts +12 -2
- package/dist/switch/root/switch-root.js +6 -1
- package/dist/tag/group/tag-group.d.ts +107 -0
- package/dist/tag/group/tag-group.js +157 -0
- package/dist/tag/index.d.ts +1 -0
- package/dist/tag/index.js +1 -0
- package/dist/tag/root/tag-root.d.ts +60 -0
- package/dist/tag/root/tag-root.js +74 -18
- package/dist/tag/selection.d.ts +10 -0
- package/dist/tag/selection.js +12 -0
- package/dist/textarea/grow.d.ts +22 -0
- package/dist/textarea/grow.js +20 -0
- package/dist/textarea/root/textarea-root.d.ts +9 -0
- package/dist/textarea/root/textarea-root.js +9 -1
- package/dist/time-input/root/time-input-root.d.ts +44 -5
- package/dist/time-input/root/time-input-root.js +69 -39
- package/dist/time-picker/root/time-picker-root.d.ts +54 -8
- package/dist/time-picker/root/time-picker-root.js +121 -91
- package/dist/toast/hotkey.d.ts +22 -0
- package/dist/toast/hotkey.js +23 -0
- package/dist/toast/item/toast-item.d.ts +16 -1
- package/dist/toast/item/toast-item.js +41 -13
- package/dist/toast/root/toast-root.d.ts +42 -8
- package/dist/toast/root/toast-root.js +64 -4
- package/dist/toggle/group/toggle-group.d.ts +57 -15
- package/dist/toggle/group/toggle-group.js +69 -9
- package/dist/toggle/root/toggle-root.d.ts +12 -1
- package/dist/toggle/root/toggle-root.js +11 -3
- package/dist/tooltip/focus-target.d.ts +6 -0
- package/dist/tooltip/focus-target.js +9 -0
- package/dist/tooltip/root/tooltip-root.d.ts +42 -17
- package/dist/tooltip/root/tooltip-root.js +50 -9
- package/dist/tour/naming.d.ts +30 -0
- package/dist/tour/naming.js +10 -0
- package/dist/tour/root/tour-root.d.ts +35 -0
- package/dist/tour/root/tour-root.js +56 -5
- package/dist/tour/stop/tour-stop.d.ts +53 -8
- package/dist/tour/stop/tour-stop.js +45 -15
- package/dist/tour/wait.d.ts +11 -0
- package/dist/tour/wait.js +19 -0
- package/dist/trend/root/trend-root.d.ts +13 -0
- package/dist/trend/root/trend-root.js +14 -9
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +4862 -254
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +1149 -67
|
@@ -19,9 +19,11 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
19
19
|
* Focus stays in the text field. The arrow keys move through the list with the
|
|
20
20
|
* listbox's public `navigate` method. `role="combobox"` is on the input itself.
|
|
21
21
|
* ArrowDown and ArrowUp open the list on the chosen option, and Enter chooses
|
|
22
|
-
* the highlighted one.
|
|
22
|
+
* the highlighted one. As typing narrows the list, a screen reader hears how
|
|
23
|
+
* many options are left, such as "3 results". Space, Home, End and letters edit the text, and Enter in
|
|
23
24
|
* a closed field is left to the form. Emptying the field and leaving it clears
|
|
24
|
-
* a single choice; Escape puts the chosen words back
|
|
25
|
+
* a single choice; Escape puts the chosen words back, and a second Escape on
|
|
26
|
+
* the closed field clears the choice.
|
|
25
27
|
*
|
|
26
28
|
* With `multiple`, each chosen option is a tag in the field with its own remove
|
|
27
29
|
* control. The panel stays open and the filter clears after each choice, so the
|
|
@@ -39,11 +41,13 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
39
41
|
* @documentation https://fusion.dev/helix/components/combobox
|
|
40
42
|
*
|
|
41
43
|
* @slot - The options.
|
|
44
|
+
* @slot empty - What the list shows when typing hides every option, for content richer than `empty-text`.
|
|
42
45
|
* @slot end - After the text, for a unit or a clear button.
|
|
43
46
|
* @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
|
|
44
47
|
* @slot hint - The hint, for content richer than plain text.
|
|
45
48
|
* @slot indicator - Replaces the caret that shows the field opens a list.
|
|
46
49
|
* @slot label - The label, for content richer than plain text.
|
|
50
|
+
* @slot loading - What the list shows while the options load, for content richer than `loading-text`.
|
|
47
51
|
* @slot start - Before the text, for an icon.
|
|
48
52
|
*
|
|
49
53
|
* @event {Event} change - Fired after the value changes.
|
|
@@ -71,16 +75,18 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
71
75
|
* @csspart listbox-hint - Help under the list.
|
|
72
76
|
* @csspart listbox-items - The element that holds the options.
|
|
73
77
|
* @csspart listbox-label - The label above the list.
|
|
74
|
-
* @csspart listbox-live - The inner list's `live` part, the region screen readers announce its empty and loading messages from.
|
|
78
|
+
* @csspart listbox-live - The inner list's `live` part, the region screen readers announce its empty and loading messages, and how many options typing left, from.
|
|
75
79
|
* @csspart listbox-loading - Shown while the options are loading.
|
|
76
80
|
* @csspart listbox-search - The region holding the field that narrows the list.
|
|
77
81
|
* @csspart listbox-search-field - The filter field inside that region.
|
|
78
82
|
* @csspart rest - The count of choices held beyond `max-tags`. A `<button>` with `expandable`, a `<span>` without it.
|
|
79
83
|
* @csspart surface - The floating panel.
|
|
80
84
|
* @csspart tag - One chosen option.
|
|
85
|
+
* @csspart tag-cell - The tag's grid cell, which a tag draws only inside a tag group.
|
|
81
86
|
* @csspart tag-dismiss - The remove button, when `dismissible`.
|
|
82
87
|
* @csspart tag-dismiss-mark - The glyph on the dismiss control, where none is slotted.
|
|
83
88
|
* @csspart tag-label - The label, trimmed to its capitals and baseline.
|
|
89
|
+
* @csspart tag-link - The tag's link around its label, which a tag draws only with `href`.
|
|
84
90
|
* @csspart tag-start - The category before the label.
|
|
85
91
|
* @csspart tags - The row of chosen options, when `multiple` is set.
|
|
86
92
|
*
|
|
@@ -89,6 +95,17 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
89
95
|
*/
|
|
90
96
|
export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
91
97
|
protected readonly aria: AriaForwardController;
|
|
98
|
+
/**
|
|
99
|
+
* Whether text that names no option is taken as the value, as React Aria's
|
|
100
|
+
* `allowsCustomValue`.
|
|
101
|
+
*
|
|
102
|
+
* For a field where the list is a help and not the whole of the answer, a
|
|
103
|
+
* city or a job title. What was typed becomes the value when the reader
|
|
104
|
+
* leaves the field or presses Enter with nothing highlighted, and text that
|
|
105
|
+
* names an option takes that option. Unlike `creatable`, no event asks
|
|
106
|
+
* first and no option is expected to be added: the value is the text.
|
|
107
|
+
*/
|
|
108
|
+
allowsCustomValue: boolean;
|
|
92
109
|
/**
|
|
93
110
|
* What the browser may autofill in the text field, as a platform token.
|
|
94
111
|
*
|
|
@@ -174,6 +191,17 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
174
191
|
* removes rather than as "Remove" said once per chosen option.
|
|
175
192
|
*/
|
|
176
193
|
removeLabel: string;
|
|
194
|
+
/**
|
|
195
|
+
* What a screen reader hears when typing leaves one option. `{count}` is
|
|
196
|
+
* the count.
|
|
197
|
+
*/
|
|
198
|
+
resultLabel: string;
|
|
199
|
+
/**
|
|
200
|
+
* What a screen reader hears when typing leaves any other number of
|
|
201
|
+
* options. `{count}` is the count, and `Intl.PluralRules` picks between the
|
|
202
|
+
* two.
|
|
203
|
+
*/
|
|
204
|
+
resultsLabel: string;
|
|
177
205
|
/**
|
|
178
206
|
* Whether the field completes the word as the user types.
|
|
179
207
|
*
|
|
@@ -313,6 +341,11 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
313
341
|
* once answered.
|
|
314
342
|
*/
|
|
315
343
|
private commitEmptied;
|
|
344
|
+
/**
|
|
345
|
+
* Takes what was typed as the answer, where `allows-custom-value` asks: the
|
|
346
|
+
* option its words name, or the text itself. Returns whether it took any.
|
|
347
|
+
*/
|
|
348
|
+
private commitTyped;
|
|
316
349
|
/** The options the arrows can reach. */
|
|
317
350
|
private get reachable();
|
|
318
351
|
/**
|
|
@@ -418,6 +451,8 @@ export declare class HelixHeadlessCombobox extends HelixHeadlessCombobox_base {
|
|
|
418
451
|
* component: a form that received "jav" got the filter, not an option.
|
|
419
452
|
*/
|
|
420
453
|
private sync;
|
|
454
|
+
/** A link option, once followed, closes the panel as a choice does. */
|
|
455
|
+
private readonly handleLinkFollowed;
|
|
421
456
|
connectedCallback(): void;
|
|
422
457
|
disconnectedCallback(): void;
|
|
423
458
|
protected willUpdate(changed: PropertyValues): void;
|
|
@@ -4,7 +4,7 @@ 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 { AriaForwardController, customElement, fillTemplate, HelixCreateEvent, HelixFilterEvent, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, reportValues, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, afterLinkFollowed, customElement, fillTemplate, HelixCreateEvent, HelixFilterEvent, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, reportValues, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -21,6 +21,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
21
21
|
this.aria = new AriaForwardController(this, {
|
|
22
22
|
target: () => this.partNamed('control'),
|
|
23
23
|
});
|
|
24
|
+
this.allowsCustomValue = false;
|
|
24
25
|
this.autocomplete = 'off';
|
|
25
26
|
this.creatable = false;
|
|
26
27
|
this.createLabel = 'Add';
|
|
@@ -32,6 +33,8 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
32
33
|
this.placeholder = '';
|
|
33
34
|
this.readOnly = false;
|
|
34
35
|
this.removeLabel = 'Remove {name}';
|
|
36
|
+
this.resultLabel = '{count} result';
|
|
37
|
+
this.resultsLabel = '{count} results';
|
|
35
38
|
this.suggest = false;
|
|
36
39
|
this.typed = '';
|
|
37
40
|
this.suggestion = '';
|
|
@@ -81,6 +84,12 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
81
84
|
if (this.open) {
|
|
82
85
|
event.preventDefault();
|
|
83
86
|
this.close();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!this.multiple && this.value !== '' && !this.readOnly) {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
event.stopPropagation();
|
|
92
|
+
this.requestValue('');
|
|
84
93
|
}
|
|
85
94
|
return;
|
|
86
95
|
}
|
|
@@ -99,6 +108,16 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
99
108
|
this.create();
|
|
100
109
|
return;
|
|
101
110
|
}
|
|
111
|
+
if (event.key === 'Enter' &&
|
|
112
|
+
this.allowsCustomValue &&
|
|
113
|
+
!this.reachable.some((option) => option.active) &&
|
|
114
|
+
this.commitTyped()) {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
if (!this.multiple) {
|
|
117
|
+
this.close();
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
102
121
|
if (!this.open) {
|
|
103
122
|
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
104
123
|
event.preventDefault();
|
|
@@ -151,6 +170,9 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
151
170
|
const tag = event.currentTarget;
|
|
152
171
|
this.drop(tag.dataset.value ?? '');
|
|
153
172
|
};
|
|
173
|
+
this.handleLinkFollowed = (event) => {
|
|
174
|
+
afterLinkFollowed(event, () => this.requestOpen(false));
|
|
175
|
+
};
|
|
154
176
|
}
|
|
155
177
|
requestValue(next) {
|
|
156
178
|
if (this.readOnly) {
|
|
@@ -246,6 +268,28 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
246
268
|
this.requestValue('');
|
|
247
269
|
}
|
|
248
270
|
this.emptied = false;
|
|
271
|
+
this.commitTyped();
|
|
272
|
+
}
|
|
273
|
+
commitTyped() {
|
|
274
|
+
const typed = this.typed.trim();
|
|
275
|
+
if (!this.allowsCustomValue || !this.editing || typed === '') {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
const named = (this.listbox?.options ?? []).find((option) => option.text.trim().toLowerCase() === typed.toLowerCase());
|
|
279
|
+
const answer = named?.value ?? typed;
|
|
280
|
+
if (this.multiple) {
|
|
281
|
+
if (!this.values.includes(answer)) {
|
|
282
|
+
this.requestValue([...this.values, answer].join(','));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else if (answer !== this.value) {
|
|
286
|
+
this.requestValue(answer);
|
|
287
|
+
}
|
|
288
|
+
this.typed = '';
|
|
289
|
+
this.suggestion = '';
|
|
290
|
+
this.editing = false;
|
|
291
|
+
this.listbox?.requestFilter('');
|
|
292
|
+
return true;
|
|
249
293
|
}
|
|
250
294
|
get reachable() {
|
|
251
295
|
return (this.listbox?.options ?? []).filter((option) => !option.hidden && !option.disabled);
|
|
@@ -312,6 +356,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
312
356
|
}
|
|
313
357
|
connectedCallback() {
|
|
314
358
|
super.connectedCallback();
|
|
359
|
+
this.addEventListener('helix-select', this.handleLinkFollowed);
|
|
315
360
|
this.watcher = new MutationObserver(() => this.requestUpdate());
|
|
316
361
|
this.watcher.observe(this, {
|
|
317
362
|
attributeFilter: ['label', 'value'],
|
|
@@ -322,6 +367,7 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
322
367
|
}
|
|
323
368
|
disconnectedCallback() {
|
|
324
369
|
super.disconnectedCallback();
|
|
370
|
+
this.removeEventListener('helix-select', this.handleLinkFollowed);
|
|
325
371
|
this.watcher?.disconnect();
|
|
326
372
|
this.watcher = undefined;
|
|
327
373
|
}
|
|
@@ -390,6 +436,8 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
390
436
|
aria-label=${this.listLabel}
|
|
391
437
|
empty-text=${this.emptyText}
|
|
392
438
|
loading-text=${this.loadingText}
|
|
439
|
+
result-label=${this.resultLabel}
|
|
440
|
+
results-label=${this.resultsLabel}
|
|
393
441
|
?loading=${this.loading}
|
|
394
442
|
?multiple=${this.multiple}
|
|
395
443
|
.value=${this.value}
|
|
@@ -404,12 +452,17 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
404
452
|
property, so nothing else would schedule the update that fixes
|
|
405
453
|
it. -->
|
|
406
454
|
<slot @slotchange=${() => this.requestUpdate()}></slot>
|
|
455
|
+
<!-- Handed on to the list's own, with the text as this slot's fallback:
|
|
456
|
+
a slot passed into another is assigned there whether or not anything
|
|
457
|
+
fills it, so the list's fallback would never show. -->
|
|
458
|
+
<slot name="empty" slot="empty">${this.emptyText}</slot>
|
|
459
|
+
<slot name="loading" slot="loading">${this.loadingText}</slot>
|
|
407
460
|
</${this.tag('listbox')}>`;
|
|
408
461
|
}
|
|
409
462
|
renderTag(option) {
|
|
410
463
|
return html `<${this.tag('tag')}
|
|
411
464
|
part="tag"
|
|
412
|
-
exportparts="dismiss: tag-dismiss, dismiss-mark: tag-dismiss-mark, label: tag-label, start: tag-start"
|
|
465
|
+
exportparts="cell: tag-cell, dismiss: tag-dismiss, dismiss-mark: tag-dismiss-mark, label: tag-label, link: tag-link, start: tag-start"
|
|
413
466
|
data-value=${option.value}
|
|
414
467
|
dismiss-label=${fillTemplate(this.removeLabel, { name: option.text })}
|
|
415
468
|
?dismissible=${!this.isDisabled && !this.readOnly}
|
|
@@ -473,6 +526,9 @@ let HelixHeadlessCombobox = class HelixHeadlessCombobox extends MixinControllerO
|
|
|
473
526
|
${this.renderHint()}${this.renderFeedback()}`;
|
|
474
527
|
}
|
|
475
528
|
};
|
|
529
|
+
__decorate([
|
|
530
|
+
property({ attribute: 'allows-custom-value', type: Boolean })
|
|
531
|
+
], HelixHeadlessCombobox.prototype, "allowsCustomValue", void 0);
|
|
476
532
|
__decorate([
|
|
477
533
|
property()
|
|
478
534
|
], HelixHeadlessCombobox.prototype, "autocomplete", void 0);
|
|
@@ -506,6 +562,12 @@ __decorate([
|
|
|
506
562
|
__decorate([
|
|
507
563
|
property({ attribute: 'remove-label' })
|
|
508
564
|
], HelixHeadlessCombobox.prototype, "removeLabel", void 0);
|
|
565
|
+
__decorate([
|
|
566
|
+
property({ attribute: 'result-label' })
|
|
567
|
+
], HelixHeadlessCombobox.prototype, "resultLabel", void 0);
|
|
568
|
+
__decorate([
|
|
569
|
+
property({ attribute: 'results-label' })
|
|
570
|
+
], HelixHeadlessCombobox.prototype, "resultsLabel", void 0);
|
|
509
571
|
__decorate([
|
|
510
572
|
property({ type: Boolean })
|
|
511
573
|
], HelixHeadlessCombobox.prototype, "suggest", void 0);
|
|
@@ -14,7 +14,8 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
14
14
|
*
|
|
15
15
|
* The palette keeps no value. It is not a form control and submits nothing.
|
|
16
16
|
* Choosing an option dispatches `helix-select` once. To remember the last
|
|
17
|
-
* command, store it yourself.
|
|
17
|
+
* command, store it yourself. An option with `href` is a link to a page, which
|
|
18
|
+
* the browser follows once the palette has closed.
|
|
18
19
|
*
|
|
19
20
|
* The hotkey listener is on the document, so the hotkey works from anywhere on
|
|
20
21
|
* the page, including inside another control's text field, whether or not the
|
|
@@ -22,7 +23,8 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
22
23
|
* write one binding.
|
|
23
24
|
*
|
|
24
25
|
* The filter is cleared when the palette closes, so each opening starts from
|
|
25
|
-
* the full list. Combobox and Select do the same.
|
|
26
|
+
* the full list. Combobox and Select do the same. As typing narrows the list, a
|
|
27
|
+
* screen reader hears how many commands are left, such as "3 commands".
|
|
26
28
|
*
|
|
27
29
|
* @since 0.1.0
|
|
28
30
|
* @status stable
|
|
@@ -31,15 +33,22 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
31
33
|
* @documentation https://fusion.dev/helix/components/command
|
|
32
34
|
*
|
|
33
35
|
* @slot - The commands, and any groups of them.
|
|
36
|
+
* @slot empty - What the palette shows when typing matches no commands, for content richer than `empty-text`.
|
|
34
37
|
* @slot footer - Content under the list, such as the keys that work in the palette.
|
|
38
|
+
* @slot loading - What the palette shows while the commands load, for content richer than `loading-text`.
|
|
35
39
|
*
|
|
36
40
|
* @event {HelixBeforeCloseEvent} helix-before-close - The palette is about to close. Cancelable: cancel it to keep it open.
|
|
37
41
|
* @event {HelixBeforeOpenEvent} helix-before-open - The palette is about to open. Cancelable: cancel it to keep it closed.
|
|
38
42
|
* @event {HelixFilterEvent} helix-filter - Fired as the user types, with the text the list is filtered by. Cancelable: cancel it to leave the list unfiltered and filter the commands yourself, for example on a server while `loading` is set.
|
|
39
|
-
* @event {HelixSelectEvent} helix-select - A command was chosen. Cancelable: cancel it to keep the palette open.
|
|
43
|
+
* @event {HelixSelectEvent} helix-select - A command was chosen, with its `href` where the option is a link. Cancelable: cancel it to keep the palette open, and to keep the browser from following the link.
|
|
40
44
|
* @event {ToggleEvent} toggle - The palette opened or closed. The event carries `newState` and `oldState`.
|
|
41
45
|
*
|
|
42
46
|
* @csspart dialog - The box of the dialog the palette opens in, forwarded from `Dialog`.
|
|
47
|
+
* @csspart dialog-body - The dialog's slot for its content, which holds the list. It draws no box until you give it one.
|
|
48
|
+
* @csspart dialog-dismiss - The dialog's close control, drawn when the dialog is `dismissible`.
|
|
49
|
+
* @csspart dialog-dismiss-mark - The glyph on the dialog's close control, where none is slotted.
|
|
50
|
+
* @csspart dialog-footer - The dialog's slot for its actions, which holds the `footer` row. It draws no box until you give it one.
|
|
51
|
+
* @csspart dialog-header - The dialog's slot for its title. It draws no box until you give it one.
|
|
43
52
|
* @csspart footer - The row under the list.
|
|
44
53
|
* @csspart listbox - The listbox inside the palette.
|
|
45
54
|
* @csspart listbox-empty - The text shown when the filter matches no commands.
|
|
@@ -49,7 +58,7 @@ declare const HelixHeadlessCommand_base: (new (...args: any[]) => import("@fusio
|
|
|
49
58
|
* @csspart listbox-hint - Help text under the list.
|
|
50
59
|
* @csspart listbox-items - The list of commands.
|
|
51
60
|
* @csspart listbox-label - The label above the list.
|
|
52
|
-
* @csspart listbox-live - The listbox's `live` part, a live region that holds the empty and loading text for screen readers.
|
|
61
|
+
* @csspart listbox-live - The listbox's `live` part, a live region that holds the empty and loading text, and how many commands typing left, for screen readers.
|
|
53
62
|
* @csspart listbox-loading - The text shown while the commands are loading.
|
|
54
63
|
* @csspart listbox-search - The region that holds the filter field.
|
|
55
64
|
* @csspart listbox-search-field - The filter field.
|
|
@@ -61,6 +70,12 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
61
70
|
* The text shown when the filter matches no commands.
|
|
62
71
|
*/
|
|
63
72
|
emptyText: string;
|
|
73
|
+
/**
|
|
74
|
+
* How long typing rests before the commands are filtered, in milliseconds,
|
|
75
|
+
* so a palette filtered on a server sends one `helix-filter` for a word.
|
|
76
|
+
* `0` filters on every keystroke. See Listbox's `filter-delay`.
|
|
77
|
+
*/
|
|
78
|
+
filterDelay: number;
|
|
64
79
|
/**
|
|
65
80
|
* The key that opens the palette, written as `mod+k`, `shift+p`, or `k`.
|
|
66
81
|
*
|
|
@@ -88,6 +103,17 @@ export declare class HelixHeadlessCommand extends HelixHeadlessCommand_base {
|
|
|
88
103
|
* The text shown in the filter field before anything is typed.
|
|
89
104
|
*/
|
|
90
105
|
placeholder: string;
|
|
106
|
+
/**
|
|
107
|
+
* What a screen reader hears when typing leaves one command. `{count}` is
|
|
108
|
+
* the count.
|
|
109
|
+
*/
|
|
110
|
+
resultLabel: string;
|
|
111
|
+
/**
|
|
112
|
+
* What a screen reader hears when typing leaves any other number of
|
|
113
|
+
* commands. `{count}` is the count, and `Intl.PluralRules` picks between the
|
|
114
|
+
* two.
|
|
115
|
+
*/
|
|
116
|
+
resultsLabel: string;
|
|
91
117
|
/**
|
|
92
118
|
* Whether the consumer put anything under the list.
|
|
93
119
|
*
|
|
@@ -22,11 +22,14 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
22
22
|
constructor() {
|
|
23
23
|
super(...arguments);
|
|
24
24
|
this.emptyText = 'No commands';
|
|
25
|
+
this.filterDelay = 0;
|
|
25
26
|
this.hotkey = 'mod+k';
|
|
26
27
|
this.label = 'Commands';
|
|
27
28
|
this.loading = false;
|
|
28
29
|
this.loadingText = 'Loading';
|
|
29
30
|
this.placeholder = 'Search commands';
|
|
31
|
+
this.resultLabel = '{count} command';
|
|
32
|
+
this.resultsLabel = '{count} commands';
|
|
30
33
|
this.footed = false;
|
|
31
34
|
this.handleHotkey = (event) => {
|
|
32
35
|
if (this.hotkey === '') {
|
|
@@ -73,9 +76,19 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
73
76
|
}
|
|
74
77
|
};
|
|
75
78
|
this.swallowOptionSelect = (event) => {
|
|
76
|
-
if (event.target
|
|
77
|
-
|
|
79
|
+
if (event.target === this) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
event.stopImmediatePropagation();
|
|
83
|
+
const { href, value } = event.detail;
|
|
84
|
+
if (href === undefined) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!this.dispatchEvent(new HelixSelectEvent({ href, value }))) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
return;
|
|
78
90
|
}
|
|
91
|
+
this.requestOpen(false);
|
|
79
92
|
};
|
|
80
93
|
}
|
|
81
94
|
get listbox() {
|
|
@@ -125,7 +138,8 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
125
138
|
}
|
|
126
139
|
renderDialog() {
|
|
127
140
|
return html `<${this.tag('dialog')}
|
|
128
|
-
exportparts="dialog
|
|
141
|
+
exportparts="body: dialog-body, dialog, dismiss: dialog-dismiss, dismiss-mark: dialog-dismiss-mark,
|
|
142
|
+
footer: dialog-footer, header: dialog-header"
|
|
129
143
|
label=${this.label}
|
|
130
144
|
.open=${this.open}
|
|
131
145
|
@helix-before-close=${() => {
|
|
@@ -142,8 +156,11 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
142
156
|
search-field: listbox-search-field"
|
|
143
157
|
aria-label=${this.label}
|
|
144
158
|
empty-text=${this.emptyText}
|
|
159
|
+
filter-delay=${this.filterDelay}
|
|
145
160
|
filter-label=${this.placeholder}
|
|
146
161
|
loading-text=${this.loadingText}
|
|
162
|
+
result-label=${this.resultLabel}
|
|
163
|
+
results-label=${this.resultsLabel}
|
|
147
164
|
?loading=${this.loading}
|
|
148
165
|
filterable
|
|
149
166
|
@helix-before-change=${this.handleChange}
|
|
@@ -151,6 +168,10 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
151
168
|
@input=${this.stopInner}
|
|
152
169
|
>
|
|
153
170
|
<slot></slot>
|
|
171
|
+
<!-- Handed on with the text as the fallback, for the reason Combobox
|
|
172
|
+
gives: the list's own fallback never shows behind a passed slot. -->
|
|
173
|
+
<slot name="empty" slot="empty">${this.emptyText}</slot>
|
|
174
|
+
<slot name="loading" slot="loading">${this.loadingText}</slot>
|
|
154
175
|
</${this.tag('listbox')}>
|
|
155
176
|
<div ?hidden=${!this.footed} part="footer" slot="footer">
|
|
156
177
|
<slot
|
|
@@ -169,6 +190,9 @@ let HelixHeadlessCommand = class HelixHeadlessCommand extends MixinControllerOpe
|
|
|
169
190
|
__decorate([
|
|
170
191
|
property({ attribute: 'empty-text' })
|
|
171
192
|
], HelixHeadlessCommand.prototype, "emptyText", void 0);
|
|
193
|
+
__decorate([
|
|
194
|
+
property({ attribute: 'filter-delay', type: Number })
|
|
195
|
+
], HelixHeadlessCommand.prototype, "filterDelay", void 0);
|
|
172
196
|
__decorate([
|
|
173
197
|
property()
|
|
174
198
|
], HelixHeadlessCommand.prototype, "hotkey", void 0);
|
|
@@ -184,6 +208,12 @@ __decorate([
|
|
|
184
208
|
__decorate([
|
|
185
209
|
property()
|
|
186
210
|
], HelixHeadlessCommand.prototype, "placeholder", void 0);
|
|
211
|
+
__decorate([
|
|
212
|
+
property({ attribute: 'result-label' })
|
|
213
|
+
], HelixHeadlessCommand.prototype, "resultLabel", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
property({ attribute: 'results-label' })
|
|
216
|
+
], HelixHeadlessCommand.prototype, "resultsLabel", void 0);
|
|
187
217
|
__decorate([
|
|
188
218
|
state()
|
|
189
219
|
], HelixHeadlessCommand.prototype, "footed", void 0);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** What a `from` reference names: an element by its id, and what to read from it. */
|
|
2
|
+
export interface FromReference {
|
|
3
|
+
/** An attribute to read, written `id[attribute]`. */
|
|
4
|
+
readonly attribute?: string;
|
|
5
|
+
/** The id of the element. */
|
|
6
|
+
readonly id: string;
|
|
7
|
+
/** A property to read, written `id.property`. */
|
|
8
|
+
readonly property?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reads a `from` reference: `id` for the element's text, `id[attribute]` for
|
|
12
|
+
* an attribute, and `id.property` for a property. Web Awesome's Copy Button
|
|
13
|
+
* takes the same three shapes.
|
|
14
|
+
*
|
|
15
|
+
* `null` for a reference with no id, or with anything after the brackets.
|
|
16
|
+
*/
|
|
17
|
+
export declare const parseFrom: (reference: string) => FromReference | null;
|
|
18
|
+
/**
|
|
19
|
+
* The text a `from` reference names, looked up in the tree `scope` is in, or
|
|
20
|
+
* `null` where the element or its attribute is not there.
|
|
21
|
+
*/
|
|
22
|
+
export declare const readFrom: (reference: string, scope: Element) => string | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { elementById } from '@fusion.dev/helix-core';
|
|
2
|
+
export const parseFrom = (reference) => {
|
|
3
|
+
const written = reference.trim();
|
|
4
|
+
const bracket = /^([^[\].\s]+)\[([^\]\s]+)\]$/u.exec(written);
|
|
5
|
+
if (bracket) {
|
|
6
|
+
return { attribute: bracket[2], id: bracket[1] };
|
|
7
|
+
}
|
|
8
|
+
const dot = /^([^[\].\s]+)\.([^[\].\s]+)$/u.exec(written);
|
|
9
|
+
if (dot) {
|
|
10
|
+
return { id: dot[1], property: dot[2] };
|
|
11
|
+
}
|
|
12
|
+
return /^[^[\].\s]+$/u.test(written) ? { id: written } : null;
|
|
13
|
+
};
|
|
14
|
+
export const readFrom = (reference, scope) => {
|
|
15
|
+
const parsed = parseFrom(reference);
|
|
16
|
+
const element = parsed ? elementById(scope, parsed.id) : null;
|
|
17
|
+
if (!(parsed && element)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (parsed.attribute !== undefined) {
|
|
21
|
+
return element.getAttribute(parsed.attribute);
|
|
22
|
+
}
|
|
23
|
+
if (parsed.property !== undefined) {
|
|
24
|
+
const read = element[parsed.property];
|
|
25
|
+
return read === undefined || read === null ? null : String(read);
|
|
26
|
+
}
|
|
27
|
+
return element.textContent;
|
|
28
|
+
};
|
|
@@ -12,7 +12,13 @@ import '../../tooltip/index.js';
|
|
|
12
12
|
* The confirmation and the event wait for the write to finish. The browser can
|
|
13
13
|
* refuse a clipboard write, for example on an insecure origin, in a document
|
|
14
14
|
* that is not focused, or after a gesture that does not qualify. A refused
|
|
15
|
-
* write
|
|
15
|
+
* write, or nothing to copy, names the button with `error-label` for the same
|
|
16
|
+
* moment a copy shows `copied-label`, draws a cross, and sends
|
|
17
|
+
* `helix-copy-error`.
|
|
18
|
+
*
|
|
19
|
+
* `from` copies from another element on the page: its text, an attribute
|
|
20
|
+
* written `id[attribute]`, or a property written `id.property`, read at the
|
|
21
|
+
* press. It takes the place of `value`.
|
|
16
22
|
*
|
|
17
23
|
* It replaces Button's render and its press, so it never submits a form or
|
|
18
24
|
* sends a command: `type`, `command`, `commandfor` and the `form*` overrides
|
|
@@ -28,10 +34,13 @@ import '../../tooltip/index.js';
|
|
|
28
34
|
*
|
|
29
35
|
* @slot - The icon shown before a copy. Defaults to a clipboard.
|
|
30
36
|
* @slot copied - The icon shown after a successful copy. Defaults to a check mark.
|
|
37
|
+
* @slot failed - The icon shown after a copy that put nothing on the clipboard. Defaults to a cross.
|
|
31
38
|
*
|
|
32
39
|
* @event {HelixCopyEvent} helix-copy - Fired after the clipboard write succeeds.
|
|
40
|
+
* @event {Event} helix-copy-error - Fired when a press put nothing on the clipboard: the browser refused the write, or there was nothing to copy.
|
|
33
41
|
*
|
|
34
42
|
* @csspart button - The inner button.
|
|
43
|
+
* @csspart live - The live region that says `pending-label` when the button becomes pending under focus. It is rendered from the first time the button is pending.
|
|
35
44
|
* @csspart mark - The clipboard icon, and the confirmation icon that replaces it.
|
|
36
45
|
* @csspart tooltip - The tooltip bubble, forwarded from the tooltip inside the button.
|
|
37
46
|
* @csspart tooltip-arrow - The tooltip's arrow, which points at the button.
|
|
@@ -52,6 +61,18 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
|
|
|
52
61
|
* dash-casing it, and `copiedlabel` is not what anyone would type.
|
|
53
62
|
*/
|
|
54
63
|
copiedLabel: string;
|
|
64
|
+
/**
|
|
65
|
+
* The button's name and tooltip text after a press that put nothing on the
|
|
66
|
+
* clipboard.
|
|
67
|
+
*/
|
|
68
|
+
errorLabel: string;
|
|
69
|
+
/**
|
|
70
|
+
* The element to copy from, by its id: `key` for its text, `key[value]` for
|
|
71
|
+
* an attribute, and `key.value` for a property. Read at the press, and used
|
|
72
|
+
* in place of `value`. An element that is not there copies nothing, which is
|
|
73
|
+
* a failed copy.
|
|
74
|
+
*/
|
|
75
|
+
from: string;
|
|
55
76
|
/**
|
|
56
77
|
* The button's name and tooltip text before a copy.
|
|
57
78
|
*
|
|
@@ -107,6 +128,9 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
|
|
|
107
128
|
*/
|
|
108
129
|
private readonly clipboard;
|
|
109
130
|
private get copied();
|
|
131
|
+
private get failed();
|
|
132
|
+
/** What a press copies: the text `from` names, or `value`. */
|
|
133
|
+
private get text();
|
|
110
134
|
private readonly handleCopy;
|
|
111
135
|
/**
|
|
112
136
|
* Both faces, drawn rather than demanded.
|
|
@@ -139,6 +163,8 @@ export declare class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
|
|
|
139
163
|
* the focused element mid-press.
|
|
140
164
|
*/
|
|
141
165
|
protected renderCopiedIcon(): TemplateResult;
|
|
166
|
+
/** The mark for a press that put nothing on the clipboard, behind a seam of its own. */
|
|
167
|
+
protected renderFailedIcon(): TemplateResult;
|
|
142
168
|
/**
|
|
143
169
|
* The bubble the control sits inside, behind a seam.
|
|
144
170
|
*
|
|
@@ -6,30 +6,44 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
var HelixHeadlessCopyButton_1;
|
|
8
8
|
import { CopyController, customElement } from '@fusion.dev/helix-core';
|
|
9
|
-
import { check, copy } from '@fusion.dev/helix-icons';
|
|
9
|
+
import { check, copy, x } from '@fusion.dev/helix-icons';
|
|
10
10
|
import { nothing } from 'lit';
|
|
11
11
|
import { property } from 'lit/decorators.js';
|
|
12
12
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
13
13
|
import { html } from 'lit/static-html.js';
|
|
14
14
|
import { HelixHeadlessIconButton } from '../../icon-button/root/icon-button-root.js';
|
|
15
|
+
import { readFrom } from '../from.js';
|
|
15
16
|
import '../../tooltip/index.js';
|
|
16
17
|
const CONFIRM = 1600;
|
|
17
18
|
let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCopyButton extends HelixHeadlessIconButton {
|
|
18
19
|
constructor() {
|
|
19
20
|
super(...arguments);
|
|
20
21
|
this.copiedLabel = 'Copied';
|
|
22
|
+
this.errorLabel = 'Could not copy';
|
|
23
|
+
this.from = '';
|
|
21
24
|
this.label = 'Copy';
|
|
22
25
|
this.value = '';
|
|
23
26
|
this.namedBy = null;
|
|
24
27
|
this.clipboard = new CopyController(this, { after: CONFIRM });
|
|
25
|
-
this.handleCopy = async () => {
|
|
28
|
+
this.handleCopy = async (event) => {
|
|
29
|
+
if (this.pending) {
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
event.stopImmediatePropagation();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
26
34
|
if (this.isDisabled) {
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
|
-
await this.clipboard.copy(this.
|
|
37
|
+
const wrote = await this.clipboard.copy(this.text);
|
|
38
|
+
if (!wrote && this.isConnected) {
|
|
39
|
+
this.dispatchEvent(new Event('helix-copy-error', { bubbles: true, composed: true }));
|
|
40
|
+
}
|
|
30
41
|
};
|
|
31
42
|
}
|
|
32
43
|
get says() {
|
|
44
|
+
if (this.failed) {
|
|
45
|
+
return this.errorLabel;
|
|
46
|
+
}
|
|
33
47
|
return this.copied ? this.copiedLabel : this.namedBy || this.label;
|
|
34
48
|
}
|
|
35
49
|
get forwardsLabel() {
|
|
@@ -41,32 +55,48 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
|
|
|
41
55
|
get copied() {
|
|
42
56
|
return this.clipboard.copied;
|
|
43
57
|
}
|
|
58
|
+
get failed() {
|
|
59
|
+
return this.clipboard.failed;
|
|
60
|
+
}
|
|
61
|
+
get text() {
|
|
62
|
+
return this.from === '' ? this.value : (readFrom(this.from, this) ?? '');
|
|
63
|
+
}
|
|
44
64
|
static icon(glyph) {
|
|
45
65
|
return html `<svg part="mark" fill="currentColor" viewBox=${glyph.viewBox} aria-hidden="true">
|
|
46
66
|
${unsafeSVG(glyph.content)}
|
|
47
67
|
</svg>`;
|
|
48
68
|
}
|
|
49
69
|
render() {
|
|
50
|
-
return this.renderTooltip(html `<button
|
|
70
|
+
return html `${this.renderTooltip(html `<button
|
|
51
71
|
part="button"
|
|
52
72
|
type="button"
|
|
73
|
+
aria-busy=${this.pending ? 'true' : nothing}
|
|
74
|
+
aria-disabled=${this.pending ? 'true' : nothing}
|
|
53
75
|
aria-label=${this.says}
|
|
54
76
|
data-copied=${this.copied ? '' : nothing}
|
|
77
|
+
data-failed=${this.failed ? '' : nothing}
|
|
55
78
|
tabindex=${this.focusable ? nothing : '-1'}
|
|
56
79
|
?disabled=${this.isDisabled}
|
|
57
80
|
@click=${this.handleCopy}
|
|
58
81
|
>
|
|
59
|
-
${this.renderCopiedIcon()} ${this.renderCopyIcon()}
|
|
60
|
-
</button>`)
|
|
82
|
+
${this.renderCopiedIcon()} ${this.renderFailedIcon()} ${this.renderCopyIcon()}
|
|
83
|
+
</button>`)}${this.renderLive()}`;
|
|
61
84
|
}
|
|
62
85
|
renderCopyIcon() {
|
|
63
|
-
return html `<slot ?hidden=${this.copied
|
|
86
|
+
return html `<slot ?hidden=${this.copied || this.failed}
|
|
87
|
+
>${HelixHeadlessCopyButton_1.icon(copy)}</slot
|
|
88
|
+
>`;
|
|
64
89
|
}
|
|
65
90
|
renderCopiedIcon() {
|
|
66
91
|
return html `<slot name="copied" ?hidden=${!this.copied}
|
|
67
92
|
>${HelixHeadlessCopyButton_1.icon(check)}</slot
|
|
68
93
|
>`;
|
|
69
94
|
}
|
|
95
|
+
renderFailedIcon() {
|
|
96
|
+
return html `<slot name="failed" ?hidden=${!this.failed}
|
|
97
|
+
>${HelixHeadlessCopyButton_1.icon(x)}</slot
|
|
98
|
+
>`;
|
|
99
|
+
}
|
|
70
100
|
renderTooltip(control) {
|
|
71
101
|
return html `<${this.tag('tooltip')} content=${this.says} persist
|
|
72
102
|
exportparts="arrow: tooltip-arrow, arrow-bottom: tooltip-arrow-bottom,
|
|
@@ -80,6 +110,12 @@ let HelixHeadlessCopyButton = HelixHeadlessCopyButton_1 = class HelixHeadlessCop
|
|
|
80
110
|
__decorate([
|
|
81
111
|
property({ attribute: 'copied-label' })
|
|
82
112
|
], HelixHeadlessCopyButton.prototype, "copiedLabel", void 0);
|
|
113
|
+
__decorate([
|
|
114
|
+
property({ attribute: 'error-label' })
|
|
115
|
+
], HelixHeadlessCopyButton.prototype, "errorLabel", void 0);
|
|
116
|
+
__decorate([
|
|
117
|
+
property()
|
|
118
|
+
], HelixHeadlessCopyButton.prototype, "from", void 0);
|
|
83
119
|
__decorate([
|
|
84
120
|
property()
|
|
85
121
|
], HelixHeadlessCopyButton.prototype, "label", void 0);
|