@fusion.dev/helix-headless 0.23.0 → 0.24.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 +48376 -26638
- package/dist/announcement/root/announcement-root.d.ts +19 -5
- package/dist/area-chart/index.d.ts +1 -0
- package/dist/area-chart/index.js +1 -0
- package/dist/area-chart/root/area-chart-root.d.ts +182 -0
- package/dist/area-chart/root/area-chart-root.js +205 -0
- package/dist/carousel/root/carousel-root.d.ts +1 -0
- package/dist/carousel/root/carousel-root.js +18 -2
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/message/chat-message.d.ts +7 -0
- package/dist/chat/message/chat-message.js +2 -1
- package/dist/chat/root/chat-root.d.ts +195 -4
- package/dist/chat/root/chat-root.js +316 -23
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
- package/dist/checkbox-group/root/checkbox-group-root.js +12 -2
- package/dist/code-block/index.d.ts +1 -0
- package/dist/code-block/index.js +1 -0
- package/dist/code-block/patch.d.ts +62 -0
- package/dist/code-block/patch.js +93 -0
- package/dist/code-block/root/code-block-root.d.ts +82 -17
- package/dist/code-block/root/code-block-root.js +83 -6
- package/dist/color-picker/root/color-picker-root.d.ts +4 -3
- package/dist/color-picker/root/color-picker-root.js +4 -1
- package/dist/data-table/root/data-table-root.d.ts +195 -23
- package/dist/data-table/root/data-table-root.js +308 -27
- package/dist/description-list/index.d.ts +2 -0
- package/dist/description-list/index.js +2 -0
- package/dist/description-list/item/description-list-item.d.ts +39 -0
- package/dist/description-list/item/description-list-item.js +26 -0
- package/dist/description-list/root/description-list-root.d.ts +56 -0
- package/dist/description-list/root/description-list-root.js +21 -0
- package/dist/element-events.generated.js +27 -6
- package/dist/filter-input/chip/filter-chip.d.ts +114 -0
- package/dist/filter-input/chip/filter-chip.js +61 -0
- package/dist/filter-input/field/filter-field.d.ts +128 -0
- package/dist/filter-input/field/filter-field.js +70 -0
- package/dist/filter-input/grammar.d.ts +96 -0
- package/dist/filter-input/grammar.js +133 -0
- package/dist/filter-input/index.d.ts +5 -0
- package/dist/filter-input/index.js +5 -0
- package/dist/filter-input/menu/filter-menu.d.ts +153 -0
- package/dist/filter-input/menu/filter-menu.js +164 -0
- package/dist/filter-input/operator/filter-operator.d.ts +105 -0
- package/dist/filter-input/operator/filter-operator.js +39 -0
- package/dist/filter-input/root/filter-input-root.d.ts +118 -9
- package/dist/filter-input/root/filter-input-root.js +96 -2
- package/dist/format-date/root/format-date-root.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/line-chart/root/line-chart-root.d.ts +9 -2
- package/dist/listbox/root/listbox-root.d.ts +8 -7
- package/dist/listbox/root/listbox-root.js +7 -2
- package/dist/menu/item/menu-item.d.ts +31 -6
- package/dist/menu/item/menu-item.js +18 -5
- package/dist/menu/root/menu-root.js +7 -2
- package/dist/notifications/group/notification-group.d.ts +129 -0
- package/dist/notifications/group/notification-group.js +106 -0
- package/dist/notifications/index.d.ts +1 -0
- package/dist/notifications/index.js +1 -0
- package/dist/notifications/notification/notification.d.ts +39 -13
- package/dist/notifications/notification/notification.js +11 -8
- package/dist/notifications/root/notifications-root.d.ts +57 -1
- package/dist/notifications/root/notifications-root.js +35 -4
- package/dist/number-input/root/number-input-root.d.ts +21 -1
- package/dist/number-input/root/number-input-root.js +18 -5
- package/dist/object-inspector/index.d.ts +1 -0
- package/dist/object-inspector/index.js +1 -0
- package/dist/object-inspector/root/object-inspector-root.d.ts +183 -0
- package/dist/object-inspector/root/object-inspector-root.js +348 -0
- package/dist/outline/root/outline-root.d.ts +16 -1
- package/dist/outline/root/outline-root.js +27 -9
- package/dist/radio/group/radio-group.d.ts +1 -0
- package/dist/scatter-chart/index.d.ts +1 -0
- package/dist/scatter-chart/index.js +1 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +144 -0
- package/dist/scatter-chart/root/scatter-chart-root.js +205 -0
- package/dist/skip-link/index.d.ts +1 -0
- package/dist/skip-link/index.js +1 -0
- package/dist/skip-link/root/skip-link-root.d.ts +81 -0
- package/dist/skip-link/root/skip-link-root.js +58 -0
- package/dist/speed-dial/root/speed-dial-root.d.ts +1 -0
- package/dist/speed-dial/root/speed-dial-root.js +6 -2
- package/dist/steps/root/steps-root.d.ts +1 -0
- package/dist/table/column/table-column.d.ts +79 -0
- package/dist/table/column/table-column.js +125 -5
- package/dist/table/root/table-root.d.ts +2 -0
- package/dist/table/root/table-root.js +12 -3
- package/dist/time-picker/root/time-picker-root.d.ts +9 -0
- package/dist/time-picker/root/time-picker-root.js +5 -0
- package/dist/toast/root/toast-root.d.ts +2 -0
- package/dist/tooltip/root/tooltip-root.js +1 -1
- package/dist/tour/root/tour-root.d.ts +1 -0
- package/dist/tree/item/tree-item.d.ts +7 -4
- package/dist/tree/item/tree-item.js +4 -1
- package/dist/video/root/video-root.d.ts +23 -1
- package/dist/video/root/video-root.js +42 -22
- package/dist/virtual-list/root/virtual-list-root.d.ts +10 -6
- package/dist/virtual-list/root/virtual-list-root.js +10 -4
- package/package.json +5 -5
- package/types/jsx.d.ts +4535 -346
- package/types/svelte.d.ts +11 -0
- package/types/vue.d.ts +737 -39
|
@@ -47,10 +47,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
47
47
|
const arrive = () => {
|
|
48
48
|
clearTimeout(this.landed);
|
|
49
49
|
this.landing = undefined;
|
|
50
|
-
globalThis.removeEventListener
|
|
50
|
+
globalThis.document?.removeEventListener('scrollend', arrive, true);
|
|
51
51
|
this.mark();
|
|
52
52
|
};
|
|
53
|
-
globalThis.addEventListener
|
|
53
|
+
globalThis.document?.addEventListener('scrollend', arrive, { capture: true, once: true });
|
|
54
54
|
this.landed = setTimeout(arrive, 1200);
|
|
55
55
|
if (this.behavior === 'auto') {
|
|
56
56
|
return;
|
|
@@ -97,7 +97,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
97
97
|
if (sections.length === 0) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
const
|
|
100
|
+
const scroller = this.scroller;
|
|
101
|
+
const page = scroller === this.page;
|
|
102
|
+
const origin = page ? 0 : scroller.getBoundingClientRect().top;
|
|
103
|
+
const passed = sections.filter(({ target }) => target.getBoundingClientRect().top - origin - this.offset <= 1);
|
|
101
104
|
const active = this.atEnd ? sections.at(-1) : (passed.at(-1) ?? sections[0]);
|
|
102
105
|
this.markTo(active);
|
|
103
106
|
}
|
|
@@ -117,12 +120,24 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
get atEnd() {
|
|
120
|
-
const doc =
|
|
121
|
-
if (!doc) {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
123
|
+
const doc = this.scroller;
|
|
124
124
|
return Math.ceil(doc.scrollTop + doc.clientHeight) >= doc.scrollHeight - 1;
|
|
125
125
|
}
|
|
126
|
+
get page() {
|
|
127
|
+
const doc = globalThis.document;
|
|
128
|
+
return (doc.scrollingElement ?? doc.documentElement);
|
|
129
|
+
}
|
|
130
|
+
get scroller() {
|
|
131
|
+
const doc = globalThis.document;
|
|
132
|
+
const first = this.sections[0]?.target;
|
|
133
|
+
for (let node = first?.parentElement; node && node !== doc.body && node !== doc.documentElement; node = node.parentElement) {
|
|
134
|
+
const { overflowY } = getComputedStyle(node);
|
|
135
|
+
if (/auto|scroll|overlay/.test(overflowY) && node.scrollHeight > node.clientHeight) {
|
|
136
|
+
return node;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return this.page;
|
|
140
|
+
}
|
|
126
141
|
reach() {
|
|
127
142
|
for (const { target } of this.sections) {
|
|
128
143
|
const element = target;
|
|
@@ -147,7 +162,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
147
162
|
disconnectedCallback() {
|
|
148
163
|
super.disconnectedCallback();
|
|
149
164
|
this.removeEventListener('click', this.handleClick);
|
|
150
|
-
globalThis.removeEventListener
|
|
165
|
+
globalThis.document?.removeEventListener('scroll', this.handleScroll, true);
|
|
151
166
|
if (this.frame !== 0) {
|
|
152
167
|
globalThis.cancelAnimationFrame(this.frame);
|
|
153
168
|
this.frame = 0;
|
|
@@ -162,7 +177,10 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
162
177
|
connectedCallback() {
|
|
163
178
|
super.connectedCallback();
|
|
164
179
|
this.addEventListener('click', this.handleClick);
|
|
165
|
-
globalThis.addEventListener
|
|
180
|
+
globalThis.document?.addEventListener('scroll', this.handleScroll, {
|
|
181
|
+
capture: true,
|
|
182
|
+
passive: true,
|
|
183
|
+
});
|
|
166
184
|
if (this.hasUpdated) {
|
|
167
185
|
this.watch();
|
|
168
186
|
this.resize = new ResizeObserver(() => this.indicator.place());
|
|
@@ -36,6 +36,7 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
|
|
|
36
36
|
*
|
|
37
37
|
* @event {Event} change - Announced once the value has moved.
|
|
38
38
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired when the answer changes, and cancelable.
|
|
39
|
+
* @event {HelixSelectEvent} helix-select - Fired when an answer is chosen. Dispatched by the radio and listened for here, because the group is what a consumer holds. Cancelable: refusing it takes the decision and nothing moves.
|
|
39
40
|
* @event {Event} input - Announced once the value has moved.
|
|
40
41
|
*
|
|
41
42
|
* @csspart feedback - What is wrong with it.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/scatter-chart-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/scatter-chart-root.js';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
3
|
+
import '../../tooltip/index.js';
|
|
4
|
+
/** One row drawn as a point: which row, which series, and where, in percent of the plot. */
|
|
5
|
+
export interface ScatterPoint {
|
|
6
|
+
depth: number;
|
|
7
|
+
row: number;
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
/** The table as the chart reads it: points, both ranges and what each axis is called. */
|
|
12
|
+
export interface ScatterField {
|
|
13
|
+
points: ScatterPoint[];
|
|
14
|
+
/** Each series' readings as pairs, for the summary. */
|
|
15
|
+
pairs: Array<Array<[number, number]>>;
|
|
16
|
+
x: [number, number];
|
|
17
|
+
xName: string;
|
|
18
|
+
y: [number, number];
|
|
19
|
+
yNames: string[];
|
|
20
|
+
}
|
|
21
|
+
declare const HelixHeadlessScatterChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
22
|
+
/**
|
|
23
|
+
* @summary Whether two measures move together, one point per row.
|
|
24
|
+
*
|
|
25
|
+
* @description
|
|
26
|
+
* The chart for a relation rather than a series. The table's first numeric
|
|
27
|
+
* column is measured across and every other numeric column is measured up, one
|
|
28
|
+
* point per row, so a table of countries with a column of income and a column
|
|
29
|
+
* of life expectancy draws one dot for each country. More numeric columns are
|
|
30
|
+
* more series against the same across, each in its own color.
|
|
31
|
+
*
|
|
32
|
+
* **Both axes are measured, so both are labeled.** A line chart's readings are
|
|
33
|
+
* named along the bottom and its values are read from the tooltip; here the
|
|
34
|
+
* horizontal position is itself a value, and a point with no scale under it says
|
|
35
|
+
* nothing about how far along it is. The ticks sit at round numbers and the
|
|
36
|
+
* range is widened to them, so the plot's edges are numbers a reader can name.
|
|
37
|
+
*
|
|
38
|
+
* **What a reader without the picture is told is how strongly the two relate**,
|
|
39
|
+
* because that is the question a scatter is drawn to answer and the one a table
|
|
40
|
+
* of the same numbers makes hardest to see. The summary gives each series'
|
|
41
|
+
* correlation coefficient and how many points it rests on; the table has every
|
|
42
|
+
* number, and the drawing is `aria-hidden`.
|
|
43
|
+
*
|
|
44
|
+
* **The points are elements, not shapes in the drawing.** The drawing stretches
|
|
45
|
+
* to its box, and a circle in a stretched space is an ellipse, so each point is
|
|
46
|
+
* a `<span>` placed by percentage over it, which is how `LineChart` draws its
|
|
47
|
+
* termini and for the same reason.
|
|
48
|
+
*
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
* @status experimental
|
|
51
|
+
* @category charts
|
|
52
|
+
* @aliases Scatter Plot, XY Chart, Correlation Chart, Dot Plot
|
|
53
|
+
* @documentation https://helix.fusion.dev/components/scatter-chart
|
|
54
|
+
*
|
|
55
|
+
* @csspart gridline - One rule across the plot, under `gridlines`.
|
|
56
|
+
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
57
|
+
* @csspart plot - The drawing.
|
|
58
|
+
* @csspart point - One row, as a point.
|
|
59
|
+
* @csspart summary - How strongly the measures relate, for a reader who cannot see the points.
|
|
60
|
+
* @csspart svg - The drawing surface the rules sit on.
|
|
61
|
+
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
62
|
+
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
63
|
+
* @csspart tooltip-cell - One column's name and value inside the tooltip.
|
|
64
|
+
* @csspart tooltip-label - The row's own name inside the tooltip.
|
|
65
|
+
* @csspart tooltip-name - A column's name inside the tooltip.
|
|
66
|
+
* @csspart tooltip-value - A column's value inside the tooltip.
|
|
67
|
+
* @csspart x-axis - The levels measured across, under the plot.
|
|
68
|
+
* @csspart x-name - What is measured across.
|
|
69
|
+
* @csspart x-tick - One labeled level across. A part of its own rather than a shared `tick`, because `::part()` cannot tell which axis a tick is on.
|
|
70
|
+
* @csspart y-axis - The levels measured up, beside the plot.
|
|
71
|
+
* @csspart y-name - What is measured up.
|
|
72
|
+
* @csspart y-tick - One labeled level up.
|
|
73
|
+
*
|
|
74
|
+
* @cssproperty --chart-point-x - Where a point sits across, as a percentage. Written by the component.
|
|
75
|
+
* @cssproperty --chart-point-y - Where a point sits up, as a percentage from the top. Written by the component.
|
|
76
|
+
* @cssproperty --chart-tick - Where a tick sits along its axis, as a percentage. Written by the component.
|
|
77
|
+
*/
|
|
78
|
+
export declare class HelixHeadlessScatterChart extends HelixHeadlessScatterChart_base {
|
|
79
|
+
/**
|
|
80
|
+
* What to call the strength of the relation in the summary.
|
|
81
|
+
*/
|
|
82
|
+
correlationLabel: string;
|
|
83
|
+
/**
|
|
84
|
+
* Which rules are drawn across the plot, if any.
|
|
85
|
+
*
|
|
86
|
+
* Both axes are measured, so the values name an axis rather than a role:
|
|
87
|
+
* `x` rules the levels across, `y` the levels up. A bare `gridlines`, or
|
|
88
|
+
* anything unrecognized, means `both`, since neither axis is the one a scatter
|
|
89
|
+
* is read along.
|
|
90
|
+
*/
|
|
91
|
+
gridlines?: 'both' | 'x' | 'y';
|
|
92
|
+
/**
|
|
93
|
+
* What to call a row in the summary, which counts them.
|
|
94
|
+
*/
|
|
95
|
+
pointsLabel: string;
|
|
96
|
+
/** The points as last drawn, which the pointer searches. */
|
|
97
|
+
private drawn;
|
|
98
|
+
/** The point a pointer is resting nearest, which the tooltip sits on. */
|
|
99
|
+
private nearest;
|
|
100
|
+
connectedCallback(): void;
|
|
101
|
+
disconnectedCallback(): void;
|
|
102
|
+
/** Whether the levels across are ruled. */
|
|
103
|
+
protected get rulesX(): boolean;
|
|
104
|
+
/** Whether the levels up are ruled. */
|
|
105
|
+
protected get rulesY(): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The table as points.
|
|
108
|
+
*
|
|
109
|
+
* Read from the rows rather than from `series`, because a point needs both of
|
|
110
|
+
* its numbers from the same row, and `series` numbers each column's readings
|
|
111
|
+
* on its own once a cell is blank. A row missing either number is not drawn:
|
|
112
|
+
* a point with one coordinate has nowhere to be.
|
|
113
|
+
*/
|
|
114
|
+
protected get field(): ScatterField;
|
|
115
|
+
protected render(): TemplateResult;
|
|
116
|
+
/** The rules and the points, behind a seam the styled tier can move. */
|
|
117
|
+
protected renderPlot(field: ScatterField, along: (value: number) => number, up: (value: number) => number): TemplateResult;
|
|
118
|
+
/**
|
|
119
|
+
* What a reader who cannot see the points is told.
|
|
120
|
+
*
|
|
121
|
+
* One clause per series, each naming what is measured up against what is
|
|
122
|
+
* measured across, how many rows it rests on and its correlation, which is
|
|
123
|
+
* absent where there are fewer than three points or no spread to measure.
|
|
124
|
+
*/
|
|
125
|
+
protected renderSummary(field: ScatterField): TemplateResult;
|
|
126
|
+
/** The row a pointer is resting on, over the point it is nearest. */
|
|
127
|
+
protected renderTooltip(): TemplateResult | typeof nothing;
|
|
128
|
+
/**
|
|
129
|
+
* The point a pointer is nearest, within reach.
|
|
130
|
+
*
|
|
131
|
+
* Measured in the plot's pixels rather than in percent, because the plot is
|
|
132
|
+
* rarely square and a percent across is a different distance from a percent
|
|
133
|
+
* up. A mouse or a pen only, as every chart here: a finger has no hover to
|
|
134
|
+
* rest on a point with.
|
|
135
|
+
*/
|
|
136
|
+
private readonly handleMove;
|
|
137
|
+
private readonly handleLeave;
|
|
138
|
+
}
|
|
139
|
+
declare global {
|
|
140
|
+
interface HTMLElementTagNameMap {
|
|
141
|
+
'helix-headless-scatter-chart': HelixHeadlessScatterChart;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, MixinChartSource, seriesIndex, ticks, } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html, nothing, svg } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
import '../../tooltip/index.js';
|
|
11
|
+
const WIDTH = 100;
|
|
12
|
+
const HEIGHT = 100;
|
|
13
|
+
const REACH = 24;
|
|
14
|
+
const coefficient = new Intl.NumberFormat(undefined, {
|
|
15
|
+
maximumFractionDigits: 2,
|
|
16
|
+
minimumFractionDigits: 2,
|
|
17
|
+
signDisplay: 'exceptZero',
|
|
18
|
+
});
|
|
19
|
+
const nice = ([low, high]) => {
|
|
20
|
+
if (low === high) {
|
|
21
|
+
return [low - 1, high + 1];
|
|
22
|
+
}
|
|
23
|
+
const levels = ticks([low, high]);
|
|
24
|
+
const step = levels.length > 1 ? levels[1] - levels[0] : high - low;
|
|
25
|
+
const round = (value) => Number(value.toPrecision(12));
|
|
26
|
+
return [round(Math.floor(low / step) * step), round(Math.ceil(high / step) * step)];
|
|
27
|
+
};
|
|
28
|
+
const correlation = (pairs) => {
|
|
29
|
+
if (pairs.length < 3) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const mean = (values) => values.reduce((sum, one) => sum + one, 0) / values.length;
|
|
33
|
+
const across = mean(pairs.map(([x]) => x));
|
|
34
|
+
const up = mean(pairs.map(([, y]) => y));
|
|
35
|
+
let both = 0;
|
|
36
|
+
let xs = 0;
|
|
37
|
+
let ys = 0;
|
|
38
|
+
for (const [x, y] of pairs) {
|
|
39
|
+
both += (x - across) * (y - up);
|
|
40
|
+
xs += (x - across) ** 2;
|
|
41
|
+
ys += (y - up) ** 2;
|
|
42
|
+
}
|
|
43
|
+
return xs === 0 || ys === 0 ? undefined : both / Math.sqrt(xs * ys);
|
|
44
|
+
};
|
|
45
|
+
let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinChartSource(HelixElement) {
|
|
46
|
+
constructor() {
|
|
47
|
+
super(...arguments);
|
|
48
|
+
this.correlationLabel = 'correlation';
|
|
49
|
+
this.pointsLabel = 'points';
|
|
50
|
+
this.drawn = [];
|
|
51
|
+
this.nearest = undefined;
|
|
52
|
+
this.handleMove = (event) => {
|
|
53
|
+
if (event.pointerType !== 'mouse' && event.pointerType !== 'pen') {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const box = this.renderRoot.querySelector('[part="plot"]')?.getBoundingClientRect();
|
|
57
|
+
if (!box || box.width === 0 || box.height === 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const across = event.clientX - box.left;
|
|
61
|
+
const up = event.clientY - box.top;
|
|
62
|
+
let found;
|
|
63
|
+
let best = REACH ** 2;
|
|
64
|
+
for (const point of this.drawn) {
|
|
65
|
+
const distance = ((point.x / WIDTH) * box.width - across) ** 2 + ((point.y / HEIGHT) * box.height - up) ** 2;
|
|
66
|
+
if (distance <= best) {
|
|
67
|
+
best = distance;
|
|
68
|
+
found = point;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (found !== this.nearest) {
|
|
72
|
+
this.nearest = found;
|
|
73
|
+
this.pointer.set(found?.row ?? -1);
|
|
74
|
+
this.requestUpdate();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
this.handleLeave = () => {
|
|
78
|
+
this.nearest = undefined;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
connectedCallback() {
|
|
82
|
+
super.connectedCallback();
|
|
83
|
+
this.pointer.axis = 'none';
|
|
84
|
+
this.addEventListener('pointermove', this.handleMove);
|
|
85
|
+
this.addEventListener('pointerleave', this.handleLeave);
|
|
86
|
+
}
|
|
87
|
+
disconnectedCallback() {
|
|
88
|
+
super.disconnectedCallback();
|
|
89
|
+
this.removeEventListener('pointermove', this.handleMove);
|
|
90
|
+
this.removeEventListener('pointerleave', this.handleLeave);
|
|
91
|
+
}
|
|
92
|
+
get rulesX() {
|
|
93
|
+
return this.gridlines !== undefined && this.gridlines !== 'y';
|
|
94
|
+
}
|
|
95
|
+
get rulesY() {
|
|
96
|
+
return this.gridlines !== undefined && this.gridlines !== 'x';
|
|
97
|
+
}
|
|
98
|
+
get field() {
|
|
99
|
+
const rows = this.sourceRows;
|
|
100
|
+
const width = Math.max(0, ...rows.map((row) => row.cells.length));
|
|
101
|
+
const columns = Array.from({ length: width }, (_, column) => column).filter((column) => rows.some((row) => row.cells[column]?.value !== undefined));
|
|
102
|
+
const [across, ...ups] = columns;
|
|
103
|
+
const name = (column) => column === undefined ? '' : (rows.find((row) => row.cells[column])?.cells[column]?.name ?? '');
|
|
104
|
+
const pairs = ups.map((column) => rows.flatMap((row, index) => {
|
|
105
|
+
const x = across === undefined ? undefined : row.cells[across]?.value;
|
|
106
|
+
const y = row.cells[column]?.value;
|
|
107
|
+
return x === undefined || y === undefined ? [] : [[x, y, index]];
|
|
108
|
+
}));
|
|
109
|
+
const all = pairs.flat();
|
|
110
|
+
const x = nice(extent(all.length > 0 ? all.map(([one]) => one) : [0]));
|
|
111
|
+
const y = nice(extent(all.length > 0 ? all.map(([, one]) => one) : [0]));
|
|
112
|
+
const along = linear(x, [0, WIDTH]);
|
|
113
|
+
const up = linear(y, [HEIGHT, 0]);
|
|
114
|
+
return {
|
|
115
|
+
pairs: pairs.map((series) => series.map(([one, two]) => [one, two])),
|
|
116
|
+
points: pairs.flatMap((series, depth) => series.map(([one, two, row]) => ({ depth, row, x: along(one), y: up(two) }))),
|
|
117
|
+
x,
|
|
118
|
+
xName: name(across),
|
|
119
|
+
y,
|
|
120
|
+
yNames: ups.map((column) => name(column)),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
render() {
|
|
124
|
+
const field = this.field;
|
|
125
|
+
this.drawn = field.points;
|
|
126
|
+
if (field.points.length === 0) {
|
|
127
|
+
return html `<div id="helix-chart-plot" part="plot"></div>`;
|
|
128
|
+
}
|
|
129
|
+
const format = formatNumbers('');
|
|
130
|
+
const along = linear(field.x, [0, WIDTH]);
|
|
131
|
+
const up = linear(field.y, [HEIGHT, 0]);
|
|
132
|
+
return html `${this.renderPlot(field, along, up)}
|
|
133
|
+
<div aria-hidden="true" part="x-axis">
|
|
134
|
+
${ticks(field.x).map((level) => html `<span part="x-tick" style="--chart-tick: ${along(level)}%">${format(level)}</span>`)}
|
|
135
|
+
</div>
|
|
136
|
+
<div aria-hidden="true" part="y-axis">
|
|
137
|
+
${ticks(field.y).map((level) => html `<span part="y-tick" style="--chart-tick: ${up(level)}%">${format(level)}</span>`)}
|
|
138
|
+
</div>
|
|
139
|
+
<span aria-hidden="true" part="x-name">${field.xName}</span>
|
|
140
|
+
<span aria-hidden="true" part="y-name">${field.yNames.join(', ')}</span>
|
|
141
|
+
${this.renderSummary(field)}`;
|
|
142
|
+
}
|
|
143
|
+
renderPlot(field, along, up) {
|
|
144
|
+
const at = this.pointer.at;
|
|
145
|
+
return html `<div aria-hidden="true" id="helix-chart-plot" part="plot">
|
|
146
|
+
<svg part="svg"
|
|
147
|
+
preserveAspectRatio="none"
|
|
148
|
+
viewBox="0 0 ${WIDTH} ${HEIGHT}"
|
|
149
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
150
|
+
>
|
|
151
|
+
${this.rulesX
|
|
152
|
+
? ticks(field.x).map((level) => svg `<line part="gridline" x1=${along(level)} x2=${along(level)} y1="0" y2=${HEIGHT} />`)
|
|
153
|
+
: nothing}
|
|
154
|
+
${this.rulesY
|
|
155
|
+
? ticks(field.y).map((level) => svg `<line part="gridline" x1="0" x2=${WIDTH} y1=${up(level)} y2=${up(level)} />`)
|
|
156
|
+
: nothing}
|
|
157
|
+
</svg>
|
|
158
|
+
${field.points.map((point) => {
|
|
159
|
+
const index = seriesIndex(point.depth);
|
|
160
|
+
return html `<span
|
|
161
|
+
data-series=${index}
|
|
162
|
+
?data-active=${point.row === at}
|
|
163
|
+
part="point"
|
|
164
|
+
style=${`--chart-series: var(--chart-series-${index}); --chart-point-x: ${point.x}%; --chart-point-y: ${point.y}%`}
|
|
165
|
+
></span>`;
|
|
166
|
+
})}
|
|
167
|
+
${this.renderTooltip()}
|
|
168
|
+
</div>`;
|
|
169
|
+
}
|
|
170
|
+
renderSummary(field) {
|
|
171
|
+
const clauses = field.yNames.map((name, depth) => {
|
|
172
|
+
const pairs = field.pairs[depth] ?? [];
|
|
173
|
+
const r = correlation(pairs);
|
|
174
|
+
const strength = r === undefined ? '' : `, ${this.correlationLabel} ${coefficient.format(r)}`;
|
|
175
|
+
return `${name} / ${field.xName}: ${pairs.length} ${this.pointsLabel}${strength}`;
|
|
176
|
+
});
|
|
177
|
+
const named = this.named ? `${this.named}. ` : '';
|
|
178
|
+
return html `<p part="summary">${named}${clauses.join('; ')}.</p>`;
|
|
179
|
+
}
|
|
180
|
+
renderTooltip() {
|
|
181
|
+
const at = this.pointer.at;
|
|
182
|
+
const point = this.nearest;
|
|
183
|
+
if (at < 0 || !point) {
|
|
184
|
+
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'));
|
|
185
|
+
}
|
|
186
|
+
return drawTooltip(this.sourceRows, at, this.tag('tooltip'), {
|
|
187
|
+
placement: 'top',
|
|
188
|
+
x: `${point.x}%`,
|
|
189
|
+
y: `${point.y}%`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
__decorate([
|
|
194
|
+
property({ attribute: 'correlation-label' })
|
|
195
|
+
], HelixHeadlessScatterChart.prototype, "correlationLabel", void 0);
|
|
196
|
+
__decorate([
|
|
197
|
+
property()
|
|
198
|
+
], HelixHeadlessScatterChart.prototype, "gridlines", void 0);
|
|
199
|
+
__decorate([
|
|
200
|
+
property({ attribute: 'points-label' })
|
|
201
|
+
], HelixHeadlessScatterChart.prototype, "pointsLabel", void 0);
|
|
202
|
+
HelixHeadlessScatterChart = __decorate([
|
|
203
|
+
customElement('helix-headless-scatter-chart')
|
|
204
|
+
], HelixHeadlessScatterChart);
|
|
205
|
+
export { HelixHeadlessScatterChart };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HelixHeadlessSkipLink } from './root/skip-link-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HelixHeadlessSkipLink } from './root/skip-link-root.js';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
|
+
import { type TemplateResult } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* @summary The first stop on a page, which moves the reader past everything
|
|
5
|
+
* repeated on every page.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* A keyboard reader arriving on a page tabs through the whole header before
|
|
9
|
+
* reaching anything the page is about, and does it again on the next page. This
|
|
10
|
+
* is the control that skips it: first in the document, and pointed at the
|
|
11
|
+
* content.
|
|
12
|
+
*
|
|
13
|
+
* **It moves focus, not only the scroll position.** An in-page link on its own
|
|
14
|
+
* scrolls to its target and leaves focus behind, so the next Tab goes to
|
|
15
|
+
* whatever followed the link, which is the header it was meant to skip. Pressed,
|
|
16
|
+
* this focuses the target. A target that cannot take focus, which is most of
|
|
17
|
+
* them, since `<main>` cannot, is given `tabindex="-1"` for as long as it holds
|
|
18
|
+
* focus and has it taken off again when focus leaves, so the page ends up as the
|
|
19
|
+
* consumer wrote it.
|
|
20
|
+
*
|
|
21
|
+
* **The address is left alone.** The press is handled rather than followed, so
|
|
22
|
+
* no `#main` lands in the URL and no history entry is added, which matters to a
|
|
23
|
+
* router reading the hash. The anchor is still a real anchor with a real
|
|
24
|
+
* `href`, so before any script has run the link does what a plain link does:
|
|
25
|
+
* it scrolls there and moves the point the next Tab starts from.
|
|
26
|
+
*
|
|
27
|
+
* **Hidden until focused is the styled tier's**, being a decision about paint,
|
|
28
|
+
* and on this tier the link is simply a link. A headless consumer hides
|
|
29
|
+
* `::part(link)` while the host is not `:focus-within` and shows it when it is.
|
|
30
|
+
* Hiding with `display: none` or `visibility: hidden` takes it out of the tab
|
|
31
|
+
* order as well, and a skip link nobody can reach is the most common way one is
|
|
32
|
+
* broken.
|
|
33
|
+
*
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
* @status experimental
|
|
36
|
+
* @category navigation
|
|
37
|
+
* @aliases Skip navigation, Skip to content
|
|
38
|
+
* @documentation https://helix.fusion.dev/components/skip-link
|
|
39
|
+
*
|
|
40
|
+
* @slot - The link's words. Falls back to `label`.
|
|
41
|
+
*
|
|
42
|
+
* @csspart link - The anchor, which is what takes focus.
|
|
43
|
+
*/
|
|
44
|
+
export declare class HelixHeadlessSkipLink extends HelixElement {
|
|
45
|
+
/** Focus goes to the anchor inside, so the host is never the focus target. */
|
|
46
|
+
static shadowRootOptions: ShadowRootInit;
|
|
47
|
+
/**
|
|
48
|
+
* Where the reader is sent, as an in-page fragment.
|
|
49
|
+
*
|
|
50
|
+
* `#main` by default, because that is the id the content region almost always
|
|
51
|
+
* has; a page whose content is somewhere else says so. Anything that is not a
|
|
52
|
+
* fragment naming an element on the page is left to the browser, as a plain
|
|
53
|
+
* link.
|
|
54
|
+
*/
|
|
55
|
+
href: string;
|
|
56
|
+
/**
|
|
57
|
+
* The link's words, for the common case of plain text.
|
|
58
|
+
*
|
|
59
|
+
* The fallback of the default slot, so slotted content replaces it rather than
|
|
60
|
+
* appearing beside it.
|
|
61
|
+
*/
|
|
62
|
+
label: string;
|
|
63
|
+
/** The element `href` names, looked up in the tree this link sits in. */
|
|
64
|
+
private get target();
|
|
65
|
+
/**
|
|
66
|
+
* Focuses the target, lending it a `tabindex` only if it needs one.
|
|
67
|
+
*
|
|
68
|
+
* Tried without one first, so a target that can already take focus, a heading
|
|
69
|
+
* with its own `tabindex` or a control, is never given an attribute it did
|
|
70
|
+
* not have. The loan is repaid on `blur` rather than on the next press: a
|
|
71
|
+
* `tabindex` left on `<main>` makes every click inside it focus the whole
|
|
72
|
+
* region.
|
|
73
|
+
*/
|
|
74
|
+
private readonly skip;
|
|
75
|
+
protected render(): TemplateResult;
|
|
76
|
+
}
|
|
77
|
+
declare global {
|
|
78
|
+
interface HTMLElementTagNameMap {
|
|
79
|
+
'helix-headless-skip-link': HelixHeadlessSkipLink;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { customElement, HelixElement } from '@fusion.dev/helix-core';
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { property } from 'lit/decorators.js';
|
|
10
|
+
let HelixHeadlessSkipLink = class HelixHeadlessSkipLink extends HelixElement {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.href = '#main';
|
|
14
|
+
this.label = 'Skip to content';
|
|
15
|
+
this.skip = (event) => {
|
|
16
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const target = this.target;
|
|
20
|
+
if (!target) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
target.focus();
|
|
25
|
+
if (target.getRootNode().activeElement === target) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
target.setAttribute('tabindex', '-1');
|
|
29
|
+
target.addEventListener('blur', () => target.removeAttribute('tabindex'), { once: true });
|
|
30
|
+
target.focus();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
static { this.shadowRootOptions = {
|
|
34
|
+
...HelixElement.shadowRootOptions,
|
|
35
|
+
delegatesFocus: true,
|
|
36
|
+
}; }
|
|
37
|
+
get target() {
|
|
38
|
+
if (!this.href.startsWith('#') || this.href.length < 2) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const id = decodeURIComponent(this.href.slice(1));
|
|
42
|
+
const root = this.getRootNode();
|
|
43
|
+
return (root.getElementById?.(id) ?? document.getElementById(id));
|
|
44
|
+
}
|
|
45
|
+
render() {
|
|
46
|
+
return html `<a part="link" href=${this.href} @click=${this.skip}><slot>${this.label}</slot></a>`;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
__decorate([
|
|
50
|
+
property()
|
|
51
|
+
], HelixHeadlessSkipLink.prototype, "href", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
property()
|
|
54
|
+
], HelixHeadlessSkipLink.prototype, "label", void 0);
|
|
55
|
+
HelixHeadlessSkipLink = __decorate([
|
|
56
|
+
customElement('helix-headless-skip-link')
|
|
57
|
+
], HelixHeadlessSkipLink);
|
|
58
|
+
export { HelixHeadlessSkipLink };
|
|
@@ -38,6 +38,7 @@ declare const HelixHeadlessSpeedDial_base: (new (...args: any[]) => import("@fus
|
|
|
38
38
|
*
|
|
39
39
|
* @event {HelixBeforeCloseEvent} helix-before-close - Fired before it closes, and cancelable.
|
|
40
40
|
* @event {HelixBeforeOpenEvent} helix-before-open - Fired before it opens, and cancelable.
|
|
41
|
+
* @event {HelixSelectEvent} helix-select - Fired when an action is chosen. Dispatched by the action and listened for here, and the dial closes behind it once it has been heard. Cancelable: refusing it takes the decision and the dial stays open.
|
|
41
42
|
* @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
|
|
42
43
|
*
|
|
43
44
|
* @csspart actions - The region the actions are drawn in.
|
|
@@ -29,8 +29,12 @@ let HelixHeadlessSpeedDial = class HelixHeadlessSpeedDial extends MixinControlle
|
|
|
29
29
|
this.handleTriggerClick = () => {
|
|
30
30
|
this.requestOpen(!this.open);
|
|
31
31
|
};
|
|
32
|
-
this.handleSelect = () => {
|
|
33
|
-
|
|
32
|
+
this.handleSelect = (event) => {
|
|
33
|
+
queueMicrotask(() => {
|
|
34
|
+
if (!event.defaultPrevented) {
|
|
35
|
+
this.close();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
34
38
|
};
|
|
35
39
|
this.handleKeydown = (event) => {
|
|
36
40
|
if (event.key !== 'Escape') {
|
|
@@ -26,6 +26,7 @@ declare const HelixHeadlessSteps_base: (new (...args: any[]) => import("@fusion.
|
|
|
26
26
|
*
|
|
27
27
|
* @event {HelixActivateEvent} helix-activate - Fired once the current one has moved, so the element already holds it.
|
|
28
28
|
* @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the active step changes. Cancelable: refusing it takes the decision and nothing moves.
|
|
29
|
+
* @event {HelixSelectEvent} helix-select - Fired when a step is pressed. Dispatched by the step and listened for here, because the root is what a consumer holds. Cancelable: refusing it takes the decision and nothing moves.
|
|
29
30
|
*/
|
|
30
31
|
export declare class HelixHeadlessSteps extends HelixHeadlessSteps_base {
|
|
31
32
|
/**
|