@fusion.dev/helix-headless 0.142.1 → 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 +25381 -1202
- package/dist/accordion/item/accordion-item.d.ts +38 -2
- package/dist/accordion/item/accordion-item.js +38 -6
- package/dist/accordion/root/accordion-root.d.ts +23 -1
- package/dist/accordion/root/accordion-root.js +39 -3
- package/dist/announcement/root/announcement-root.d.ts +11 -7
- package/dist/announcement/root/announcement-root.js +1 -1
- package/dist/app-header/root/app-header-root.d.ts +21 -0
- package/dist/app-header/root/app-header-root.js +31 -5
- package/dist/area-chart/root/area-chart-root.d.ts +12 -4
- package/dist/area-chart/root/area-chart-root.js +39 -11
- package/dist/avatar/group/avatar-group.d.ts +24 -3
- package/dist/avatar/group/avatar-group.js +34 -2
- package/dist/avatar/root/avatar-root.d.ts +17 -1
- package/dist/avatar/root/avatar-root.js +31 -10
- package/dist/badge/root/badge-root.d.ts +12 -0
- package/dist/badge/root/badge-root.js +1 -1
- package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
- package/dist/bar-chart/root/bar-chart-root.js +58 -21
- package/dist/blockquote/root/blockquote-root.d.ts +12 -7
- package/dist/blockquote/root/blockquote-root.js +6 -4
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
- package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
- package/dist/button/root/button-root.d.ts +84 -1
- package/dist/button/root/button-root.js +81 -3
- package/dist/callout/root/callout-root.d.ts +32 -6
- package/dist/callout/root/callout-root.js +26 -7
- package/dist/canvas/root/canvas-root.d.ts +1 -0
- package/dist/canvas/root/canvas-root.js +10 -1
- 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 +47 -5
- package/dist/carousel/root/carousel-root.js +53 -21
- package/dist/chat/composer/chat-composer.d.ts +13 -11
- package/dist/chat/composer/chat-composer.js +6 -12
- package/dist/chat/message/chat-message.js +8 -1
- package/dist/chat/root/chat-root.d.ts +1 -0
- package/dist/chat/root/chat-root.js +2 -1
- package/dist/checkbox/root/checkbox-root.d.ts +7 -0
- package/dist/checkbox/root/checkbox-root.js +9 -3
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
- package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
- package/dist/code/root/code-root.d.ts +1 -1
- 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 +49 -0
- package/dist/code-block/root/code-block-root.js +44 -3
- package/dist/color-input/root/color-input-root.d.ts +60 -2
- package/dist/color-input/root/color-input-root.js +55 -7
- 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 +118 -12
- package/dist/color-picker/root/color-picker-root.js +183 -13
- package/dist/combobox/root/combobox-root.d.ts +126 -3
- package/dist/combobox/root/combobox-root.js +224 -21
- package/dist/command/root/command-root.d.ts +47 -10
- package/dist/command/root/command-root.js +45 -4
- 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 +48 -7
- package/dist/copy-button/root/copy-button-root.js +54 -8
- package/dist/data-table/root/data-table-root.d.ts +164 -10
- package/dist/data-table/root/data-table-root.js +488 -57
- package/dist/date-input/root/date-input-root.d.ts +123 -9
- package/dist/date-input/root/date-input-root.js +147 -45
- package/dist/date-picker/root/date-picker-root.d.ts +229 -18
- package/dist/date-picker/root/date-picker-root.js +342 -46
- package/dist/dialog/description.d.ts +9 -0
- package/dist/dialog/description.js +14 -0
- package/dist/dialog/root/dialog-root.d.ts +101 -10
- package/dist/dialog/root/dialog-root.js +83 -4
- package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
- package/dist/donut-chart/root/donut-chart-root.js +48 -13
- package/dist/drawer/root/drawer-root.d.ts +93 -7
- package/dist/drawer/root/drawer-root.js +126 -5
- 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 +16 -13
- package/dist/drilldown/item/drilldown-item.js +6 -2
- package/dist/drilldown/root/drilldown-root.d.ts +21 -5
- package/dist/drilldown/root/drilldown-root.js +26 -10
- package/dist/element-events.generated.js +42 -13
- package/dist/empty/root/empty-root.d.ts +1 -1
- package/dist/file-input/root/file-input-root.d.ts +65 -1
- package/dist/file-input/root/file-input-root.js +76 -12
- 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 +9 -0
- package/dist/filter-input/menu/filter-menu.js +8 -0
- package/dist/filter-input/root/filter-input-root.d.ts +43 -6
- package/dist/filter-input/root/filter-input-root.js +89 -10
- 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 +62 -3
- package/dist/format-date/root/format-date-root.js +78 -37
- package/dist/graph/root/graph-root.d.ts +24 -3
- package/dist/graph/root/graph-root.js +66 -12
- package/dist/heatmap/root/buckets.d.ts +2 -1
- package/dist/heatmap/root/buckets.js +5 -1
- package/dist/heatmap/root/heatmap-root.d.ts +47 -9
- package/dist/heatmap/root/heatmap-root.js +46 -18
- 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 +53 -6
- package/dist/icon/root/icon-root.js +72 -4
- 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/icon-button/root/icon-button-root.d.ts +1 -1
- package/dist/image/root/image-root.d.ts +23 -2
- package/dist/image/root/image-root.js +23 -4
- package/dist/input/root/input-root.d.ts +62 -9
- package/dist/input/root/input-root.js +61 -5
- package/dist/kbd/root/kbd-root.d.ts +26 -11
- package/dist/kbd/root/kbd-root.js +35 -38
- 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 +63 -0
- package/dist/legend/root/legend-root.js +61 -7
- package/dist/lightbox/item/lightbox-item.d.ts +17 -1
- package/dist/lightbox/item/lightbox-item.js +10 -1
- package/dist/lightbox/root/lightbox-root.d.ts +77 -6
- package/dist/lightbox/root/lightbox-root.js +116 -5
- package/dist/line-chart/root/line-chart-root.d.ts +29 -4
- package/dist/line-chart/root/line-chart-root.js +58 -17
- package/dist/list/item/list-item.d.ts +11 -5
- package/dist/list/item/list-item.js +5 -6
- package/dist/listbox/range.d.ts +17 -0
- package/dist/listbox/range.js +5 -0
- package/dist/listbox/root/listbox-root.d.ts +184 -4
- package/dist/listbox/root/listbox-root.js +229 -23
- 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 +19 -4
- package/dist/map/root/map-root.js +36 -7
- package/dist/megamenu/root/megamenu-root.d.ts +10 -1
- package/dist/megamenu/root/megamenu-root.js +10 -1
- package/dist/menu/item/menu-item.d.ts +41 -12
- package/dist/menu/item/menu-item.js +46 -3
- package/dist/menu/root/menu-root.d.ts +32 -2
- package/dist/menu/root/menu-root.js +48 -8
- package/dist/meter/root/meter-root.d.ts +13 -0
- package/dist/meter/root/meter-root.js +24 -5
- package/dist/navigation/root/navigation-root.d.ts +10 -2
- package/dist/navigation/root/navigation-root.js +13 -2
- package/dist/notifications/feed-keys.d.ts +10 -0
- package/dist/notifications/feed-keys.js +14 -0
- package/dist/notifications/group/notification-group.d.ts +1 -0
- package/dist/notifications/notification/notification.d.ts +29 -0
- package/dist/notifications/notification/notification.js +51 -3
- package/dist/notifications/root/notifications-root.d.ts +49 -0
- package/dist/notifications/root/notifications-root.js +76 -2
- package/dist/number-input/root/number-input-root.d.ts +109 -7
- package/dist/number-input/root/number-input-root.js +109 -21
- package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
- package/dist/object-inspector/root/object-inspector-root.js +92 -12
- package/dist/option/root/option-root.d.ts +58 -6
- package/dist/option/root/option-root.js +35 -5
- package/dist/outline/root/outline-root.d.ts +13 -2
- package/dist/outline/root/outline-root.js +4 -2
- package/dist/pagination/root/pagination-root.d.ts +19 -1
- package/dist/pagination/root/pagination-root.js +49 -9
- package/dist/pagination/size/pagination-size.d.ts +13 -3
- package/dist/pagination/size/pagination-size.js +7 -0
- 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 +37 -0
- package/dist/pin-input/root/pin-input-root.js +48 -7
- package/dist/popover/root/popover-root.d.ts +78 -3
- package/dist/popover/root/popover-root.js +74 -5
- package/dist/progress/root/progress-root.d.ts +23 -1
- package/dist/progress/root/progress-root.js +40 -10
- 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 +45 -0
- package/dist/qr-code/root/qr-code-root.js +78 -3
- package/dist/radio/group/radio-group.d.ts +64 -0
- package/dist/radio/group/radio-group.js +66 -12
- package/dist/radio/root/radio-root.d.ts +8 -5
- package/dist/radio/root/radio-root.js +1 -2
- package/dist/rating/root/rating-root.d.ts +49 -1
- package/dist/rating/root/rating-root.js +69 -10
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
- package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
- package/dist/select/root/select-root.d.ts +93 -3
- package/dist/select/root/select-root.js +111 -13
- package/dist/skeleton/root/skeleton-root.d.ts +39 -3
- package/dist/skeleton/root/skeleton-root.js +33 -4
- package/dist/skip-link/root/skip-link-root.d.ts +10 -0
- package/dist/skip-link/root/skip-link-root.js +15 -1
- package/dist/slider/root/slider-root.d.ts +59 -2
- package/dist/slider/root/slider-root.js +82 -8
- package/dist/sparkline/root/sparkline-root.d.ts +9 -10
- package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
- package/dist/speed-dial/item/speed-dial-item.js +7 -2
- package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
- package/dist/speed-dial/root/speed-dial-root.js +19 -5
- package/dist/splitter/root/splitter-root.d.ts +28 -6
- package/dist/splitter/root/splitter-root.js +35 -1
- package/dist/stat/root/stat-root.d.ts +9 -1
- package/dist/stat/root/stat-root.js +15 -2
- package/dist/steps/root/steps-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 +14 -2
- package/dist/switch/root/switch-root.js +7 -1
- package/dist/table/cell/table-cell.d.ts +24 -4
- package/dist/table/cell/table-cell.js +21 -1
- package/dist/table/column/table-column.d.ts +32 -6
- package/dist/table/column/table-column.js +36 -6
- package/dist/table/root/table-root.d.ts +94 -3
- package/dist/table/root/table-root.js +128 -5
- package/dist/table/row/table-row.d.ts +7 -0
- package/dist/table/row/table-row.js +9 -1
- package/dist/tabs/root/tabs-root.d.ts +6 -1
- package/dist/tabs/root/tabs-root.js +6 -2
- 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 +83 -1
- package/dist/tag/root/tag-root.js +87 -17
- 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 +13 -0
- package/dist/textarea/root/textarea-root.js +10 -2
- package/dist/time-input/root/time-input-root.d.ts +71 -6
- package/dist/time-input/root/time-input-root.js +110 -44
- package/dist/time-picker/root/time-picker-root.d.ts +155 -26
- package/dist/time-picker/root/time-picker-root.js +192 -63
- 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 +105 -8
- package/dist/toast/root/toast-root.js +209 -4
- package/dist/toggle/group/toggle-group.d.ts +95 -16
- package/dist/toggle/group/toggle-group.js +99 -13
- package/dist/toggle/root/toggle-root.d.ts +26 -11
- package/dist/toggle/root/toggle-root.js +14 -6
- package/dist/toolbar/root/toolbar-root.d.ts +55 -15
- package/dist/toolbar/root/toolbar-root.js +99 -16
- 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 +73 -15
- package/dist/tooltip/root/tooltip-root.js +96 -7
- package/dist/tour/naming.d.ts +30 -0
- package/dist/tour/naming.js +10 -0
- package/dist/tour/root/tour-root.d.ts +67 -3
- package/dist/tour/root/tour-root.js +105 -17
- package/dist/tour/stop/tour-stop.d.ts +65 -3
- package/dist/tour/stop/tour-stop.js +61 -11
- package/dist/tour/wait.d.ts +11 -0
- package/dist/tour/wait.js +19 -0
- package/dist/tree/item/tree-item.d.ts +21 -5
- package/dist/tree/item/tree-item.js +14 -4
- package/dist/tree/root/tree-root.d.ts +29 -5
- package/dist/tree/root/tree-root.js +64 -8
- package/dist/trend/root/trend-root.d.ts +36 -1
- package/dist/trend/root/trend-root.js +41 -15
- package/dist/video/root/keys.d.ts +5 -0
- package/dist/video/root/keys.js +13 -1
- package/dist/video/root/video-root.d.ts +19 -0
- package/dist/video/root/video-root.js +48 -4
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
- package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
- package/dist/video-playlist/root/video-playlist-root.js +6 -3
- package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
- package/dist/virtual-list/root/virtual-list-root.js +103 -21
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +9140 -1149
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +2089 -267
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandController, HelixElement, type Placement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, CommandController, HelixElement, type Placement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
|
|
4
4
|
/**
|
|
@@ -21,6 +21,29 @@ declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusio
|
|
|
21
21
|
* commands work as they do on `Dialog` and `Drawer`, so a button anywhere on
|
|
22
22
|
* the page can open the popover with no script.
|
|
23
23
|
*
|
|
24
|
+
* **Opening moves focus into the panel**, to the first control in it, or to the
|
|
25
|
+
* panel itself where it holds none, and closing from inside gives it back to
|
|
26
|
+
* the trigger. It closes on Escape, on a press outside it, and when focus moves
|
|
27
|
+
* out of the trigger and the panel to somewhere else on the page.
|
|
28
|
+
*
|
|
29
|
+
* `modal` keeps `Tab` inside the panel while it is open and says so with
|
|
30
|
+
* `aria-modal`, for a panel that has to be answered before the page goes on.
|
|
31
|
+
* Without it the popover does not trap focus.
|
|
32
|
+
*
|
|
33
|
+
* The panel's name is `label` when you set it. Otherwise it is the first
|
|
34
|
+
* heading in the panel, or the elements an `aria-labelledby` on the host names.
|
|
35
|
+
*
|
|
36
|
+
* `arrow` draws a point on the panel's edge facing the trigger. The side the
|
|
37
|
+
* panel landed on is on the host as `:state(placed-top)` and its kin, and the
|
|
38
|
+
* trigger's width is on the panel as `--floating-anchor-inline-size`, for a
|
|
39
|
+
* panel as wide as its trigger:
|
|
40
|
+
*
|
|
41
|
+
* ```css
|
|
42
|
+
* helix-popover::part(popover) {
|
|
43
|
+
* min-inline-size: var(--floating-anchor-inline-size);
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
24
47
|
* @since 0.1.0
|
|
25
48
|
* @status stable
|
|
26
49
|
* @category overlays
|
|
@@ -32,18 +55,41 @@ declare const HelixHeadlessPopover_base: (new (...args: any[]) => import("@fusio
|
|
|
32
55
|
*
|
|
33
56
|
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before it closes, and cancelable.
|
|
34
57
|
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before it opens, and cancelable.
|
|
35
|
-
* @event {
|
|
58
|
+
* @event {ToggleEvent} toggle - Fired after the panel opens or closes, with `newState` and `oldState`.
|
|
36
59
|
*
|
|
60
|
+
* @csspart arrow - The point on the panel's edge facing the trigger, drawn with `arrow`.
|
|
61
|
+
* @csspart arrow-bottom - Added to `arrow` while the panel is below its trigger, with the point on the panel's top edge.
|
|
62
|
+
* @csspart arrow-left - Added to `arrow` while the panel is left of its trigger, with the point on the panel's right edge.
|
|
63
|
+
* @csspart arrow-right - Added to `arrow` while the panel is right of its trigger, with the point on the panel's left edge.
|
|
64
|
+
* @csspart arrow-top - Added to `arrow` while the panel is above its trigger, with the point on the panel's bottom edge.
|
|
37
65
|
* @csspart content - The scrolling region inside the panel.
|
|
38
66
|
* @csspart popover - The panel.
|
|
39
67
|
*
|
|
40
68
|
* @cssstate open - While the popover is open.
|
|
69
|
+
* @cssstate placed-bottom - While the panel sits below its trigger.
|
|
70
|
+
* @cssstate placed-left - While the panel sits to the left of its trigger.
|
|
71
|
+
* @cssstate placed-right - While the panel sits to the right of its trigger.
|
|
72
|
+
* @cssstate placed-top - While the panel sits above its trigger.
|
|
41
73
|
*/
|
|
42
74
|
export declare class HelixHeadlessPopover extends HelixHeadlessPopover_base {
|
|
43
75
|
/**
|
|
44
|
-
*
|
|
76
|
+
* Draws a point on the panel's edge facing the trigger.
|
|
77
|
+
*
|
|
78
|
+
* Off by default, which is the house style: a panel that opens from its
|
|
79
|
+
* trigger already reads as belonging to it.
|
|
80
|
+
*/
|
|
81
|
+
arrow: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The panel's name, read by screen readers. Left at `Popover`, the first
|
|
84
|
+
* heading in the panel names it instead.
|
|
45
85
|
*/
|
|
46
86
|
label: string;
|
|
87
|
+
/**
|
|
88
|
+
* Keeps `Tab` inside the panel while it is open, and marks the panel
|
|
89
|
+
* `aria-modal`. A press outside and Escape still close it.
|
|
90
|
+
*/
|
|
91
|
+
modal: boolean;
|
|
92
|
+
protected readonly aria: AriaForwardController;
|
|
47
93
|
/**
|
|
48
94
|
* Space between the trigger and the panel, in pixels.
|
|
49
95
|
*/
|
|
@@ -55,6 +101,23 @@ export declare class HelixHeadlessPopover extends HelixHeadlessPopover_base {
|
|
|
55
101
|
*/
|
|
56
102
|
placement: Placement;
|
|
57
103
|
private readonly panel;
|
|
104
|
+
/**
|
|
105
|
+
* The side of its trigger the panel last landed on, which names the arrow's
|
|
106
|
+
* second part. Undefined until the first placement, which the server never
|
|
107
|
+
* makes.
|
|
108
|
+
*/
|
|
109
|
+
private side?;
|
|
110
|
+
private readonly scope;
|
|
111
|
+
/** What is assigned to the panel's slot. */
|
|
112
|
+
private get slotted();
|
|
113
|
+
/** Whether the first heading in the panel is what names it. */
|
|
114
|
+
private namedByHeading;
|
|
115
|
+
/**
|
|
116
|
+
* Names the panel by its first heading, where `label` was left alone and no
|
|
117
|
+
* `aria-labelledby` on the host names it. An element reference, for the
|
|
118
|
+
* reason `Dialog` gives.
|
|
119
|
+
*/
|
|
120
|
+
private nameByHeading;
|
|
58
121
|
/**
|
|
59
122
|
* The invoker that last opened the panel, when a command did.
|
|
60
123
|
*
|
|
@@ -75,6 +138,18 @@ export declare class HelixHeadlessPopover extends HelixHeadlessPopover_base {
|
|
|
75
138
|
show(): void;
|
|
76
139
|
/** Closes the panel and puts focus back on the trigger. */
|
|
77
140
|
hide(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Focus leaving the control closes the panel.
|
|
143
|
+
*
|
|
144
|
+
* A popover does not trap focus, so `Tab` past its last control walks on
|
|
145
|
+
* into the page, and a panel left open there covers what the reader moved
|
|
146
|
+
* to. Focus moving between the trigger, the panel and a command's invoker
|
|
147
|
+
* stays inside the control. With no `relatedTarget`, focus left the
|
|
148
|
+
* document or went nowhere, and a press outside is already its own signal.
|
|
149
|
+
*/
|
|
150
|
+
private readonly handleFocusOut;
|
|
151
|
+
connectedCallback(): void;
|
|
152
|
+
disconnectedCallback(): void;
|
|
78
153
|
private readonly handleTriggerClick;
|
|
79
154
|
/** Tells a slotted trigger the disclosure state, by property. See `tellDisclosure`. */
|
|
80
155
|
private tellTrigger;
|
|
@@ -4,24 +4,44 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { anchorElement, CommandController, customElement, deepActiveElement, HelixElement, MixinControllerOpen, PopoverController, tellDisclosure, } from '@fusion.dev/helix-core';
|
|
8
|
-
import { html } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
7
|
+
import { AriaForwardController, anchorElement, arrowReach, CommandController, customElement, deepActiveElement, FocusScopeController, HelixElement, headingIn, MixinControllerOpen, PopoverController, reflectElements, sideOf, tellDisclosure, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing } from 'lit';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpen(HelixElement) {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
|
+
this.arrow = false;
|
|
13
14
|
this.label = 'Popover';
|
|
15
|
+
this.modal = false;
|
|
16
|
+
this.aria = new AriaForwardController(this, {
|
|
17
|
+
target: () => this.partNamed('popover'),
|
|
18
|
+
});
|
|
14
19
|
this.offset = 4;
|
|
15
20
|
this.placement = 'bottom-start';
|
|
16
21
|
this.panel = new PopoverController(this, {
|
|
17
22
|
anchor: () => this.trigger,
|
|
23
|
+
arrow: () => this.partNamed('arrow'),
|
|
18
24
|
boundary: () => [this, this.invoker],
|
|
19
|
-
offset: () => this.offset,
|
|
25
|
+
offset: () => this.offset + arrowReach(this.partNamed('arrow')),
|
|
20
26
|
onDismiss: () => this.hide(),
|
|
27
|
+
onPlace: (placement) => {
|
|
28
|
+
const side = sideOf(placement);
|
|
29
|
+
this.toggleState('placed-bottom', side === 'bottom');
|
|
30
|
+
this.toggleState('placed-left', side === 'left');
|
|
31
|
+
this.toggleState('placed-right', side === 'right');
|
|
32
|
+
this.toggleState('placed-top', side === 'top');
|
|
33
|
+
this.side = side;
|
|
34
|
+
},
|
|
21
35
|
open: () => this.open,
|
|
22
36
|
placement: () => this.placement,
|
|
23
37
|
surface: () => this.partNamed('popover'),
|
|
24
38
|
});
|
|
39
|
+
this.scope = new FocusScopeController(this, {
|
|
40
|
+
contain: () => this.modal && this.open,
|
|
41
|
+
contents: () => this.slotted,
|
|
42
|
+
surface: () => this.partNamed('popover'),
|
|
43
|
+
});
|
|
44
|
+
this.namedByHeading = false;
|
|
25
45
|
this.commands = new CommandController(this, {
|
|
26
46
|
commands: {
|
|
27
47
|
'--close': () => this.hide(),
|
|
@@ -40,6 +60,16 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
40
60
|
},
|
|
41
61
|
},
|
|
42
62
|
});
|
|
63
|
+
this.handleFocusOut = (event) => {
|
|
64
|
+
const next = event.relatedTarget;
|
|
65
|
+
if (!this.open || !(next instanceof Node)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (this.contains(next) || this.renderRoot.contains(next) || this.invoker?.contains(next)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.hide();
|
|
72
|
+
};
|
|
43
73
|
this.handleTriggerClick = () => {
|
|
44
74
|
if (this.open) {
|
|
45
75
|
this.hide();
|
|
@@ -49,6 +79,16 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
49
79
|
}
|
|
50
80
|
};
|
|
51
81
|
}
|
|
82
|
+
get slotted() {
|
|
83
|
+
return (this.renderRoot
|
|
84
|
+
?.querySelector('slot:not([name])')
|
|
85
|
+
?.assignedElements({ flatten: true }) ?? []);
|
|
86
|
+
}
|
|
87
|
+
nameByHeading() {
|
|
88
|
+
const asked = this.label !== 'Popover' || this.hasAttribute('aria-labelledby');
|
|
89
|
+
const heading = asked ? null : headingIn(this.slotted);
|
|
90
|
+
this.namedByHeading = reflectElements(this.partNamed('popover'), 'ariaLabelledByElements', heading ? [heading] : [], this.namedByHeading);
|
|
91
|
+
}
|
|
52
92
|
get trigger() {
|
|
53
93
|
return (this.invoker ??
|
|
54
94
|
anchorElement(this.renderRoot?.querySelector('slot[name="trigger"]'), this));
|
|
@@ -67,6 +107,14 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
67
107
|
this.trigger.focus();
|
|
68
108
|
}
|
|
69
109
|
}
|
|
110
|
+
connectedCallback() {
|
|
111
|
+
super.connectedCallback();
|
|
112
|
+
this.addEventListener('focusout', this.handleFocusOut);
|
|
113
|
+
}
|
|
114
|
+
disconnectedCallback() {
|
|
115
|
+
super.disconnectedCallback();
|
|
116
|
+
this.removeEventListener('focusout', this.handleFocusOut);
|
|
117
|
+
}
|
|
70
118
|
tellTrigger() {
|
|
71
119
|
const slot = this.renderRoot?.querySelector('slot[name="trigger"]');
|
|
72
120
|
tellDisclosure(slot?.assignedElements({ flatten: false })[0], this.open);
|
|
@@ -74,6 +122,10 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
74
122
|
updated(changed) {
|
|
75
123
|
super.updated(changed);
|
|
76
124
|
this.tellTrigger();
|
|
125
|
+
this.nameByHeading();
|
|
126
|
+
if (changed.has('open') && this.open && changed.get('open') === false) {
|
|
127
|
+
this.scope.enter();
|
|
128
|
+
}
|
|
77
129
|
}
|
|
78
130
|
render() {
|
|
79
131
|
return html `<slot name="trigger" @click=${this.handleTriggerClick}></slot>
|
|
@@ -82,21 +134,38 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
82
134
|
popover="manual"
|
|
83
135
|
role="dialog"
|
|
84
136
|
aria-label=${this.label}
|
|
137
|
+
aria-modal=${this.modal ? 'true' : nothing}
|
|
138
|
+
tabindex="-1"
|
|
85
139
|
?hidden=${!this.open}
|
|
86
140
|
>
|
|
87
|
-
|
|
141
|
+
${this.arrow
|
|
142
|
+
? html `<div
|
|
143
|
+
part="arrow${this.side ? { bottom: ' arrow-bottom', left: ' arrow-left', right: ' arrow-right', top: ' arrow-top' }[this.side] : ''}"
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
></div>`
|
|
146
|
+
: nothing}
|
|
147
|
+
<div part="content"><slot @slotchange=${this.nameByHeading}></slot></div>
|
|
88
148
|
</div>`;
|
|
89
149
|
}
|
|
90
150
|
};
|
|
151
|
+
__decorate([
|
|
152
|
+
property({ type: Boolean })
|
|
153
|
+
], HelixHeadlessPopover.prototype, "arrow", void 0);
|
|
91
154
|
__decorate([
|
|
92
155
|
property()
|
|
93
156
|
], HelixHeadlessPopover.prototype, "label", void 0);
|
|
157
|
+
__decorate([
|
|
158
|
+
property({ type: Boolean })
|
|
159
|
+
], HelixHeadlessPopover.prototype, "modal", void 0);
|
|
94
160
|
__decorate([
|
|
95
161
|
property({ type: Number })
|
|
96
162
|
], HelixHeadlessPopover.prototype, "offset", void 0);
|
|
97
163
|
__decorate([
|
|
98
164
|
property()
|
|
99
165
|
], HelixHeadlessPopover.prototype, "placement", void 0);
|
|
166
|
+
__decorate([
|
|
167
|
+
state()
|
|
168
|
+
], HelixHeadlessPopover.prototype, "side", void 0);
|
|
100
169
|
HelixHeadlessPopover = __decorate([
|
|
101
170
|
customElement('helix-headless-popover')
|
|
102
171
|
], HelixHeadlessPopover);
|
|
@@ -21,6 +21,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
21
21
|
* @aliases Progress bar, Loading bar
|
|
22
22
|
* @documentation https://fusion.dev/helix/components/progress
|
|
23
23
|
*
|
|
24
|
+
* @slot label - A visible label. Its words name the progress bar in place of `label`.
|
|
25
|
+
*
|
|
24
26
|
* @csspart groove - The unfilled circle under the ring's filled arc.
|
|
25
27
|
* @csspart indicator - The filled part that shows the work done.
|
|
26
28
|
* @csspart track - The full length the work is measured against.
|
|
@@ -46,12 +48,28 @@ export declare class HelixHeadlessProgress extends HelixElement {
|
|
|
46
48
|
indeterminate: boolean;
|
|
47
49
|
/**
|
|
48
50
|
* Accessible name for what is in progress.
|
|
51
|
+
*
|
|
52
|
+
* A visible label in the `label` slot names it instead, with the words it
|
|
53
|
+
* shows, so the name and the label cannot disagree.
|
|
49
54
|
*/
|
|
50
55
|
label: string;
|
|
56
|
+
/**
|
|
57
|
+
* The locale the percentage is written in, such as `fr` for `72 %`.
|
|
58
|
+
*
|
|
59
|
+
* Absent uses the runtime's own.
|
|
60
|
+
*/
|
|
61
|
+
locale: string;
|
|
51
62
|
/**
|
|
52
63
|
* The total the work is measured against.
|
|
53
64
|
*/
|
|
54
65
|
max: number;
|
|
66
|
+
/**
|
|
67
|
+
* Where the work starts, in the same units as `max`. Defaults to zero.
|
|
68
|
+
*
|
|
69
|
+
* For a measure that does not start at nothing, such as a migration from
|
|
70
|
+
* version 12 to version 20, where 12 is the empty bar.
|
|
71
|
+
*/
|
|
72
|
+
min: number;
|
|
55
73
|
/**
|
|
56
74
|
* Whether the value is drawn as a bar or as a ring.
|
|
57
75
|
*
|
|
@@ -82,7 +100,7 @@ export declare class HelixHeadlessProgress extends HelixElement {
|
|
|
82
100
|
/**
|
|
83
101
|
* What has been done so far, in the same units as `max`.
|
|
84
102
|
*
|
|
85
|
-
* Clamped between
|
|
103
|
+
* Clamped between `min` and `max` for both the fill and the announcement, so
|
|
86
104
|
* the two can never disagree about what is on screen.
|
|
87
105
|
*/
|
|
88
106
|
value: number;
|
|
@@ -96,9 +114,13 @@ export declare class HelixHeadlessProgress extends HelixElement {
|
|
|
96
114
|
* report progress.
|
|
97
115
|
*/
|
|
98
116
|
valueText: string;
|
|
117
|
+
/** The range the value is measured in, with a top above its bottom. */
|
|
118
|
+
private get range();
|
|
99
119
|
/** What is done, as a fraction of the whole, between 0 and 1. */
|
|
100
120
|
get fraction(): number;
|
|
101
121
|
protected willUpdate(changed: PropertyValues): void;
|
|
122
|
+
/** A label's words changed, so the name is written again. */
|
|
123
|
+
private readonly renamed;
|
|
102
124
|
protected render(): TemplateResult;
|
|
103
125
|
}
|
|
104
126
|
declare global {
|
|
@@ -4,32 +4,56 @@ 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, HelixElement, hostAria } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, HelixElement, hostAria, slottedWords } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
const percents = new Map();
|
|
11
|
+
const percent = (share, locale) => {
|
|
12
|
+
let format = percents.get(locale);
|
|
13
|
+
if (!format) {
|
|
14
|
+
format = new Intl.NumberFormat(locale || undefined, {
|
|
15
|
+
maximumFractionDigits: 0,
|
|
16
|
+
style: 'percent',
|
|
17
|
+
});
|
|
18
|
+
percents.set(locale, format);
|
|
19
|
+
}
|
|
20
|
+
return format.format(share);
|
|
21
|
+
};
|
|
10
22
|
let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
|
|
11
23
|
constructor() {
|
|
12
24
|
super(...arguments);
|
|
13
25
|
this.indeterminate = false;
|
|
14
26
|
this.label = 'Progress';
|
|
27
|
+
this.locale = '';
|
|
15
28
|
this.max = 100;
|
|
29
|
+
this.min = 0;
|
|
16
30
|
this.showValue = false;
|
|
17
31
|
this.value = 0;
|
|
18
32
|
this.valueText = '';
|
|
33
|
+
this.renamed = () => {
|
|
34
|
+
this.requestUpdate();
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
get range() {
|
|
38
|
+
const min = Number.isFinite(this.min) ? this.min : 0;
|
|
39
|
+
return { max: this.max > min ? this.max : min + 100, min };
|
|
19
40
|
}
|
|
20
41
|
get fraction() {
|
|
21
|
-
const
|
|
22
|
-
return Math.min(Math.max(this.value / max, 0), 1) || 0;
|
|
42
|
+
const { max, min } = this.range;
|
|
43
|
+
return Math.min(Math.max((this.value - min) / (max - min), 0), 1) || 0;
|
|
23
44
|
}
|
|
24
45
|
willUpdate(changed) {
|
|
25
46
|
super.willUpdate(changed);
|
|
26
|
-
hostAria(this.internals, {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
hostAria(this.internals, {
|
|
48
|
+
label: slottedWords(this, ['label']) || this.label,
|
|
49
|
+
role: 'progressbar',
|
|
50
|
+
});
|
|
51
|
+
const { max, min } = this.range;
|
|
52
|
+
this.internals.ariaValueMin = String(min);
|
|
53
|
+
this.internals.ariaValueMax = String(max);
|
|
30
54
|
this.internals.ariaValueNow = this.indeterminate
|
|
31
55
|
? null
|
|
32
|
-
: String(Math.min(Math.max(this.value ||
|
|
56
|
+
: String(Math.min(Math.max(this.value || min, min), max));
|
|
33
57
|
this.internals.ariaValueText = this.valueText || null;
|
|
34
58
|
}
|
|
35
59
|
render() {
|
|
@@ -38,14 +62,14 @@ let HelixHeadlessProgress = class HelixHeadlessProgress extends HelixElement {
|
|
|
38
62
|
<circle part="groove" />
|
|
39
63
|
<circle part="indicator" pathLength="100" style="--progress-fraction: ${fraction}" />
|
|
40
64
|
</svg>`;
|
|
41
|
-
return html
|
|
65
|
+
return html `<slot name="label" @slotchange=${this.renamed}></slot>${this.shape === 'ring'
|
|
42
66
|
? ring
|
|
43
67
|
: html `<div part="track">
|
|
44
68
|
<div part="indicator" style="--progress-fraction: ${fraction}"></div>
|
|
45
69
|
</div>`}
|
|
46
70
|
${this.showValue && !this.indeterminate
|
|
47
71
|
? html `<span part="value" aria-hidden="true"
|
|
48
|
-
>${this.valueText ||
|
|
72
|
+
>${this.valueText || percent(this.fraction, this.locale)}</span
|
|
49
73
|
>`
|
|
50
74
|
: ''}`;
|
|
51
75
|
}
|
|
@@ -56,9 +80,15 @@ __decorate([
|
|
|
56
80
|
__decorate([
|
|
57
81
|
property()
|
|
58
82
|
], HelixHeadlessProgress.prototype, "label", void 0);
|
|
83
|
+
__decorate([
|
|
84
|
+
property()
|
|
85
|
+
], HelixHeadlessProgress.prototype, "locale", void 0);
|
|
59
86
|
__decorate([
|
|
60
87
|
property({ type: Number })
|
|
61
88
|
], HelixHeadlessProgress.prototype, "max", void 0);
|
|
89
|
+
__decorate([
|
|
90
|
+
property({ type: Number })
|
|
91
|
+
], HelixHeadlessProgress.prototype, "min", void 0);
|
|
62
92
|
__decorate([
|
|
63
93
|
property({ reflect: true })
|
|
64
94
|
], HelixHeadlessProgress.prototype, "shape", void 0);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A drawn code as a file of its own: an SVG with its colors written in, which
|
|
3
|
+
* an image can load and a canvas can turn into a PNG.
|
|
4
|
+
*
|
|
5
|
+
* Beside the family's `index.ts`, because writing a file is not the element's
|
|
6
|
+
* template, and the markup is a pure function of what the element drew.
|
|
7
|
+
*/
|
|
8
|
+
/** What a code is drawn with, read from the element once it has rendered. */
|
|
9
|
+
export interface QrDrawing {
|
|
10
|
+
/** The fill behind the code, quiet zone included. */
|
|
11
|
+
background: string;
|
|
12
|
+
/** The side of the `viewBox`, quiet zone included. */
|
|
13
|
+
box: number;
|
|
14
|
+
/** The color of the corner squares. */
|
|
15
|
+
finderColor: string;
|
|
16
|
+
/** The path of the corner squares. */
|
|
17
|
+
finders: string;
|
|
18
|
+
/** The color of every other module. */
|
|
19
|
+
moduleColor: string;
|
|
20
|
+
/** The path of every other module. */
|
|
21
|
+
modules: string;
|
|
22
|
+
/** The quiet zone, in modules, that both paths are moved in by. */
|
|
23
|
+
quiet: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The code as standalone SVG markup, sized `size` pixels square.
|
|
27
|
+
*
|
|
28
|
+
* The background is a rectangle under the code, since a scanner needs the
|
|
29
|
+
* light quiet zone and a transparent file put on a dark page has none.
|
|
30
|
+
*/
|
|
31
|
+
export declare const qrSvg: (drawing: QrDrawing, size: number) => string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const attribute = (value) => value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
2
|
+
export const qrSvg = (drawing, size) => {
|
|
3
|
+
const shift = `translate(${drawing.quiet} ${drawing.quiet})`;
|
|
4
|
+
return (`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${drawing.box} ${drawing.box}"` +
|
|
5
|
+
` width="${size}" height="${size}" shape-rendering="crispEdges">` +
|
|
6
|
+
`<rect width="${drawing.box}" height="${drawing.box}" fill="${attribute(drawing.background)}"/>` +
|
|
7
|
+
`<path d="${attribute(drawing.modules)}" transform="${shift}" fill="${attribute(drawing.moduleColor)}"/>` +
|
|
8
|
+
`<path d="${attribute(drawing.finders)}" transform="${shift}" fill="${attribute(drawing.finderColor)}"/>` +
|
|
9
|
+
'</svg>');
|
|
10
|
+
};
|
|
@@ -26,6 +26,14 @@ export type QrStyle = 'dots' | 'rounded' | 'square';
|
|
|
26
26
|
* the same mark makes the code unscannable. The default is `M`, and `H` when the
|
|
27
27
|
* `logo` slot is filled.
|
|
28
28
|
*
|
|
29
|
+
* A higher level holds fewer characters, so a value that fits at `M` can be too
|
|
30
|
+
* long once a logo raises the level to `H`. A value too long for the level
|
|
31
|
+
* draws no code, and the element fires `error`.
|
|
32
|
+
*
|
|
33
|
+
* `toSvg()`, `toPng()` and `download()` give the drawn code as a file of its
|
|
34
|
+
* own, in the colors it is drawn in, for a page that offers it to save or
|
|
35
|
+
* print, as Zag's download trigger does.
|
|
36
|
+
*
|
|
29
37
|
* @since 0.1.0
|
|
30
38
|
* @status stable
|
|
31
39
|
* @category media
|
|
@@ -34,6 +42,8 @@ export type QrStyle = 'dots' | 'rounded' | 'square';
|
|
|
34
42
|
*
|
|
35
43
|
* @slot logo - A logo for the center. A code with a logo scans only at a high correction level.
|
|
36
44
|
*
|
|
45
|
+
* @event {Event} error - The value is too long for a code at the correction level, so no code is drawn.
|
|
46
|
+
*
|
|
37
47
|
* @csspart code - The square code image.
|
|
38
48
|
* @csspart finders - The three corner squares, which can take their own color.
|
|
39
49
|
* @csspart logo - The box that holds the center logo.
|
|
@@ -84,6 +94,41 @@ export declare class HelixHeadlessQrCode extends HelixElement {
|
|
|
84
94
|
protected firstUpdated(changed: PropertyValues): void;
|
|
85
95
|
/** The level actually used, which the slot can raise. */
|
|
86
96
|
private get level();
|
|
97
|
+
/** The value and level that did not fit, so `error` fires once for each. */
|
|
98
|
+
private overflowed;
|
|
99
|
+
/**
|
|
100
|
+
* The code for the value at the level, or undefined where the value is too
|
|
101
|
+
* long for it.
|
|
102
|
+
*
|
|
103
|
+
* `encode` throws for a value past the level's capacity, and it threw inside
|
|
104
|
+
* the render: the update rejected and the last code stayed on screen. A
|
|
105
|
+
* logo raising the level to `H` was the easy way to get there, since `H`
|
|
106
|
+
* holds the fewest characters.
|
|
107
|
+
*/
|
|
108
|
+
private encoded;
|
|
109
|
+
protected updated(changed: PropertyValues): void;
|
|
110
|
+
/**
|
|
111
|
+
* The code as an SVG file of its own, `size` pixels square, in the colors it
|
|
112
|
+
* is drawn in, with its background under the quiet zone.
|
|
113
|
+
*
|
|
114
|
+
* `''` where no code is drawn, before the first render or for a value too
|
|
115
|
+
* long for the level. A slotted logo is not in the file, being the page's
|
|
116
|
+
* own markup; save the file at `H` and the code still scans without it.
|
|
117
|
+
*/
|
|
118
|
+
toSvg(size?: number): string;
|
|
119
|
+
/**
|
|
120
|
+
* The code as a PNG, `size` pixels square, drawn from `toSvg()`.
|
|
121
|
+
*
|
|
122
|
+
* `null` where no code is drawn.
|
|
123
|
+
*/
|
|
124
|
+
toPng(size?: number): Promise<Blob | null>;
|
|
125
|
+
/**
|
|
126
|
+
* Saves the code as a PNG named `filename`, `size` pixels square.
|
|
127
|
+
*
|
|
128
|
+
* Call it from a press, since a browser lets a page start a download only
|
|
129
|
+
* in answer to one. Does nothing where no code is drawn.
|
|
130
|
+
*/
|
|
131
|
+
download(filename?: string, size?: number): Promise<void>;
|
|
87
132
|
private renderCode;
|
|
88
133
|
protected render(): TemplateResult;
|
|
89
134
|
}
|
|
@@ -4,10 +4,12 @@ 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, HelixElement } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, HelixElement, rgbOf } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg, } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { encode } from 'uqr';
|
|
11
|
+
import { qrSvg } from '../export.js';
|
|
12
|
+
const QUIET = 4;
|
|
11
13
|
const isFinder = (x, y, size) => {
|
|
12
14
|
const near = (a, b) => a < 8 && b < 8;
|
|
13
15
|
return near(x, y) || near(size - 1 - x, y) || near(x, size - 1 - y);
|
|
@@ -65,6 +67,7 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
65
67
|
this.label = '';
|
|
66
68
|
this.value = '';
|
|
67
69
|
this.logo = false;
|
|
70
|
+
this.overflowed = '';
|
|
68
71
|
}
|
|
69
72
|
willUpdate(changed) {
|
|
70
73
|
super.willUpdate(changed);
|
|
@@ -79,13 +82,85 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
79
82
|
get level() {
|
|
80
83
|
return this.correction ?? (this.logo ? 'H' : 'M');
|
|
81
84
|
}
|
|
85
|
+
encoded() {
|
|
86
|
+
try {
|
|
87
|
+
return encode(this.value, { border: 0, ecc: this.level });
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error instanceof RangeError) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
updated(changed) {
|
|
97
|
+
super.updated(changed);
|
|
98
|
+
const key = `${this.level}:${this.value}`;
|
|
99
|
+
if (this.value !== '' && this.encoded() === undefined) {
|
|
100
|
+
if (this.overflowed !== key) {
|
|
101
|
+
this.overflowed = key;
|
|
102
|
+
this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.overflowed = '';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
toSvg(size = 512) {
|
|
110
|
+
const code = this.renderRoot?.querySelector('svg[part="code"]');
|
|
111
|
+
const modules = code?.querySelector('[part="modules"]');
|
|
112
|
+
const finders = code?.querySelector('[part="finders"]');
|
|
113
|
+
if (!code || !modules || !finders) {
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
const ground = getComputedStyle(this).backgroundColor;
|
|
117
|
+
const clear = ground === '' || ground === 'transparent' || /^rgba\(.*,\s*0\)$/.test(ground);
|
|
118
|
+
return qrSvg({
|
|
119
|
+
background: clear ? '#ffffff' : rgbOf(ground),
|
|
120
|
+
box: Number(code.getAttribute('viewBox')?.split(' ')[2] ?? 0),
|
|
121
|
+
finderColor: rgbOf(getComputedStyle(finders).fill || '#000000'),
|
|
122
|
+
finders: finders.getAttribute('d') ?? '',
|
|
123
|
+
moduleColor: rgbOf(getComputedStyle(modules).fill || '#000000'),
|
|
124
|
+
modules: modules.getAttribute('d') ?? '',
|
|
125
|
+
quiet: QUIET,
|
|
126
|
+
}, size);
|
|
127
|
+
}
|
|
128
|
+
async toPng(size = 512) {
|
|
129
|
+
const markup = this.toSvg(size);
|
|
130
|
+
if (markup === '') {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const image = new Image();
|
|
134
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
|
|
135
|
+
await image.decode();
|
|
136
|
+
const canvas = document.createElement('canvas');
|
|
137
|
+
canvas.width = size;
|
|
138
|
+
canvas.height = size;
|
|
139
|
+
canvas.getContext('2d')?.drawImage(image, 0, 0, size, size);
|
|
140
|
+
return new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
|
|
141
|
+
}
|
|
142
|
+
async download(filename = 'qr-code.png', size = 512) {
|
|
143
|
+
const png = await this.toPng(size);
|
|
144
|
+
if (!png) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const url = URL.createObjectURL(png);
|
|
148
|
+
const link = document.createElement('a');
|
|
149
|
+
link.href = url;
|
|
150
|
+
link.download = filename;
|
|
151
|
+
link.click();
|
|
152
|
+
setTimeout(() => URL.revokeObjectURL(url));
|
|
153
|
+
}
|
|
82
154
|
renderCode() {
|
|
83
155
|
if (this.value === '') {
|
|
84
156
|
return nothing;
|
|
85
157
|
}
|
|
86
|
-
const result =
|
|
158
|
+
const result = this.encoded();
|
|
159
|
+
if (result === undefined) {
|
|
160
|
+
return nothing;
|
|
161
|
+
}
|
|
87
162
|
const size = result.size;
|
|
88
|
-
const quiet =
|
|
163
|
+
const quiet = QUIET;
|
|
89
164
|
const box = size + quiet * 2;
|
|
90
165
|
const finders = pathFor(result.data, 'square', 'finders');
|
|
91
166
|
return svg `<svg
|