@fusion.dev/helix-headless 0.144.0 → 0.146.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 +15195 -4240
- package/dist/accordion/item/accordion-item.d.ts +62 -12
- package/dist/accordion/item/accordion-item.js +41 -10
- package/dist/accordion/root/accordion-root.d.ts +14 -5
- package/dist/accordion/root/accordion-root.js +7 -1
- package/dist/announcement/root/announcement-root.d.ts +24 -3
- package/dist/announcement/root/announcement-root.js +39 -2
- package/dist/app-header/root/app-header-root.d.ts +12 -0
- package/dist/app-header/root/app-header-root.js +13 -1
- package/dist/app-header/sectioning.d.ts +9 -0
- package/dist/app-header/sectioning.js +27 -0
- package/dist/avatar/group/avatar-group.d.ts +20 -14
- package/dist/avatar/group/avatar-group.js +17 -19
- package/dist/badge/root/badge-root.d.ts +3 -1
- package/dist/badge/root/badge-root.js +2 -2
- package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
- package/dist/bar-chart/root/bar-chart-root.js +52 -15
- package/dist/bar-chart/stack.d.ts +15 -0
- package/dist/bar-chart/stack.js +19 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +57 -1
- package/dist/breadcrumbs/root/breadcrumbs-root.js +73 -3
- package/dist/button/root/button-root.d.ts +26 -0
- package/dist/button/root/button-root.js +38 -18
- package/dist/button-group/root/button-group-root.d.ts +2 -1
- package/dist/button-group/root/button-group-root.js +5 -1
- package/dist/canvas/node/canvas-node.d.ts +4 -3
- package/dist/canvas/node/canvas-node.js +3 -2
- package/dist/canvas/root/canvas-root.d.ts +38 -3
- package/dist/canvas/root/canvas-root.js +123 -42
- package/dist/card/root/card-root.d.ts +24 -3
- package/dist/card/root/card-root.js +20 -8
- package/dist/carousel/root/carousel-root.d.ts +133 -4
- package/dist/carousel/root/carousel-root.js +174 -24
- package/dist/carousel/stops.d.ts +20 -0
- package/dist/carousel/stops.js +30 -0
- 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 +27 -10
- package/dist/chat/naming.d.ts +26 -0
- package/dist/chat/naming.js +14 -0
- package/dist/chat/root/chat-root.d.ts +58 -0
- package/dist/chat/root/chat-root.js +43 -3
- package/dist/checkbox/root/checkbox-root.d.ts +20 -0
- package/dist/checkbox/root/checkbox-root.js +14 -1
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
- package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
- package/dist/checkbox-group/select-all.d.ts +27 -0
- package/dist/checkbox-group/select-all.js +12 -0
- package/dist/code-block/highlight.d.ts +15 -0
- package/dist/code-block/highlight.js +4 -0
- package/dist/code-block/root/code-block-root.d.ts +57 -2
- package/dist/code-block/root/code-block-root.js +63 -8
- package/dist/color-input/root/color-input-root.d.ts +3 -0
- package/dist/color-input/root/color-input-root.js +6 -3
- package/dist/color-picker/root/color-picker-root.d.ts +33 -4
- package/dist/color-picker/root/color-picker-root.js +80 -21
- package/dist/color-picker/swatches.d.ts +12 -0
- package/dist/color-picker/swatches.js +26 -0
- package/dist/combobox/root/combobox-root.d.ts +43 -0
- package/dist/combobox/root/combobox-root.js +79 -15
- package/dist/command/root/command-root.d.ts +16 -1
- package/dist/command/root/command-root.js +22 -4
- package/dist/copy-button/root/copy-button-root.d.ts +26 -1
- package/dist/copy-button/root/copy-button-root.js +12 -5
- package/dist/data-table/index.d.ts +1 -1
- package/dist/data-table/index.js +1 -1
- package/dist/data-table/keys.d.ts +24 -0
- package/dist/data-table/keys.js +24 -0
- package/dist/data-table/root/data-table-root.d.ts +170 -13
- package/dist/data-table/root/data-table-root.js +294 -52
- package/dist/data-table/tree.d.ts +55 -0
- package/dist/data-table/tree.js +54 -0
- package/dist/date-input/root/date-input-root.d.ts +3 -1
- package/dist/date-input/root/date-input-root.js +18 -7
- package/dist/date-picker/root/date-picker-root.js +12 -6
- 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.js +15 -1
- package/dist/dialog/root/dialog-root.d.ts +8 -2
- package/dist/dialog/root/dialog-root.js +16 -1
- package/dist/divider/root/divider-root.d.ts +3 -0
- package/dist/divider/root/divider-root.js +2 -1
- package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
- package/dist/donut-chart/root/donut-chart-root.js +48 -1
- package/dist/drawer/reveal.d.ts +8 -0
- package/dist/drawer/reveal.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +59 -0
- package/dist/drawer/root/drawer-root.js +63 -4
- 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 +29 -0
- package/dist/drilldown/root/drilldown-root.js +28 -5
- package/dist/element-events.generated.js +83 -12
- package/dist/empty/root/empty-root.d.ts +3 -1
- package/dist/empty/root/empty-root.js +3 -2
- package/dist/file-input/root/file-input-root.d.ts +69 -6
- package/dist/file-input/root/file-input-root.js +82 -19
- package/dist/file-input/screen.d.ts +2 -1
- package/dist/filter-input/menu/filter-menu.d.ts +5 -0
- package/dist/filter-input/menu/filter-menu.js +16 -4
- package/dist/filter-input/root/filter-input-root.d.ts +30 -6
- package/dist/filter-input/root/filter-input-root.js +1 -1
- package/dist/filter-input/suggest.d.ts +28 -3
- package/dist/filter-input/suggest.js +18 -8
- package/dist/format-date/root/format-date-root.d.ts +9 -16
- package/dist/format-date/root/format-date-root.js +10 -27
- package/dist/heatmap/root/heatmap-root.d.ts +24 -13
- package/dist/heatmap/root/heatmap-root.js +34 -12
- package/dist/icon/root/icon-root.d.ts +12 -1
- package/dist/icon/root/icon-root.js +21 -10
- package/dist/icon/settle.d.ts +7 -0
- package/dist/icon/settle.js +10 -0
- package/dist/icon-button/root/icon-button-root.d.ts +2 -0
- package/dist/image/root/image-root.d.ts +5 -1
- package/dist/image/root/image-root.js +10 -2
- package/dist/input/root/input-root.d.ts +13 -3
- package/dist/input/root/input-root.js +12 -3
- package/dist/kbd/root/kbd-root.d.ts +35 -1
- package/dist/kbd/root/kbd-root.js +23 -4
- package/dist/legend/root/legend-root.d.ts +55 -8
- package/dist/legend/root/legend-root.js +55 -13
- package/dist/lightbox/root/lightbox-root.d.ts +49 -6
- package/dist/lightbox/root/lightbox-root.js +86 -19
- package/dist/lightbox/zoom-controller.d.ts +59 -0
- package/dist/lightbox/zoom-controller.js +128 -0
- package/dist/lightbox/zoom.d.ts +22 -0
- package/dist/lightbox/zoom.js +16 -0
- package/dist/line-chart/root/line-chart-root.d.ts +22 -1
- package/dist/line-chart/root/line-chart-root.js +21 -1
- package/dist/list/item/list-item.d.ts +70 -1
- package/dist/list/item/list-item.js +77 -6
- package/dist/list/numbering.d.ts +14 -0
- package/dist/list/numbering.js +8 -0
- package/dist/list/root/list-root.d.ts +155 -9
- package/dist/list/root/list-root.js +246 -4
- package/dist/list/selection.d.ts +49 -0
- package/dist/list/selection.js +30 -0
- package/dist/listbox/root/listbox-root.d.ts +37 -1
- package/dist/listbox/root/listbox-root.js +49 -4
- package/dist/loader/busy.d.ts +10 -0
- package/dist/loader/busy.js +11 -0
- package/dist/loader/root/loader-root.d.ts +9 -2
- package/dist/loader/root/loader-root.js +23 -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 +62 -0
- package/dist/megamenu/root/megamenu-root.js +90 -5
- package/dist/menu/index.d.ts +1 -0
- package/dist/menu/index.js +1 -0
- package/dist/menu/item/menu-item.d.ts +87 -11
- package/dist/menu/item/menu-item.js +78 -22
- package/dist/menu/root/menu-root.d.ts +39 -1
- package/dist/menu/root/menu-root.js +65 -8
- 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 +20 -1
- package/dist/meter/root/meter-root.js +11 -15
- package/dist/navigation/root/navigation-root.d.ts +18 -2
- package/dist/navigation/root/navigation-root.js +29 -3
- package/dist/notifications/root/notifications-root.d.ts +25 -0
- package/dist/notifications/root/notifications-root.js +33 -5
- package/dist/number-input/root/number-input-root.d.ts +17 -8
- package/dist/number-input/root/number-input-root.js +2 -7
- package/dist/object-inspector/root/object-inspector-root.d.ts +161 -32
- package/dist/object-inspector/root/object-inspector-root.js +264 -123
- package/dist/object-inspector/rows.d.ts +75 -0
- package/dist/object-inspector/rows.js +119 -0
- package/dist/option/root/option-root.d.ts +5 -3
- package/dist/outline/levels.d.ts +31 -0
- package/dist/outline/levels.js +23 -0
- package/dist/outline/root/outline-root.d.ts +86 -3
- package/dist/outline/root/outline-root.js +95 -12
- package/dist/pagination/size/pagination-size.d.ts +12 -2
- package/dist/pagination/size/pagination-size.js +20 -5
- package/dist/pin-input/code.d.ts +10 -0
- package/dist/pin-input/code.js +13 -0
- package/dist/pin-input/root/pin-input-root.d.ts +25 -1
- package/dist/pin-input/root/pin-input-root.js +10 -11
- package/dist/popover/hide-outside.d.ts +14 -0
- package/dist/popover/hide-outside.js +41 -0
- package/dist/popover/root/popover-root.d.ts +57 -7
- package/dist/popover/root/popover-root.js +58 -3
- package/dist/progress/root/progress-root.d.ts +20 -0
- package/dist/progress/root/progress-root.js +15 -15
- package/dist/qr-code/export.d.ts +26 -1
- package/dist/qr-code/export.js +14 -0
- package/dist/qr-code/root/qr-code-root.d.ts +19 -5
- package/dist/qr-code/root/qr-code-root.js +58 -9
- package/dist/quote/marks.d.ts +8 -0
- package/dist/quote/marks.js +5 -0
- package/dist/quote/root/quote-root.d.ts +14 -2
- package/dist/quote/root/quote-root.js +23 -1
- package/dist/radio/group/radio-group.d.ts +4 -10
- package/dist/radio/group/radio-group.js +5 -10
- package/dist/radio/root/radio-root.d.ts +6 -2
- package/dist/radio/root/radio-root.js +6 -1
- package/dist/rating/root/rating-root.d.ts +10 -5
- package/dist/rating/root/rating-root.js +32 -8
- package/dist/rating/steps.d.ts +7 -0
- package/dist/rating/steps.js +9 -0
- package/dist/scatter-chart/fit.d.ts +15 -0
- package/dist/scatter-chart/fit.js +35 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
- package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
- package/dist/scroll-area/root/scroll-area-root.d.ts +84 -0
- package/dist/scroll-area/root/scroll-area-root.js +106 -7
- package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
- package/dist/scroll-area/scrollbar-controller.js +172 -0
- package/dist/scroll-area/scrollbar.d.ts +92 -0
- package/dist/scroll-area/scrollbar.js +33 -0
- package/dist/select/root/select-root.d.ts +15 -0
- package/dist/select/root/select-root.js +18 -6
- package/dist/skeleton/root/skeleton-root.d.ts +3 -0
- package/dist/skeleton/root/skeleton-root.js +4 -2
- package/dist/skip-link/root/skip-link-root.js +2 -2
- package/dist/slider/root/slider-root.d.ts +41 -4
- package/dist/slider/root/slider-root.js +23 -3
- package/dist/sparkline/readings.d.ts +18 -0
- package/dist/sparkline/readings.js +19 -0
- package/dist/sparkline/root/sparkline-root.d.ts +78 -7
- package/dist/sparkline/root/sparkline-root.js +118 -23
- package/dist/speed-dial/root/speed-dial-root.js +2 -2
- package/dist/splitter/root/splitter-root.d.ts +20 -12
- package/dist/splitter/root/splitter-root.js +226 -203
- package/dist/splitter/snap.d.ts +9 -0
- package/dist/splitter/snap.js +12 -0
- package/dist/stat/root/stat-root.d.ts +14 -5
- package/dist/stat/root/stat-root.js +21 -5
- package/dist/steps/reach.d.ts +20 -0
- package/dist/steps/reach.js +15 -0
- package/dist/steps/root/steps-root.d.ts +76 -0
- package/dist/steps/root/steps-root.js +93 -1
- package/dist/steps/step/step.d.ts +45 -2
- package/dist/steps/step/step.js +33 -6
- package/dist/switch/root/switch-root.d.ts +10 -0
- package/dist/table/column/table-column.d.ts +11 -10
- package/dist/table/column/table-column.js +8 -19
- package/dist/table/root/table-root.d.ts +126 -4
- package/dist/table/root/table-root.js +156 -6
- 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 +22 -5
- package/dist/tabs/panel/tab-panel.js +34 -6
- package/dist/tabs/root/tabs-root.d.ts +37 -1
- package/dist/tabs/root/tabs-root.js +22 -4
- package/dist/tabs/tab/tab.d.ts +41 -4
- package/dist/tabs/tab/tab.js +49 -3
- package/dist/tag/group/tag-group.d.ts +31 -4
- package/dist/tag/group/tag-group.js +46 -7
- package/dist/tag/root/tag-root.js +9 -2
- package/dist/textarea/root/textarea-root.d.ts +28 -1
- package/dist/textarea/root/textarea-root.js +40 -1
- package/dist/time-input/root/time-input-root.d.ts +5 -1
- package/dist/time-input/root/time-input-root.js +16 -8
- package/dist/time-picker/root/time-picker-root.d.ts +18 -4
- package/dist/time-picker/root/time-picker-root.js +7 -10
- package/dist/time-picker/wheel-name.d.ts +9 -0
- package/dist/time-picker/wheel-name.js +15 -0
- 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/item/toast-item.d.ts +24 -18
- package/dist/toast/item/toast-item.js +36 -40
- package/dist/toast/root/toast-root.d.ts +6 -0
- package/dist/toast/root/toast-root.js +16 -1
- package/dist/toggle/group/toggle-group.d.ts +24 -3
- package/dist/toggle/group/toggle-group.js +34 -5
- package/dist/toolbar/root/toolbar-root.d.ts +13 -0
- package/dist/toolbar/root/toolbar-root.js +15 -5
- package/dist/tooltip/root/tooltip-root.d.ts +13 -2
- package/dist/tooltip/root/tooltip-root.js +9 -1
- package/dist/tour/back.d.ts +10 -0
- package/dist/tour/back.js +8 -0
- package/dist/tour/root/tour-root.d.ts +30 -2
- package/dist/tour/root/tour-root.js +34 -8
- package/dist/tree/item/tree-item.d.ts +61 -4
- package/dist/tree/item/tree-item.js +92 -20
- package/dist/tree/root/tree-root.d.ts +58 -0
- package/dist/tree/root/tree-root.js +80 -16
- package/dist/tree/selection.d.ts +24 -0
- package/dist/tree/selection.js +13 -0
- package/dist/trend/root/trend-root.d.ts +43 -18
- package/dist/trend/root/trend-root.js +31 -6
- package/dist/video/media-events.d.ts +44 -0
- package/dist/video/media-events.js +35 -0
- package/dist/video/root/video-root.d.ts +117 -18
- package/dist/video/root/video-root.js +93 -47
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +5 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +3 -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 +77 -3
- 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 +22 -0
- package/dist/virtual-list/fill.js +23 -0
- package/dist/virtual-list/keys.d.ts +15 -0
- package/dist/virtual-list/keys.js +24 -0
- package/dist/virtual-list/plan.d.ts +49 -0
- package/dist/virtual-list/plan.js +16 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +259 -26
- package/dist/virtual-list/root/virtual-list-root.js +333 -49
- package/lint.json +1 -1
- package/package.json +8 -8
- package/types/jsx.d.ts +8113 -1394
- package/types/svelte.d.ts +2 -0
- package/types/vue.d.ts +1678 -353
- package/dist/data-table/root/order.d.ts +0 -141
- package/dist/data-table/root/order.js +0 -76
- package/dist/listbox/range.d.ts +0 -17
- package/dist/listbox/range.js +0 -5
- package/dist/tag/selection.d.ts +0 -10
- package/dist/tag/selection.js +0 -12
|
@@ -10,6 +10,18 @@ import { property } from 'lit/decorators.js';
|
|
|
10
10
|
import { encode } from 'uqr';
|
|
11
11
|
import { qrSvg } from '../export.js';
|
|
12
12
|
const QUIET = 4;
|
|
13
|
+
const pixelsOf = (image) => {
|
|
14
|
+
try {
|
|
15
|
+
const canvas = document.createElement('canvas');
|
|
16
|
+
canvas.width = image.naturalWidth;
|
|
17
|
+
canvas.height = image.naturalHeight;
|
|
18
|
+
canvas.getContext('2d')?.drawImage(image, 0, 0);
|
|
19
|
+
return canvas.toDataURL('image/png');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return image.currentSrc || image.src;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
13
25
|
const isFinder = (x, y, size) => {
|
|
14
26
|
const near = (a, b) => a < 8 && b < 8;
|
|
15
27
|
return near(x, y) || near(size - 1 - x, y) || near(x, size - 1 - y);
|
|
@@ -96,7 +108,9 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
96
108
|
updated(changed) {
|
|
97
109
|
super.updated(changed);
|
|
98
110
|
const key = `${this.level}:${this.value}`;
|
|
99
|
-
|
|
111
|
+
const overflow = this.value !== '' && this.encoded() === undefined;
|
|
112
|
+
this.toggleState('overflow', overflow);
|
|
113
|
+
if (overflow) {
|
|
100
114
|
if (this.overflowed !== key) {
|
|
101
115
|
this.overflowed = key;
|
|
102
116
|
this.dispatchEvent(new Event('error', { bubbles: false, cancelable: false, composed: false }));
|
|
@@ -115,16 +129,53 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
115
129
|
}
|
|
116
130
|
const ground = getComputedStyle(this).backgroundColor;
|
|
117
131
|
const clear = ground === '' || ground === 'transparent' || /^rgba\(.*,\s*0\)$/.test(ground);
|
|
132
|
+
const background = clear ? '#ffffff' : rgbOf(ground);
|
|
133
|
+
const box = Number(code.getAttribute('viewBox')?.split(' ')[2] ?? 0);
|
|
134
|
+
const logo = this.logoDrawing(code, box, background);
|
|
118
135
|
return qrSvg({
|
|
119
|
-
background
|
|
120
|
-
box
|
|
136
|
+
background,
|
|
137
|
+
box,
|
|
121
138
|
finderColor: rgbOf(getComputedStyle(finders).fill || '#000000'),
|
|
122
139
|
finders: finders.getAttribute('d') ?? '',
|
|
123
140
|
moduleColor: rgbOf(getComputedStyle(modules).fill || '#000000'),
|
|
124
141
|
modules: modules.getAttribute('d') ?? '',
|
|
125
142
|
quiet: QUIET,
|
|
143
|
+
...(logo ? { logo } : {}),
|
|
126
144
|
}, size);
|
|
127
145
|
}
|
|
146
|
+
logoDrawing(code, box, background) {
|
|
147
|
+
const holder = this.renderRoot.querySelector('[part="logo"]');
|
|
148
|
+
const mark = this.knownChildren.find((child) => child.slot === 'logo');
|
|
149
|
+
const area = code.getBoundingClientRect();
|
|
150
|
+
if (!holder || holder.hidden || !mark || area.width === 0) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const scale = box / area.width;
|
|
154
|
+
const at = (rect) => [
|
|
155
|
+
(rect.left - area.left) * scale,
|
|
156
|
+
(rect.top - area.top) * scale,
|
|
157
|
+
rect.width * scale,
|
|
158
|
+
rect.height * scale,
|
|
159
|
+
];
|
|
160
|
+
const frame = at(holder.getBoundingClientRect());
|
|
161
|
+
if (frame[0] < 0 || frame[1] < 0 || frame[0] + frame[2] > box || frame[1] + frame[3] > box) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
const fill = getComputedStyle(holder).backgroundColor;
|
|
165
|
+
const ground = fill === '' || fill === 'transparent' || /^rgba\(.*,\s*0\)$/.test(fill);
|
|
166
|
+
const logo = {
|
|
167
|
+
frame,
|
|
168
|
+
ground: ground ? background : rgbOf(fill),
|
|
169
|
+
mark: at(mark.getBoundingClientRect()),
|
|
170
|
+
};
|
|
171
|
+
if (mark instanceof HTMLImageElement) {
|
|
172
|
+
return { ...logo, href: pixelsOf(mark) };
|
|
173
|
+
}
|
|
174
|
+
if (mark instanceof SVGSVGElement) {
|
|
175
|
+
return { ...logo, svg: new XMLSerializer().serializeToString(mark) };
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
128
179
|
async toPng(size = 512) {
|
|
129
180
|
const markup = this.toSvg(size);
|
|
130
181
|
if (markup === '') {
|
|
@@ -151,11 +202,7 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
151
202
|
link.click();
|
|
152
203
|
setTimeout(() => URL.revokeObjectURL(url));
|
|
153
204
|
}
|
|
154
|
-
renderCode() {
|
|
155
|
-
if (this.value === '') {
|
|
156
|
-
return nothing;
|
|
157
|
-
}
|
|
158
|
-
const result = this.encoded();
|
|
205
|
+
renderCode(result) {
|
|
159
206
|
if (result === undefined) {
|
|
160
207
|
return nothing;
|
|
161
208
|
}
|
|
@@ -186,7 +233,9 @@ let HelixHeadlessQrCode = class HelixHeadlessQrCode extends HelixElement {
|
|
|
186
233
|
</svg>`;
|
|
187
234
|
}
|
|
188
235
|
render() {
|
|
189
|
-
|
|
236
|
+
const result = this.value === '' ? undefined : this.encoded();
|
|
237
|
+
const overflow = this.value !== '' && result === undefined;
|
|
238
|
+
return html `${this.renderCode(result)}${overflow ? html `<span part="fallback"><slot name="fallback"></slot></span>` : nothing}
|
|
190
239
|
<span ?hidden=${!this.logo} part="logo">
|
|
191
240
|
<slot
|
|
192
241
|
name="logo"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether quoted text starts or ends with a quotation mark someone typed.
|
|
3
|
+
*
|
|
4
|
+
* A Quote renders a `<q>`, which sets the marks itself, so typed ones show
|
|
5
|
+
* twice: “"like this"”. Only the ends are read, since a mark inside the text
|
|
6
|
+
* is a quotation within it, which a nested Quote should set.
|
|
7
|
+
*/
|
|
8
|
+
export declare const typedMarks: (text: string) => boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
-
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
4
|
* @summary A short quotation, set inline with the text around it.
|
|
5
5
|
*
|
|
@@ -7,7 +7,10 @@ import { type TemplateResult } from 'lit';
|
|
|
7
7
|
* Renders a real `<q>`, so the browser supplies the quotation marks. It inserts
|
|
8
8
|
* the correct marks for the document's language and nests them correctly when
|
|
9
9
|
* one quotation sits inside another. Do not type quotation marks into the
|
|
10
|
-
* content
|
|
10
|
+
* content: one that starts or ends with a typed mark warns once, with the
|
|
11
|
+
* runtime kind `typed-marks`. A quotation in a language other than the page's takes `lang` on the
|
|
12
|
+
* element, and its marks follow it: `lang="de"` sets „…“ and `lang="fr"` «…»
|
|
13
|
+
* inside an English page.
|
|
11
14
|
*
|
|
12
15
|
* For a passage set off from the surrounding text, use a blockquote.
|
|
13
16
|
*
|
|
@@ -26,6 +29,15 @@ export declare class HelixHeadlessQuote extends HelixElement {
|
|
|
26
29
|
* URL identifying where the quotation came from.
|
|
27
30
|
*/
|
|
28
31
|
cite: string;
|
|
32
|
+
/** Whether this quote has said its text carries typed marks, so it says so once. */
|
|
33
|
+
private warned;
|
|
34
|
+
/**
|
|
35
|
+
* Reads the quoted text as it first renders and as it changes, for marks the
|
|
36
|
+
* `<q>` would double. On the shadow root rather than in the template, so the
|
|
37
|
+
* served markup is the `<q>` and its slot and nothing else.
|
|
38
|
+
*/
|
|
39
|
+
private readonly readText;
|
|
40
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
29
41
|
protected render(): TemplateResult;
|
|
30
42
|
}
|
|
31
43
|
declare global {
|
|
@@ -4,13 +4,35 @@ 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, reportRuntime } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { typedMarks } from '../marks.js';
|
|
10
11
|
let HelixHeadlessQuote = class HelixHeadlessQuote extends HelixElement {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
13
14
|
this.cite = '';
|
|
15
|
+
this.warned = false;
|
|
16
|
+
this.readText = () => {
|
|
17
|
+
const text = [...this.childNodes]
|
|
18
|
+
.filter((node) => !(node instanceof Element) || !node.slot)
|
|
19
|
+
.map((node) => node.textContent ?? '')
|
|
20
|
+
.join('');
|
|
21
|
+
if (this.warned || !typedMarks(text)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.warned = true;
|
|
25
|
+
reportRuntime({
|
|
26
|
+
kind: 'typed-marks',
|
|
27
|
+
message: `<${this.localName}> starts or ends with a typed quotation mark, and the browser sets its own, so the marks show twice. Take the typed marks out.`,
|
|
28
|
+
tag: this.localName,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
firstUpdated(changed) {
|
|
33
|
+
super.firstUpdated(changed);
|
|
34
|
+
this.readText();
|
|
35
|
+
this.renderRoot.addEventListener('slotchange', this.readText);
|
|
14
36
|
}
|
|
15
37
|
render() {
|
|
16
38
|
return html `<q part="quote" cite=${this.cite || nothing}><slot></slot></q>`;
|
|
@@ -80,18 +80,12 @@ export declare class HelixHeadlessRadioGroup extends HelixHeadlessRadioGroup_bas
|
|
|
80
80
|
* Whether a reader can see and focus the answer and cannot change it. A
|
|
81
81
|
* read-only group still submits its value.
|
|
82
82
|
*
|
|
83
|
-
* React Aria's `isReadOnly`. A press on a radio is refused
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* A script can still set `value`.
|
|
83
|
+
* React Aria's `isReadOnly`. A press on a radio is refused. The arrow keys,
|
|
84
|
+
* Home and End move focus between the radios and choose nothing, so a reader
|
|
85
|
+
* can hear every choice, and Tab still reaches the answer. The group says
|
|
86
|
+
* `aria-readonly`. A script can still set `value`.
|
|
87
87
|
*/
|
|
88
88
|
readOnly: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* Keeps the keys that would move the answer from the keyboard controller
|
|
91
|
-
* while the group is read-only. A capture listener on the host runs ahead of
|
|
92
|
-
* the controller's own on the same target.
|
|
93
|
-
*/
|
|
94
|
-
private readonly holdKeys;
|
|
95
89
|
protected readonly keys: KeyboardNavigationController;
|
|
96
90
|
protected readonly cascade: DisabledCascadeController;
|
|
97
91
|
/** The radios, in light-DOM order. Client only: the server has no tree. */
|
|
@@ -8,19 +8,16 @@ import { customElement, DisabledCascadeController, HelixFormElement, hostAria, K
|
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { HelixHeadlessRadio } from '../root/radio-root.js';
|
|
11
|
-
const MOVING_KEYS = new Set(['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'End', 'Home']);
|
|
12
11
|
let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
13
12
|
constructor() {
|
|
14
13
|
super(...arguments);
|
|
15
14
|
this.focusable = true;
|
|
16
15
|
this.readOnly = false;
|
|
17
|
-
this.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
};
|
|
23
|
-
this.keys = new KeyboardNavigationController(this, { items: () => this.items });
|
|
16
|
+
this.keys = new KeyboardNavigationController(this, {
|
|
17
|
+
items: () => this.items,
|
|
18
|
+
origin: () => (this.readOnly ? 'focus' : 'selection'),
|
|
19
|
+
selects: () => !this.readOnly,
|
|
20
|
+
});
|
|
24
21
|
this.cascade = new DisabledCascadeController(this, {
|
|
25
22
|
items: () => this.items,
|
|
26
23
|
});
|
|
@@ -92,12 +89,10 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
|
|
|
92
89
|
connectedCallback() {
|
|
93
90
|
super.connectedCallback();
|
|
94
91
|
this.addEventListener('helix-select', this.handleSelection);
|
|
95
|
-
this.addEventListener('keydown', this.holdKeys, { capture: true });
|
|
96
92
|
}
|
|
97
93
|
disconnectedCallback() {
|
|
98
94
|
super.disconnectedCallback();
|
|
99
95
|
this.removeEventListener('helix-select', this.handleSelection);
|
|
100
|
-
this.removeEventListener('keydown', this.holdKeys, { capture: true });
|
|
101
96
|
}
|
|
102
97
|
willUpdate(changed) {
|
|
103
98
|
super.willUpdate(changed);
|
|
@@ -29,15 +29,19 @@ declare const HelixHeadlessRadio_base: (new (...args: any[]) => import("@fusion.
|
|
|
29
29
|
* replaces the visible label, and the label becomes the name read by screen
|
|
30
30
|
* readers. The icon has its own slot so the chip can hide the label and show
|
|
31
31
|
* the icon.
|
|
32
|
+
* @slot indicator - An icon drawn inside the circle in place of the dot, such
|
|
33
|
+
* as a check mark.
|
|
32
34
|
*
|
|
33
35
|
* @event {HelixSelectEvent} helix-select - Fired when the radio is chosen. Cancelable: call `preventDefault()` to keep the current choice.
|
|
34
36
|
*
|
|
35
37
|
* @csspart box - The circle. Draw the dot inside it with `::part(box)::after` in
|
|
36
|
-
* your stylesheet
|
|
37
|
-
* the dot, which would add an empty child to
|
|
38
|
+
* your stylesheet, or slot an icon into `indicator`. This tier ships no CSS, so
|
|
39
|
+
* it renders no separate element for the dot, which would add an empty child to
|
|
40
|
+
* your layout.
|
|
38
41
|
* @csspart item - The button, which covers the whole row.
|
|
39
42
|
* @csspart label - The label beside the circle.
|
|
40
43
|
*
|
|
44
|
+
* @cssstate has-indicator - An icon is slotted into `indicator`.
|
|
41
45
|
* @cssstate selected - The radio is chosen.
|
|
42
46
|
*/
|
|
43
47
|
export declare class HelixHeadlessRadio extends HelixHeadlessRadio_base {
|
|
@@ -32,7 +32,12 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends MixinSelectableItem(He
|
|
|
32
32
|
tabindex=${(this.focusable ?? this.selected) || !this.childrenKnown ? '0' : '-1'}
|
|
33
33
|
@click=${() => this.select()}
|
|
34
34
|
>
|
|
35
|
-
<span part="box"
|
|
35
|
+
<span part="box" aria-hidden="true"
|
|
36
|
+
><slot
|
|
37
|
+
name="indicator"
|
|
38
|
+
@slotchange=${(event) => this.toggleState('has-indicator', event.target.assignedNodes().length > 0)}
|
|
39
|
+
></slot
|
|
40
|
+
></span>
|
|
36
41
|
<slot name="icon"></slot>
|
|
37
42
|
<span part="label"><slot></slot></span>
|
|
38
43
|
</button>`;
|
|
@@ -24,6 +24,7 @@ declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion
|
|
|
24
24
|
* @aliases Stars, Score, Review score
|
|
25
25
|
* @documentation https://fusion.dev/helix/components/rating
|
|
26
26
|
*
|
|
27
|
+
* @slot empty-mark - The shape of an unfilled mark, copied into every position of the track, such as an outlined star beside a filled `mark`. Without it, the track copies `mark`.
|
|
27
28
|
* @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
|
|
28
29
|
* @slot hint - Help under the control, for content richer than the `hint` text.
|
|
29
30
|
* @slot label - The label, for content richer than the `label` text.
|
|
@@ -31,7 +32,8 @@ declare const HelixHeadlessRating_base: (new (...args: any[]) => import("@fusion
|
|
|
31
32
|
*
|
|
32
33
|
* @event {Event} change - The score was chosen. Sent together with `input`, because each choice commits the score.
|
|
33
34
|
* @event {HelixBeforeChangeEvent} helix-before-change - The score is about to change. Cancelable: cancel it to keep the current score.
|
|
34
|
-
* @event {
|
|
35
|
+
* @event {HelixClearEvent} helix-clear - The rating went back to unrated, under `clearable`. Sent after `input` and `change`.
|
|
36
|
+
* @event {HelixPreviewEvent} helix-preview - The score under the pointer, shown before it is chosen, or `''` once the marks show the score again. Sent once each time the preview changes.
|
|
35
37
|
* @event {Event} input - The score changed.
|
|
36
38
|
*
|
|
37
39
|
* @csspart feedback - The message that says what is wrong.
|
|
@@ -223,6 +225,8 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
|
|
|
223
225
|
/** The shape for one place, or nothing when the stylesheet is drawing it. */
|
|
224
226
|
private markFor;
|
|
225
227
|
private readonly handleMarkSlot;
|
|
228
|
+
/** The element slotted into `empty-mark`, which the track copies. */
|
|
229
|
+
private emptySource;
|
|
226
230
|
/**
|
|
227
231
|
* The step under a point on the row, or the nearest one to it.
|
|
228
232
|
*
|
|
@@ -297,10 +301,11 @@ export declare class HelixHeadlessRating extends HelixHeadlessRating_base {
|
|
|
297
301
|
/** The name of one score, as `optionLabel` writes it. */
|
|
298
302
|
private nameFor;
|
|
299
303
|
/**
|
|
300
|
-
* The option
|
|
301
|
-
* steps, such as an average of 3.
|
|
302
|
-
* the first while nothing is chosen. Between steps no
|
|
303
|
-
* the rating left the tab
|
|
304
|
+
* The option that is checked and holds the tab stop: the chosen one, or for
|
|
305
|
+
* a score between steps, such as an average of 3.4 on whole marks, the
|
|
306
|
+
* nearest step, and the first while nothing is chosen. Between steps no
|
|
307
|
+
* option matched, so no option was checked and the rating left the tab
|
|
308
|
+
* order.
|
|
304
309
|
*/
|
|
305
310
|
private stopAt;
|
|
306
311
|
protected render(): TemplateResult;
|
|
@@ -4,9 +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, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, valueAsNumber as asNumber, customElement, fillTemplate, numberAsValue as fromNumber, HelixClearEvent, HelixFormElement, HelixPreviewEvent, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
import { nearestStep } from '../steps.js';
|
|
10
11
|
let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue(MixinField(HelixFormElement)) {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
@@ -33,9 +34,17 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
33
34
|
this.dragging = undefined;
|
|
34
35
|
this.clones = new Map();
|
|
35
36
|
this.handleMarkSlot = (event) => {
|
|
37
|
+
const slot = event.target;
|
|
38
|
+
const source = slot.assignedElements({ flatten: true })[0];
|
|
36
39
|
this.clones.clear();
|
|
37
|
-
|
|
40
|
+
if (slot.name === 'empty-mark') {
|
|
41
|
+
this.emptySource = source;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.markSource = source;
|
|
45
|
+
}
|
|
38
46
|
};
|
|
47
|
+
this.emptySource = undefined;
|
|
39
48
|
this.handleDragStart = (event) => {
|
|
40
49
|
if (this.isDisabled || this.readOnly || event.button !== 0) {
|
|
41
50
|
return;
|
|
@@ -158,7 +167,8 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
158
167
|
}));
|
|
159
168
|
}
|
|
160
169
|
markFor(layer, index) {
|
|
161
|
-
|
|
170
|
+
const source = layer === 'track' ? (this.emptySource ?? this.markSource) : this.markSource;
|
|
171
|
+
if (!source) {
|
|
162
172
|
return undefined;
|
|
163
173
|
}
|
|
164
174
|
const key = `${layer}:${index}`;
|
|
@@ -166,7 +176,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
166
176
|
if (held) {
|
|
167
177
|
return held;
|
|
168
178
|
}
|
|
169
|
-
const clone =
|
|
179
|
+
const clone = source.cloneNode(true);
|
|
170
180
|
this.clones.set(key, clone);
|
|
171
181
|
return clone;
|
|
172
182
|
}
|
|
@@ -195,7 +205,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
195
205
|
}
|
|
196
206
|
clear() {
|
|
197
207
|
if (this.value !== '' && !this.readOnly && !this.isDisabled && this.requestValue('')) {
|
|
198
|
-
this.dispatchEvent(new
|
|
208
|
+
this.dispatchEvent(new HelixClearEvent());
|
|
199
209
|
}
|
|
200
210
|
}
|
|
201
211
|
connectedCallback() {
|
|
@@ -208,6 +218,11 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
208
218
|
}
|
|
209
219
|
updated(changed) {
|
|
210
220
|
super.updated?.(changed);
|
|
221
|
+
if (changed.has('previewing')) {
|
|
222
|
+
this.dispatchEvent(new HelixPreviewEvent({
|
|
223
|
+
value: this.previewing === undefined ? '' : String(this.previewing),
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
211
226
|
this.sync();
|
|
212
227
|
if (this.floor !== this.min && !this.warned) {
|
|
213
228
|
this.warned = true;
|
|
@@ -225,6 +240,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
225
240
|
if (!this.markRead && this.childrenKnown) {
|
|
226
241
|
this.markRead = true;
|
|
227
242
|
this.markSource = this.knownChildren.find((child) => child.slot === 'mark');
|
|
243
|
+
this.emptySource = this.knownChildren.find((child) => child.slot === 'empty-mark');
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
firstUpdated(changed) {
|
|
@@ -239,8 +255,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
239
255
|
if (!answered) {
|
|
240
256
|
return first;
|
|
241
257
|
}
|
|
242
|
-
|
|
243
|
-
return below.at(-1) ?? first;
|
|
258
|
+
return nearestStep(this.steps, this.score) ?? first;
|
|
244
259
|
}
|
|
245
260
|
render() {
|
|
246
261
|
const showing = this.previewing ?? this.score;
|
|
@@ -276,9 +291,15 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
276
291
|
hidden
|
|
277
292
|
@slotchange=${this.handleMarkSlot}
|
|
278
293
|
></slot>
|
|
294
|
+
<slot
|
|
295
|
+
name="empty-mark"
|
|
296
|
+
hidden
|
|
297
|
+
@slotchange=${this.handleMarkSlot}
|
|
298
|
+
></slot>
|
|
279
299
|
<span
|
|
280
300
|
part="track"
|
|
281
301
|
aria-hidden="true"
|
|
302
|
+
data-slotted=${this.emptySource ? '' : nothing}
|
|
282
303
|
>${marksIn('track')}</span
|
|
283
304
|
>
|
|
284
305
|
<span
|
|
@@ -295,7 +316,7 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
|
|
|
295
316
|
part="option"
|
|
296
317
|
role="radio"
|
|
297
318
|
type="button"
|
|
298
|
-
aria-checked=${answered &&
|
|
319
|
+
aria-checked=${answered && score === stop ? 'true' : 'false'}
|
|
299
320
|
aria-label=${this.nameFor(score)}
|
|
300
321
|
data-score=${String(score)}
|
|
301
322
|
style=${`--rating-step: ${index + 1}`}
|
|
@@ -352,6 +373,9 @@ __decorate([
|
|
|
352
373
|
__decorate([
|
|
353
374
|
state()
|
|
354
375
|
], HelixHeadlessRating.prototype, "previewing", void 0);
|
|
376
|
+
__decorate([
|
|
377
|
+
state()
|
|
378
|
+
], HelixHeadlessRating.prototype, "emptySource", void 0);
|
|
355
379
|
HelixHeadlessRating = __decorate([
|
|
356
380
|
customElement('helix-headless-rating')
|
|
357
381
|
], HelixHeadlessRating);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step a score stands at: the step itself, or for a score between steps,
|
|
3
|
+
* such as an average of 3.4 on whole marks, the nearest one. Halfway between
|
|
4
|
+
* two goes to the lower, so a score is never read as more than it is.
|
|
5
|
+
* `undefined` for a row with no steps.
|
|
6
|
+
*/
|
|
7
|
+
export declare const nearestStep: (steps: readonly number[], score: number) => number | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Pearson's r, or nothing where there are too few points or no spread to have one. */
|
|
2
|
+
export declare const correlation: (pairs: ReadonlyArray<readonly [number, number]>) => number | undefined;
|
|
3
|
+
/** A straight line, as the measure up at any measure across. */
|
|
4
|
+
export interface Fit {
|
|
5
|
+
/** The measure up where the measure across is zero. */
|
|
6
|
+
intercept: number;
|
|
7
|
+
/** How far up the line goes for each step across. */
|
|
8
|
+
slope: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The straight line nearest the points, by least squares, or nothing where
|
|
12
|
+
* there are fewer than two points or they share one measure across, which no
|
|
13
|
+
* line up the page can be fitted to.
|
|
14
|
+
*/
|
|
15
|
+
export declare const leastSquares: (pairs: ReadonlyArray<readonly [number, number]>) => Fit | undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const mean = (values) => values.reduce((sum, one) => sum + one, 0) / values.length;
|
|
2
|
+
export const correlation = (pairs) => {
|
|
3
|
+
if (pairs.length < 3) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const across = mean(pairs.map(([x]) => x));
|
|
7
|
+
const up = mean(pairs.map(([, y]) => y));
|
|
8
|
+
let both = 0;
|
|
9
|
+
let xs = 0;
|
|
10
|
+
let ys = 0;
|
|
11
|
+
for (const [x, y] of pairs) {
|
|
12
|
+
both += (x - across) * (y - up);
|
|
13
|
+
xs += (x - across) ** 2;
|
|
14
|
+
ys += (y - up) ** 2;
|
|
15
|
+
}
|
|
16
|
+
return xs === 0 || ys === 0 ? undefined : both / Math.sqrt(xs * ys);
|
|
17
|
+
};
|
|
18
|
+
export const leastSquares = (pairs) => {
|
|
19
|
+
if (pairs.length < 2) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const across = mean(pairs.map(([x]) => x));
|
|
23
|
+
const up = mean(pairs.map(([, y]) => y));
|
|
24
|
+
let both = 0;
|
|
25
|
+
let xs = 0;
|
|
26
|
+
for (const [x, y] of pairs) {
|
|
27
|
+
both += (x - across) * (y - up);
|
|
28
|
+
xs += (x - across) ** 2;
|
|
29
|
+
}
|
|
30
|
+
if (xs === 0) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const slope = both / xs;
|
|
34
|
+
return { intercept: up - slope * across, slope };
|
|
35
|
+
};
|
|
@@ -32,6 +32,9 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
32
32
|
* Both axes are labeled, because both positions are values. The ticks sit at
|
|
33
33
|
* round numbers, and the range extends to them.
|
|
34
34
|
*
|
|
35
|
+
* `trend` draws each series' line of best fit, by least squares, across the
|
|
36
|
+
* plot in the series' color.
|
|
37
|
+
*
|
|
35
38
|
* The summary gives each series' correlation coefficient and its number of
|
|
36
39
|
* points. The table has every number, and the drawing is hidden from screen
|
|
37
40
|
* readers.
|
|
@@ -66,7 +69,7 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
66
69
|
* @csspart point - One row, as a point.
|
|
67
70
|
* @csspart point-active - A point in the row under the pointer. Set alongside `point`.
|
|
68
71
|
* @csspart summary - The text that says how strongly the measures relate, for people who cannot see the points.
|
|
69
|
-
* @csspart svg - The drawing surface the gridlines sit on.
|
|
72
|
+
* @csspart svg - The drawing surface the gridlines and the trend lines sit on.
|
|
70
73
|
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
71
74
|
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
72
75
|
* @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
|
|
@@ -83,6 +86,7 @@ declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...arg
|
|
|
83
86
|
* @csspart tooltip-right - The bubble while it is right of the reading. Set alongside `tooltip`.
|
|
84
87
|
* @csspart tooltip-top - The bubble while it is above the reading. Set alongside `tooltip`.
|
|
85
88
|
* @csspart tooltip-value - A column's value inside the tooltip.
|
|
89
|
+
* @csspart trend - One series' line of best fit, under `trend`. Carries its series color as `--chart-series`.
|
|
86
90
|
* @csspart x-axis - The values along the horizontal axis, under the plot.
|
|
87
91
|
* @csspart x-name - The name of the horizontal measure.
|
|
88
92
|
* @csspart x-tick - One labeled value on the horizontal axis. Each axis has its own tick part, because `::part()` cannot tell which axis a tick is on.
|
|
@@ -112,6 +116,16 @@ export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart
|
|
|
112
116
|
* The word the summary uses for a row when it counts them.
|
|
113
117
|
*/
|
|
114
118
|
pointsLabel: string;
|
|
119
|
+
/**
|
|
120
|
+
* Draws each series' line of best fit across the plot, by least squares, in
|
|
121
|
+
* the series' color: the regression line a reader would otherwise draw by
|
|
122
|
+
* eye.
|
|
123
|
+
*
|
|
124
|
+
* Absent draws none. A series with fewer than two points, or with every
|
|
125
|
+
* point at one measure across, has no line to draw. The summary already
|
|
126
|
+
* gives each series' correlation, which says how near the points are to it.
|
|
127
|
+
*/
|
|
128
|
+
trend: boolean;
|
|
115
129
|
/** The points as last drawn, which the pointer searches. */
|
|
116
130
|
private drawn;
|
|
117
131
|
/** The point a pointer is resting nearest, which the tooltip sits on. */
|
|
@@ -150,6 +164,12 @@ export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart
|
|
|
150
164
|
protected render(): TemplateResult;
|
|
151
165
|
/** The rules and the points, behind a seam the styled tier can move. */
|
|
152
166
|
protected renderPlot(field: ScatterField, along: (value: number) => number, up: (value: number) => number): TemplateResult;
|
|
167
|
+
/**
|
|
168
|
+
* Each shown series' line of best fit, from one end of the measure across
|
|
169
|
+
* to the other, under `trend`. The drawing clips a line that leaves the
|
|
170
|
+
* plot.
|
|
171
|
+
*/
|
|
172
|
+
protected renderTrends(field: ScatterField, along: (value: number) => number, up: (value: number) => number): TemplateResult[] | typeof nothing;
|
|
153
173
|
/**
|
|
154
174
|
* What a reader who cannot see the points is told.
|
|
155
175
|
*
|