@fusion.dev/helix-headless 0.143.0 → 0.145.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 +16990 -1383
- package/dist/accordion/item/accordion-item.d.ts +48 -12
- package/dist/accordion/item/accordion-item.js +26 -5
- package/dist/accordion/root/accordion-root.d.ts +6 -0
- package/dist/accordion/root/accordion-root.js +7 -1
- package/dist/announcement/root/announcement-root.d.ts +21 -1
- package/dist/announcement/root/announcement-root.js +36 -1
- package/dist/app-header/root/app-header-root.d.ts +7 -0
- package/dist/app-header/root/app-header-root.js +5 -1
- package/dist/app-header/sectioning.d.ts +7 -0
- package/dist/app-header/sectioning.js +26 -0
- 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/breadcrumbs/root/breadcrumbs-root.d.ts +32 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +25 -2
- 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/canvas/root/canvas-root.d.ts +38 -3
- package/dist/canvas/root/canvas-root.js +121 -40
- package/dist/card/root/card-root.d.ts +30 -1
- package/dist/card/root/card-root.js +38 -5
- package/dist/carousel/root/carousel-root.d.ts +90 -0
- package/dist/carousel/root/carousel-root.js +134 -12
- package/dist/chat/composer/chat-composer.d.ts +38 -6
- package/dist/chat/composer/chat-composer.js +42 -9
- package/dist/chat/message/chat-message.d.ts +32 -1
- package/dist/chat/message/chat-message.js +25 -9
- package/dist/chat/naming.d.ts +22 -0
- package/dist/chat/naming.js +12 -0
- package/dist/chat/relative-clock.d.ts +39 -0
- package/dist/chat/relative-clock.js +31 -0
- package/dist/chat/root/chat-root.d.ts +36 -0
- package/dist/chat/root/chat-root.js +24 -1
- 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 +38 -1
- package/dist/code-block/root/code-block-root.js +28 -7
- 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 +47 -1
- package/dist/copy-button/root/copy-button-root.js +44 -8
- package/dist/data-table/root/data-table-root.d.ts +150 -2
- package/dist/data-table/root/data-table-root.js +303 -29
- package/dist/data-table/tree.d.ts +40 -0
- package/dist/data-table/tree.js +55 -0
- 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/description-list/item/description-list-item.d.ts +17 -2
- package/dist/description-list/item/description-list-item.js +15 -2
- package/dist/description-list/root/description-list-root.d.ts +27 -1
- package/dist/description-list/root/description-list-root.js +26 -1
- 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/drilldown/item/drilldown-item.d.ts +11 -6
- package/dist/drilldown/item/drilldown-item.js +8 -5
- package/dist/drilldown/root/drilldown-root.d.ts +11 -0
- package/dist/drilldown/root/drilldown-root.js +8 -3
- package/dist/element-events.generated.js +61 -13
- 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/menu/filter-menu.d.ts +3 -0
- package/dist/filter-input/menu/filter-menu.js +8 -4
- 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 +23 -2
- package/dist/input/root/input-root.d.ts +11 -0
- package/dist/input/root/input-root.js +16 -1
- 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/list/item/list-item.d.ts +68 -1
- package/dist/list/item/list-item.js +66 -4
- package/dist/list/numbering.d.ts +14 -0
- package/dist/list/numbering.js +8 -0
- package/dist/list/root/list-root.d.ts +106 -9
- package/dist/list/root/list-root.js +189 -4
- package/dist/list/selection.d.ts +19 -0
- package/dist/list/selection.js +21 -0
- package/dist/listbox/root/listbox-root.d.ts +74 -3
- package/dist/listbox/root/listbox-root.js +86 -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/map/root/map-root.d.ts +6 -2
- package/dist/map/root/map-root.js +5 -2
- package/dist/megamenu/root/megamenu-root.d.ts +21 -0
- package/dist/megamenu/root/megamenu-root.js +16 -2
- package/dist/menu/index.d.ts +1 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/item/menu-item.d.ts +38 -11
- package/dist/menu/item/menu-item.js +33 -1
- package/dist/menu/root/menu-root.d.ts +25 -1
- package/dist/menu/root/menu-root.js +63 -6
- package/dist/menu/section/menu-section.d.ts +55 -0
- package/dist/menu/section/menu-section.js +60 -0
- package/dist/meter/root/meter-root.d.ts +7 -0
- package/dist/meter/root/meter-root.js +6 -3
- package/dist/navigation/root/navigation-root.d.ts +16 -1
- package/dist/navigation/root/navigation-root.js +27 -1
- 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 +60 -0
- package/dist/notifications/root/notifications-root.js +76 -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 +98 -16
- package/dist/object-inspector/root/object-inspector-root.js +180 -80
- package/dist/object-inspector/rows.d.ts +57 -0
- package/dist/object-inspector/rows.js +86 -0
- package/dist/option/root/option-root.d.ts +54 -5
- package/dist/option/root/option-root.js +34 -5
- package/dist/outline/levels.d.ts +31 -0
- package/dist/outline/levels.js +23 -0
- package/dist/outline/root/outline-root.d.ts +68 -3
- package/dist/outline/root/outline-root.js +73 -8
- package/dist/pagination/size/pagination-size.d.ts +11 -2
- package/dist/pagination/size/pagination-size.js +19 -4
- 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 +17 -0
- package/dist/radio/group/radio-group.js +14 -1
- package/dist/rating/root/rating-root.d.ts +25 -0
- package/dist/rating/root/rating-root.js +46 -4
- 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/scroll-area/root/scroll-area-root.d.ts +113 -0
- package/dist/scroll-area/root/scroll-area-root.js +218 -3
- package/dist/scroll-area/scrollbar.d.ts +65 -0
- package/dist/scroll-area/scrollbar.js +24 -0
- package/dist/select/root/select-root.d.ts +24 -0
- package/dist/select/root/select-root.js +21 -7
- 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/splitter/root/splitter-root.d.ts +13 -0
- package/dist/splitter/root/splitter-root.js +20 -2
- package/dist/splitter/snap.d.ts +9 -0
- package/dist/splitter/snap.js +12 -0
- package/dist/stat/root/stat-root.d.ts +2 -0
- package/dist/stat/root/stat-root.js +1 -1
- package/dist/steps/reach.d.ts +15 -0
- package/dist/steps/reach.js +12 -0
- package/dist/steps/root/steps-root.d.ts +30 -0
- package/dist/steps/root/steps-root.js +53 -0
- package/dist/steps/step/step.d.ts +39 -2
- package/dist/steps/step/step.js +29 -7
- package/dist/switch/root/switch-root.d.ts +12 -2
- package/dist/switch/root/switch-root.js +6 -1
- package/dist/table/column/table-column.d.ts +11 -0
- package/dist/table/column/table-column.js +6 -1
- package/dist/table/root/table-root.d.ts +101 -0
- package/dist/table/root/table-root.js +166 -5
- package/dist/table/row/table-row.d.ts +47 -1
- package/dist/table/row/table-row.js +39 -2
- package/dist/tabs/panel/tab-panel.d.ts +9 -5
- package/dist/tabs/panel/tab-panel.js +3 -4
- package/dist/tabs/root/tabs-root.d.ts +25 -1
- package/dist/tabs/root/tabs-root.js +21 -3
- package/dist/tabs/tab/tab.d.ts +36 -4
- package/dist/tabs/tab/tab.js +46 -3
- 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/timeline/group/timeline-group.d.ts +46 -0
- package/dist/timeline/group/timeline-group.js +37 -0
- package/dist/timeline/index.d.ts +1 -0
- package/dist/timeline/index.js +1 -0
- package/dist/timeline/root/timeline-root.d.ts +10 -3
- package/dist/toast/hotkey.d.ts +22 -0
- package/dist/toast/hotkey.js +23 -0
- package/dist/toast/item/toast-item.d.ts +26 -18
- package/dist/toast/item/toast-item.js +55 -48
- 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/toolbar/root/toolbar-root.d.ts +13 -0
- package/dist/toolbar/root/toolbar-root.js +15 -5
- 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/tree/item/tree-item.d.ts +54 -1
- package/dist/tree/item/tree-item.js +89 -20
- package/dist/tree/root/tree-root.d.ts +37 -0
- package/dist/tree/root/tree-root.js +51 -6
- package/dist/tree/selection.d.ts +24 -0
- package/dist/tree/selection.js +26 -0
- package/dist/trend/root/trend-root.d.ts +13 -0
- package/dist/trend/root/trend-root.js +14 -9
- package/dist/video/media-events.d.ts +49 -0
- package/dist/video/media-events.js +39 -0
- package/dist/video/root/video-root.d.ts +99 -13
- package/dist/video/root/video-root.js +84 -47
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
- package/dist/video-playlist/order.d.ts +41 -0
- package/dist/video-playlist/order.js +34 -0
- package/dist/video-playlist/root/video-playlist-root.d.ts +70 -2
- package/dist/video-playlist/root/video-playlist-root.js +60 -16
- package/dist/virtual-list/controls.d.ts +4 -0
- package/dist/virtual-list/controls.js +30 -0
- package/dist/virtual-list/fill.d.ts +30 -0
- package/dist/virtual-list/fill.js +39 -0
- package/dist/virtual-list/keys.d.ts +15 -0
- package/dist/virtual-list/keys.js +24 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +155 -23
- package/dist/virtual-list/root/virtual-list-root.js +175 -34
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +8871 -1286
- package/types/svelte.d.ts +3 -0
- package/types/vue.d.ts +1918 -269
|
@@ -4,9 +4,24 @@ 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, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, focusableElements, HelixElement, IndicatorController, KeyboardNavigationController, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
const FOCUSABLE = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
11
|
+
const navigable = (item) => {
|
|
12
|
+
const control = () => item.navigationControl ??
|
|
13
|
+
(item.matches(FOCUSABLE) ? item : focusableElements(item)[0]);
|
|
14
|
+
return {
|
|
15
|
+
contains: (node) => {
|
|
16
|
+
const target = control();
|
|
17
|
+
return target !== undefined && node !== null && (node === target || target.contains(node));
|
|
18
|
+
},
|
|
19
|
+
disabled: false,
|
|
20
|
+
focusItem: () => control()?.focus(),
|
|
21
|
+
select: () => { },
|
|
22
|
+
selected: false,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
10
25
|
let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement {
|
|
11
26
|
constructor() {
|
|
12
27
|
super(...arguments);
|
|
@@ -23,7 +38,15 @@ let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement
|
|
|
23
38
|
observe: () => this.items,
|
|
24
39
|
surface: () => this.partNamed('nav'),
|
|
25
40
|
});
|
|
41
|
+
this.arrows = false;
|
|
26
42
|
this.label = 'Main';
|
|
43
|
+
this.keys = new KeyboardNavigationController(this, {
|
|
44
|
+
items: () => (this.arrows ? this.items.map(navigable) : []),
|
|
45
|
+
origin: 'focus',
|
|
46
|
+
reserved: () => this.orientation === 'vertical' ? ['ArrowLeft', 'ArrowRight'] : ['ArrowDown', 'ArrowUp'],
|
|
47
|
+
selects: () => false,
|
|
48
|
+
wrap: false,
|
|
49
|
+
});
|
|
27
50
|
this.aria = new AriaForwardController(this, {
|
|
28
51
|
label: true,
|
|
29
52
|
target: () => this.partNamed('nav'),
|
|
@@ -62,6 +85,9 @@ let HelixHeadlessNavigation = class HelixHeadlessNavigation extends HelixElement
|
|
|
62
85
|
</nav>`;
|
|
63
86
|
}
|
|
64
87
|
};
|
|
88
|
+
__decorate([
|
|
89
|
+
property({ type: Boolean })
|
|
90
|
+
], HelixHeadlessNavigation.prototype, "arrows", void 0);
|
|
65
91
|
__decorate([
|
|
66
92
|
property()
|
|
67
93
|
], HelixHeadlessNavigation.prototype, "label", void 0);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a key moves focus in a feed of `count` rows, from the row at `at`, or
|
|
3
|
+
* `undefined` for a key the feed does not take.
|
|
4
|
+
*
|
|
5
|
+
* `onRow` says whether focus is on the row's own box. The arrows and `Home` and
|
|
6
|
+
* `End` move only from there, so a control inside a row keeps its keys.
|
|
7
|
+
* `PageDown` and `PageUp` move from anywhere in a row, which is how the APG feed
|
|
8
|
+
* pattern moves on from inside an article. The answer stays inside the feed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const feedStep: (key: string, at: number, count: number, onRow: boolean) => number | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const feedStep = (key, at, count, onRow) => {
|
|
2
|
+
if (count <= 0) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const anywhere = { PageDown: at + 1, PageUp: at - 1 };
|
|
6
|
+
const fromRow = {
|
|
7
|
+
ArrowDown: at + 1,
|
|
8
|
+
ArrowUp: at - 1,
|
|
9
|
+
End: count - 1,
|
|
10
|
+
Home: 0,
|
|
11
|
+
};
|
|
12
|
+
const to = anywhere[key] ?? (onRow ? fromRow[key] : undefined);
|
|
13
|
+
return to === undefined ? undefined : Math.max(0, Math.min(to, count - 1));
|
|
14
|
+
};
|
|
@@ -79,6 +79,12 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
79
79
|
* Empty is the runtime's own.
|
|
80
80
|
*/
|
|
81
81
|
locale: string;
|
|
82
|
+
/**
|
|
83
|
+
* @internal Set by the feed: this row's place in it, counting from one, read
|
|
84
|
+
* as `aria-posinset`. Zero leaves the place to the browser, which counts the
|
|
85
|
+
* rows it can see.
|
|
86
|
+
*/
|
|
87
|
+
position: number;
|
|
82
88
|
/**
|
|
83
89
|
* Whether the notification has been read.
|
|
84
90
|
*
|
|
@@ -96,6 +102,17 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
96
102
|
*/
|
|
97
103
|
get read(): boolean;
|
|
98
104
|
set read(next: boolean);
|
|
105
|
+
/**
|
|
106
|
+
* @internal Set by the feed: how many rows the feed holds in all, read as
|
|
107
|
+
* `aria-setsize`, and `-1` when nobody knows. Zero leaves it to the browser.
|
|
108
|
+
*/
|
|
109
|
+
setSize: number;
|
|
110
|
+
/**
|
|
111
|
+
* @internal Set by the feed: whether `Tab` stops on this row. Every row is a
|
|
112
|
+
* stop until the feed has chosen one, which is also what a server sends, so
|
|
113
|
+
* the first render on the client is the server's.
|
|
114
|
+
*/
|
|
115
|
+
stop: boolean;
|
|
99
116
|
/**
|
|
100
117
|
* What a screen reader hears before an unread notification's text.
|
|
101
118
|
*
|
|
@@ -17,6 +17,9 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
17
17
|
this.at = '';
|
|
18
18
|
this.defaultRead = false;
|
|
19
19
|
this.locale = '';
|
|
20
|
+
this.position = 0;
|
|
21
|
+
this.setSize = 0;
|
|
22
|
+
this.stop = true;
|
|
20
23
|
this.unreadLabel = 'Unread';
|
|
21
24
|
this.isNotification = true;
|
|
22
25
|
this.readState = undefined;
|
|
@@ -58,6 +61,8 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
58
61
|
willUpdate(changed) {
|
|
59
62
|
super.willUpdate(changed);
|
|
60
63
|
hostAria(this.internals, { role: 'listitem' });
|
|
64
|
+
this.internals.ariaPosInSet = this.position > 0 ? String(this.position) : null;
|
|
65
|
+
this.internals.ariaSetSize = this.setSize === 0 ? null : String(this.setSize);
|
|
61
66
|
this.toggleState('read', this.read);
|
|
62
67
|
}
|
|
63
68
|
updated(changed) {
|
|
@@ -105,10 +110,15 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
105
110
|
></${stamp}>`;
|
|
106
111
|
}
|
|
107
112
|
render() {
|
|
108
|
-
return html `<div
|
|
113
|
+
return html `<div
|
|
114
|
+
part="item"
|
|
115
|
+
role="group"
|
|
116
|
+
aria-labelledby="label"
|
|
117
|
+
tabindex=${this.stop ? 0 : -1}
|
|
118
|
+
>
|
|
109
119
|
<span part="media"><slot name="media"></slot></span>
|
|
110
120
|
<div part="body">
|
|
111
|
-
<div part="label">${this.read ? nothing : html `<span role="img" aria-label=${this.unreadLabel}></span>`}<slot></slot></div>
|
|
121
|
+
<div id="label" part="label">${this.read ? nothing : html `<span role="img" aria-label=${this.unreadLabel}></span>`}<slot></slot></div>
|
|
112
122
|
<slot name="description"></slot>
|
|
113
123
|
<slot name="detail"></slot>
|
|
114
124
|
<span part="actions"><slot name="actions" @slotchange=${this.handleActions}></slot></span>
|
|
@@ -152,9 +162,18 @@ __decorate([
|
|
|
152
162
|
__decorate([
|
|
153
163
|
property()
|
|
154
164
|
], HelixHeadlessNotification.prototype, "locale", void 0);
|
|
165
|
+
__decorate([
|
|
166
|
+
property({ attribute: false })
|
|
167
|
+
], HelixHeadlessNotification.prototype, "position", void 0);
|
|
155
168
|
__decorate([
|
|
156
169
|
property({ type: Boolean })
|
|
157
170
|
], HelixHeadlessNotification.prototype, "read", null);
|
|
171
|
+
__decorate([
|
|
172
|
+
property({ attribute: false })
|
|
173
|
+
], HelixHeadlessNotification.prototype, "setSize", void 0);
|
|
174
|
+
__decorate([
|
|
175
|
+
property({ attribute: false })
|
|
176
|
+
], HelixHeadlessNotification.prototype, "stop", void 0);
|
|
158
177
|
__decorate([
|
|
159
178
|
property({ attribute: 'unread-label' })
|
|
160
179
|
], HelixHeadlessNotification.prototype, "unreadLabel", void 0);
|
|
@@ -21,6 +21,19 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
|
|
|
21
21
|
* the items and written to an attribute, so a stylesheet and a badge can both
|
|
22
22
|
* use it without counting.
|
|
23
23
|
*
|
|
24
|
+
* **The feed is one `Tab` stop for its rows.** Each row takes focus itself and
|
|
25
|
+
* is read by its label. `ArrowDown` and `ArrowUp` move to the next and the
|
|
26
|
+
* previous row, `Home` and `End` to the first and the last, and `PageDown` and
|
|
27
|
+
* `PageUp` move a row on or back from anywhere inside one, as in the APG feed
|
|
28
|
+
* pattern. The controls inside a row stay in the `Tab` order. Set `total` when
|
|
29
|
+
* the feed holds one page of a longer list, and each row says its place in the
|
|
30
|
+
* whole of it.
|
|
31
|
+
*
|
|
32
|
+
* **`load-more` adds a button after the list** that sends `helix-load-more`,
|
|
33
|
+
* for a feed that holds one page of a longer list. Append the next page, set
|
|
34
|
+
* `loading` while it is on its way, and take `load-more` off once there is
|
|
35
|
+
* nothing more. The button is hidden while the feed is loading.
|
|
36
|
+
*
|
|
24
37
|
* @since 0.1.0
|
|
25
38
|
* @status stable
|
|
26
39
|
* @category feedback
|
|
@@ -32,6 +45,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
|
|
|
32
45
|
* @slot header - Above the list, for a title and the mark-all control.
|
|
33
46
|
*
|
|
34
47
|
* @event {HelixDismissEvent} helix-dismiss - Fired when the user dismisses a row. The row dispatches it, and you can listen for it on the feed. Cancelable: cancel it to keep the row.
|
|
48
|
+
* @event {HelixLoadMoreEvent} helix-load-more - Fired when a reader presses the `load-more` button, asking for the next page of the feed.
|
|
35
49
|
* @event {HelixReadEvent} helix-read - Fired by an item when it is marked read or unread, and by the feed when everything is marked read. Cancelable.
|
|
36
50
|
*
|
|
37
51
|
* @csspart empty - Shown when there is nothing in the feed.
|
|
@@ -41,6 +55,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
|
|
|
41
55
|
* @csspart live - A copy of the empty and loading messages, in a live region that screen readers announce when they change. Visually hidden in the styled tier.
|
|
42
56
|
* @csspart loading - Shown while the feed is loading.
|
|
43
57
|
* @csspart mark-all - The control that marks everything read.
|
|
58
|
+
* @csspart more - The button after the list that asks for more, while `load-more` is set and the feed is not loading.
|
|
44
59
|
*
|
|
45
60
|
* @cssstate media - While a notification in the list holds something in its media slot, so every row keeps that column.
|
|
46
61
|
*/
|
|
@@ -59,6 +74,15 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
|
|
|
59
74
|
* The feed's name, read by screen readers.
|
|
60
75
|
*/
|
|
61
76
|
label: string;
|
|
77
|
+
/**
|
|
78
|
+
* Whether to draw a button after the list that asks for the next page, by
|
|
79
|
+
* sending `helix-load-more`. Take it off once there is nothing more.
|
|
80
|
+
*/
|
|
81
|
+
loadMore: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The text of the `load-more` button.
|
|
84
|
+
*/
|
|
85
|
+
loadMoreLabel: string;
|
|
62
86
|
/**
|
|
63
87
|
* Whether to draw a control that marks everything read.
|
|
64
88
|
*/
|
|
@@ -67,6 +91,14 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
|
|
|
67
91
|
* Label for the mark-all control.
|
|
68
92
|
*/
|
|
69
93
|
markAllLabel: string;
|
|
94
|
+
/**
|
|
95
|
+
* How many notifications there are in all, where the feed holds only some of
|
|
96
|
+
* them, read as each row's `aria-setsize`. `-1` says the count is unknown.
|
|
97
|
+
*
|
|
98
|
+
* Unset, the browser counts the rows it can see, which is right for a feed
|
|
99
|
+
* that holds everything.
|
|
100
|
+
*/
|
|
101
|
+
total?: number;
|
|
70
102
|
/**
|
|
71
103
|
* How many items are unread, counted from the items themselves.
|
|
72
104
|
*
|
|
@@ -158,6 +190,32 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
|
|
|
158
190
|
*/
|
|
159
191
|
private readonly handleRead;
|
|
160
192
|
private assign;
|
|
193
|
+
/** The row `Tab` stops on, once a reader has been to one. */
|
|
194
|
+
private stopRow;
|
|
195
|
+
/**
|
|
196
|
+
* The rows a reader can reach with the keys: every row in the feed, in order,
|
|
197
|
+
* less the ones a folded group renders nowhere.
|
|
198
|
+
*/
|
|
199
|
+
private get reachable();
|
|
200
|
+
/** The row whose own box is the element focus is on, if it is one. */
|
|
201
|
+
private rowFocused;
|
|
202
|
+
/** A row taking focus becomes the feed's stop, so `Tab` comes back to it. */
|
|
203
|
+
private readonly handleFocusIn;
|
|
204
|
+
/**
|
|
205
|
+
* Moves between rows.
|
|
206
|
+
*
|
|
207
|
+
* The arrows and `Home` and `End` only from a row's own box, so a control in
|
|
208
|
+
* a row keeps its keys. `PageDown` and `PageUp` from anywhere in a row, which
|
|
209
|
+
* is how the APG feed pattern moves on from inside an article.
|
|
210
|
+
*/
|
|
211
|
+
private readonly handleKeydown;
|
|
212
|
+
/**
|
|
213
|
+
* Tells each row whether it is the stop, and where it sits in the feed.
|
|
214
|
+
*
|
|
215
|
+
* Through each row's own properties, written once it has hydrated, so a
|
|
216
|
+
* server's markup stays the server's: every row is a stop until this runs.
|
|
217
|
+
*/
|
|
218
|
+
private rove;
|
|
161
219
|
connectedCallback(): void;
|
|
162
220
|
disconnectedCallback(): void;
|
|
163
221
|
protected willUpdate(changed: PropertyValues): void;
|
|
@@ -202,6 +260,8 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
|
|
|
202
260
|
private markEmpty;
|
|
203
261
|
protected updated(changed: PropertyValues): void;
|
|
204
262
|
protected render(): TemplateResult;
|
|
263
|
+
/** Asks for the next page, for the `load-more` button. */
|
|
264
|
+
private readonly askForMore;
|
|
205
265
|
/**
|
|
206
266
|
* Marks everything read, and keeps focus in the feed.
|
|
207
267
|
*
|
|
@@ -4,13 +4,16 @@ 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 { customElement, focusableElements, HelixElement, HelixReadEvent, hostAria, MixinCollection, markRegion, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, deepContains, focusableElements, HelixElement, HelixLoadMoreEvent, HelixReadEvent, hostAria, MixinCollection, markRegion, rovingStop, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { feedStep } from '../feed-keys.js';
|
|
10
11
|
let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinCollection(HelixElement) {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
13
14
|
this.label = '';
|
|
15
|
+
this.loadMore = false;
|
|
16
|
+
this.loadMoreLabel = 'Load more';
|
|
14
17
|
this.markAll = false;
|
|
15
18
|
this.markAllLabel = 'Mark all as read';
|
|
16
19
|
this.handleSlotChange = () => {
|
|
@@ -20,6 +23,39 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
|
|
|
20
23
|
this.handleRead = () => {
|
|
21
24
|
queueMicrotask(() => this.requestUpdate());
|
|
22
25
|
};
|
|
26
|
+
this.handleFocusIn = (event) => {
|
|
27
|
+
const row = this.rowFocused(event.composedPath()[0]);
|
|
28
|
+
if (row && row !== this.stopRow) {
|
|
29
|
+
this.stopRow = row;
|
|
30
|
+
this.rove();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
this.handleKeydown = (event) => {
|
|
34
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const rows = this.reachable;
|
|
38
|
+
const from = event.composedPath()[0];
|
|
39
|
+
const own = this.rowFocused(from);
|
|
40
|
+
const inside = own ?? rows.find((row) => from instanceof Node && deepContains(row, from));
|
|
41
|
+
if (!inside) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const to = feedStep(event.key, rows.indexOf(inside), rows.length, own !== undefined);
|
|
45
|
+
if (to === undefined) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
const next = rows[to];
|
|
50
|
+
if (next) {
|
|
51
|
+
this.stopRow = next;
|
|
52
|
+
this.rove();
|
|
53
|
+
rowBox(next)?.focus();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
this.askForMore = () => {
|
|
57
|
+
this.dispatchEvent(new HelixLoadMoreEvent());
|
|
58
|
+
};
|
|
23
59
|
this.handleMarkAll = (event) => {
|
|
24
60
|
const button = event.currentTarget;
|
|
25
61
|
const focused = this.shadowRoot?.activeElement === button;
|
|
@@ -106,13 +142,36 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
|
|
|
106
142
|
slot?.assign?.(...[...this.children].filter((child) => child.getAttribute('slot') === name));
|
|
107
143
|
}
|
|
108
144
|
}
|
|
145
|
+
get reachable() {
|
|
146
|
+
return this.items.filter((row) => !('assignedSlot' in row) || row.assignedSlot !== null);
|
|
147
|
+
}
|
|
148
|
+
rowFocused(target) {
|
|
149
|
+
return this.reachable.find((row) => rowBox(row) === target);
|
|
150
|
+
}
|
|
151
|
+
rove() {
|
|
152
|
+
const rows = this.reachable;
|
|
153
|
+
const stop = rovingStop(rows, this.stopRow);
|
|
154
|
+
const direct = this.rows;
|
|
155
|
+
for (const row of this.items) {
|
|
156
|
+
const position = direct.indexOf(row) + 1;
|
|
157
|
+
whenHydrated(row, () => {
|
|
158
|
+
row.stop = row === stop;
|
|
159
|
+
row.position = this.total === undefined ? 0 : position;
|
|
160
|
+
row.setSize = this.total ?? 0;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
109
164
|
connectedCallback() {
|
|
110
165
|
super.connectedCallback();
|
|
111
166
|
this.addEventListener('helix-read', this.handleRead);
|
|
167
|
+
this.addEventListener('focusin', this.handleFocusIn);
|
|
168
|
+
this.addEventListener('keydown', this.handleKeydown);
|
|
112
169
|
}
|
|
113
170
|
disconnectedCallback() {
|
|
114
171
|
super.disconnectedCallback();
|
|
115
172
|
this.removeEventListener('helix-read', this.handleRead);
|
|
173
|
+
this.removeEventListener('focusin', this.handleFocusIn);
|
|
174
|
+
this.removeEventListener('keydown', this.handleKeydown);
|
|
116
175
|
}
|
|
117
176
|
willUpdate(changed) {
|
|
118
177
|
super.willUpdate(changed);
|
|
@@ -130,6 +189,7 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
|
|
|
130
189
|
this.assign();
|
|
131
190
|
this.markEmpty();
|
|
132
191
|
this.markMedia();
|
|
192
|
+
this.rove();
|
|
133
193
|
const unread = this.unread > 0 ? String(this.unread) : null;
|
|
134
194
|
if (this.getAttribute('unread') !== unread) {
|
|
135
195
|
if (unread === null) {
|
|
@@ -155,19 +215,34 @@ let HelixHeadlessNotifications = class HelixHeadlessNotifications extends MixinC
|
|
|
155
215
|
${this.renderStatus(hasItems)}
|
|
156
216
|
</div>
|
|
157
217
|
${this.renderLive(hasItems)}
|
|
218
|
+
${this.loadMore && !this.loading
|
|
219
|
+
? html `<button part="more" type="button" @click=${this.askForMore}>
|
|
220
|
+
${this.loadMoreLabel}
|
|
221
|
+
</button>`
|
|
222
|
+
: nothing}
|
|
158
223
|
<div part="footer"><slot name="footer"></slot></div>`;
|
|
159
224
|
}
|
|
160
225
|
};
|
|
161
226
|
__decorate([
|
|
162
227
|
property()
|
|
163
228
|
], HelixHeadlessNotifications.prototype, "label", void 0);
|
|
229
|
+
__decorate([
|
|
230
|
+
property({ attribute: 'load-more', type: Boolean })
|
|
231
|
+
], HelixHeadlessNotifications.prototype, "loadMore", void 0);
|
|
232
|
+
__decorate([
|
|
233
|
+
property({ attribute: 'load-more-label' })
|
|
234
|
+
], HelixHeadlessNotifications.prototype, "loadMoreLabel", void 0);
|
|
164
235
|
__decorate([
|
|
165
236
|
property({ attribute: 'mark-all', type: Boolean })
|
|
166
237
|
], HelixHeadlessNotifications.prototype, "markAll", void 0);
|
|
167
238
|
__decorate([
|
|
168
239
|
property({ attribute: 'mark-all-label' })
|
|
169
240
|
], HelixHeadlessNotifications.prototype, "markAllLabel", void 0);
|
|
241
|
+
__decorate([
|
|
242
|
+
property({ type: Number })
|
|
243
|
+
], HelixHeadlessNotifications.prototype, "total", void 0);
|
|
170
244
|
HelixHeadlessNotifications = __decorate([
|
|
171
245
|
customElement('helix-headless-notifications')
|
|
172
246
|
], HelixHeadlessNotifications);
|
|
173
247
|
export { HelixHeadlessNotifications };
|
|
248
|
+
const rowBox = (row) => row.shadowRoot?.querySelector('[part="item"]') ?? null;
|
|
@@ -18,6 +18,17 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
|
|
|
18
18
|
*
|
|
19
19
|
* `inputmode="decimal"` keeps the numeric keypad on phones.
|
|
20
20
|
*
|
|
21
|
+
* `format-options` shows the number with `Intl.NumberFormat` in `locale`, as a
|
|
22
|
+
* currency, a percent or a unit, while the field is not being edited. Focus
|
|
23
|
+
* shows the number itself, and a typed `$1,200` or `25%` is read back with the
|
|
24
|
+
* same options. The value stays the plain number: `1200`, or `0.25` for 25%.
|
|
25
|
+
* The locale's group separators are read in every field, so `1,234` is a
|
|
26
|
+
* number in `en-US`.
|
|
27
|
+
*
|
|
28
|
+
* On iOS and iPadOS the field is a text field described as a number field,
|
|
29
|
+
* with no `role="spinbutton"`, which VoiceOver reads as a slider you cannot
|
|
30
|
+
* type in. React Aria's NumberField does the same.
|
|
31
|
+
*
|
|
21
32
|
* Validity follows the platform's rules: `min`, `max`, and `step` are checked
|
|
22
33
|
* and reported through the host's own `setValidity`, under the platform's flag
|
|
23
34
|
* names.
|
|
@@ -80,6 +91,16 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
|
|
|
80
91
|
* The name of the step down button, read by screen readers.
|
|
81
92
|
*/
|
|
82
93
|
decrementLabel: string;
|
|
94
|
+
/**
|
|
95
|
+
* `Intl.NumberFormat` options the number is shown with while the field is
|
|
96
|
+
* not being edited: a currency, a percent, a unit, or a number of decimals.
|
|
97
|
+
* Written as JSON in the attribute.
|
|
98
|
+
*
|
|
99
|
+
* ```html
|
|
100
|
+
* <helix-number-input format-options='{"style":"currency","currency":"EUR"}'></helix-number-input>
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
83
104
|
/**
|
|
84
105
|
* The name of the step up button, read by screen readers.
|
|
85
106
|
*/
|
|
@@ -133,6 +154,11 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
|
|
|
133
154
|
* Prevents editing. The value is still submitted with the form.
|
|
134
155
|
*/
|
|
135
156
|
readOnly: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* What the field is described as on iOS and iPadOS, where it carries no
|
|
159
|
+
* spinbutton role. See the class description.
|
|
160
|
+
*/
|
|
161
|
+
roleDescription: string;
|
|
136
162
|
/**
|
|
137
163
|
* Makes the label a grip that changes the value when it is dragged.
|
|
138
164
|
*
|
|
@@ -163,6 +189,27 @@ export declare class HelixHeadlessNumberInput extends HelixHeadlessNumberInput_b
|
|
|
163
189
|
* number alone.
|
|
164
190
|
*/
|
|
165
191
|
valueText: string;
|
|
192
|
+
/**
|
|
193
|
+
* Whether the mouse wheel steps the number while the field has focus, as
|
|
194
|
+
* React Aria's NumberField does unless told not to. Off, the wheel scrolls
|
|
195
|
+
* the page, which is what a reader passing over a form expects.
|
|
196
|
+
*/
|
|
197
|
+
wheel: boolean;
|
|
198
|
+
/** Whether the field has focus, so it shows the number itself and not its format. */
|
|
199
|
+
private editing;
|
|
200
|
+
/**
|
|
201
|
+
* What the field shows: the value as written while it is edited, and with
|
|
202
|
+
* `format-options` once it is not.
|
|
203
|
+
*/
|
|
204
|
+
private get shown();
|
|
205
|
+
/** Steps on the wheel, where asked, while the field has focus. */
|
|
206
|
+
private readonly handleWheel;
|
|
207
|
+
private readonly handleFocus;
|
|
208
|
+
/**
|
|
209
|
+
* Whether this is an iPhone or an iPad, read once on the client. A server
|
|
210
|
+
* has no navigator and renders the spinbutton.
|
|
211
|
+
*/
|
|
212
|
+
private readonly touchApple;
|
|
166
213
|
private get control();
|
|
167
214
|
/** The value as a number, or `null` where the field holds nothing usable. */
|
|
168
215
|
private get numeric();
|
|
@@ -4,10 +4,10 @@ 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, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, appleTouch, valueAsNumber as asNumber, customElement, fillTemplate, formatNumber, numberAsValue as fromNumber, HelixFormElement, holdCaret, MixinControllerValue, MixinField, parseNumber, restoreValue, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { minus, plus } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
|
-
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
12
|
const SCRUB_PITCH = 4;
|
|
13
13
|
const SCRUB_FINE = 16;
|
|
@@ -24,9 +24,28 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
24
24
|
this.locale = '';
|
|
25
25
|
this.placeholder = '';
|
|
26
26
|
this.readOnly = false;
|
|
27
|
+
this.roleDescription = 'number field';
|
|
27
28
|
this.scrub = false;
|
|
28
29
|
this.step = 1;
|
|
29
30
|
this.valueText = '';
|
|
31
|
+
this.wheel = false;
|
|
32
|
+
this.editing = false;
|
|
33
|
+
this.handleWheel = (event) => {
|
|
34
|
+
if (!this.wheel || !this.editing || event.deltaY === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
if (event.deltaY < 0) {
|
|
39
|
+
this.stepUp();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.stepDown();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
this.handleFocus = () => {
|
|
46
|
+
this.editing = true;
|
|
47
|
+
};
|
|
48
|
+
this.touchApple = appleTouch(typeof navigator === 'undefined' ? undefined : navigator);
|
|
30
49
|
this.scrubFrom = 0;
|
|
31
50
|
this.scrubValue = 0;
|
|
32
51
|
this.scrubStart = '';
|
|
@@ -77,6 +96,7 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
77
96
|
restoreValue(control, this.value);
|
|
78
97
|
};
|
|
79
98
|
this.handleBlur = () => {
|
|
99
|
+
this.editing = false;
|
|
80
100
|
if (this.settle()) {
|
|
81
101
|
this.announceChange();
|
|
82
102
|
}
|
|
@@ -122,6 +142,13 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
122
142
|
}
|
|
123
143
|
};
|
|
124
144
|
}
|
|
145
|
+
get shown() {
|
|
146
|
+
const parsed = this.numeric;
|
|
147
|
+
if (this.editing || this.formatOptions === undefined || parsed === null) {
|
|
148
|
+
return this.value;
|
|
149
|
+
}
|
|
150
|
+
return formatNumber(parsed, this.locale, this.formatOptions);
|
|
151
|
+
}
|
|
125
152
|
get control() {
|
|
126
153
|
return this.partNamed('control');
|
|
127
154
|
}
|
|
@@ -130,11 +157,7 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
130
157
|
if (text === '') {
|
|
131
158
|
return null;
|
|
132
159
|
}
|
|
133
|
-
|
|
134
|
-
? text.replace(',', '.')
|
|
135
|
-
: text;
|
|
136
|
-
const parsed = read.includes(',') ? Number.NaN : Number(read);
|
|
137
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
160
|
+
return parseNumber(text, this.locale, this.formatOptions);
|
|
138
161
|
}
|
|
139
162
|
onStep(value) {
|
|
140
163
|
const step = Math.abs(this.step) || 1;
|
|
@@ -291,9 +314,10 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
291
314
|
<input
|
|
292
315
|
part="control"
|
|
293
316
|
id=${this.controlId}
|
|
294
|
-
.value=${this.
|
|
317
|
+
.value=${this.shown}
|
|
295
318
|
type="text"
|
|
296
|
-
role
|
|
319
|
+
role=${this.touchApple ? nothing : 'spinbutton'}
|
|
320
|
+
aria-roledescription=${this.touchApple ? this.roleDescription : nothing}
|
|
297
321
|
inputmode="decimal"
|
|
298
322
|
autocomplete=${this.autocomplete || nothing}
|
|
299
323
|
name=${this.name || nothing}
|
|
@@ -303,17 +327,27 @@ let HelixHeadlessNumberInput = class HelixHeadlessNumberInput extends MixinContr
|
|
|
303
327
|
aria-valuemin=${this.min ?? nothing}
|
|
304
328
|
aria-valuemax=${this.max ?? nothing}
|
|
305
329
|
aria-valuenow=${parsed ?? nothing}
|
|
306
|
-
aria-valuetext=${
|
|
307
|
-
?
|
|
308
|
-
:
|
|
330
|
+
aria-valuetext=${parsed === null
|
|
331
|
+
? nothing
|
|
332
|
+
: this.valueText
|
|
333
|
+
? fillTemplate(this.valueText, {
|
|
334
|
+
value: this.formatOptions
|
|
335
|
+
? formatNumber(parsed, this.locale, this.formatOptions)
|
|
336
|
+
: this.value,
|
|
337
|
+
})
|
|
338
|
+
: this.formatOptions
|
|
339
|
+
? formatNumber(parsed, this.locale, this.formatOptions)
|
|
340
|
+
: nothing}
|
|
309
341
|
?disabled=${this.isDisabled}
|
|
310
342
|
?readonly=${this.readOnly}
|
|
311
343
|
?required=${this.required}
|
|
312
344
|
@blur=${this.handleBlur}
|
|
313
345
|
@change=${this.handleChange}
|
|
346
|
+
@focus=${this.handleFocus}
|
|
314
347
|
@input=${this.handleInput}
|
|
315
348
|
@keydown=${this.handleKeydown}
|
|
316
349
|
@keypress=${this.handleKeypress}
|
|
350
|
+
@wheel=${this.handleWheel}
|
|
317
351
|
/>
|
|
318
352
|
<slot name="end"></slot>
|
|
319
353
|
<button
|
|
@@ -347,6 +381,9 @@ __decorate([
|
|
|
347
381
|
__decorate([
|
|
348
382
|
property({ attribute: 'decrement-label' })
|
|
349
383
|
], HelixHeadlessNumberInput.prototype, "decrementLabel", void 0);
|
|
384
|
+
__decorate([
|
|
385
|
+
property({ attribute: 'format-options', type: Object })
|
|
386
|
+
], HelixHeadlessNumberInput.prototype, "formatOptions", void 0);
|
|
350
387
|
__decorate([
|
|
351
388
|
property({ attribute: 'increment-label' })
|
|
352
389
|
], HelixHeadlessNumberInput.prototype, "incrementLabel", void 0);
|
|
@@ -374,6 +411,9 @@ __decorate([
|
|
|
374
411
|
__decorate([
|
|
375
412
|
property({ attribute: 'readonly', reflect: true, type: Boolean })
|
|
376
413
|
], HelixHeadlessNumberInput.prototype, "readOnly", void 0);
|
|
414
|
+
__decorate([
|
|
415
|
+
property({ attribute: 'role-description' })
|
|
416
|
+
], HelixHeadlessNumberInput.prototype, "roleDescription", void 0);
|
|
377
417
|
__decorate([
|
|
378
418
|
property({ reflect: true, type: Boolean })
|
|
379
419
|
], HelixHeadlessNumberInput.prototype, "scrub", void 0);
|
|
@@ -386,20 +426,16 @@ __decorate([
|
|
|
386
426
|
__decorate([
|
|
387
427
|
property({ attribute: 'value-text' })
|
|
388
428
|
], HelixHeadlessNumberInput.prototype, "valueText", void 0);
|
|
429
|
+
__decorate([
|
|
430
|
+
property({ type: Boolean })
|
|
431
|
+
], HelixHeadlessNumberInput.prototype, "wheel", void 0);
|
|
432
|
+
__decorate([
|
|
433
|
+
state()
|
|
434
|
+
], HelixHeadlessNumberInput.prototype, "editing", void 0);
|
|
389
435
|
HelixHeadlessNumberInput = __decorate([
|
|
390
436
|
customElement('helix-headless-number-input')
|
|
391
437
|
], HelixHeadlessNumberInput);
|
|
392
438
|
export { HelixHeadlessNumberInput };
|
|
393
|
-
const commas = new Map();
|
|
394
|
-
const decimalComma = (locale) => {
|
|
395
|
-
let comma = commas.get(locale);
|
|
396
|
-
if (comma === undefined) {
|
|
397
|
-
const parts = new Intl.NumberFormat(locale || undefined).formatToParts(1.5);
|
|
398
|
-
comma = parts.find((part) => part.type === 'decimal')?.value === ',';
|
|
399
|
-
commas.set(locale, comma);
|
|
400
|
-
}
|
|
401
|
-
return comma;
|
|
402
|
-
};
|
|
403
439
|
const decimals = (value) => {
|
|
404
440
|
const text = String(value);
|
|
405
441
|
const exponent = /e-(\d+)$/.exec(text);
|