@fusion.dev/helix-core 0.143.0 → 0.145.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +10266 -4161
- package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +10 -0
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +6 -0
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +89 -0
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +102 -1
- package/dist/controllers/chart-pointer-controller/reading-step.d.ts +57 -0
- package/dist/controllers/chart-pointer-controller/reading-step.js +45 -0
- package/dist/controllers/copy-controller/copy-controller.d.ts +11 -6
- package/dist/controllers/copy-controller/copy-controller.js +11 -9
- package/dist/controllers/countdown-controller/countdown-controller.d.ts +55 -0
- package/dist/controllers/countdown-controller/countdown-controller.js +57 -0
- package/dist/controllers/delay-controller/delay-controller.d.ts +50 -0
- package/dist/controllers/delay-controller/delay-controller.js +54 -0
- package/dist/controllers/focus-scope-controller/focus-scope-controller.d.ts +56 -0
- package/dist/controllers/focus-scope-controller/focus-scope-controller.js +71 -0
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +11 -2
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +4 -1
- package/dist/controllers/open-phase-controller/open-phase-controller.d.ts +49 -0
- package/dist/controllers/open-phase-controller/open-phase-controller.js +41 -0
- package/dist/controllers/source-controller/source-controller.d.ts +16 -0
- package/dist/controllers/source-controller/source-controller.js +44 -1
- package/dist/controllers/status-controller/status-controller.d.ts +47 -0
- package/dist/controllers/status-controller/status-controller.js +34 -0
- package/dist/controllers/swipe-controller/swipe-controller.d.ts +61 -0
- package/dist/controllers/swipe-controller/swipe-controller.js +106 -0
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +68 -35
- package/dist/controllers/virtualize-controller/virtualize-controller.js +77 -10
- package/dist/events/events.d.ts +221 -0
- package/dist/events/events.js +150 -0
- package/dist/helix-element/helix-element.js +14 -1
- package/dist/helix-element/react-bridge.d.ts +9 -4
- package/dist/helix-element/react-bridge.js +14 -1
- package/dist/index.d.ts +31 -9
- package/dist/index.js +29 -7
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +7 -2
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +5 -1
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +10 -0
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +9 -0
- package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +2 -0
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +7 -3
- package/dist/mixins/mixin-field/mixin-field.d.ts +2 -0
- package/dist/mixins/mixin-field/mixin-field.js +8 -0
- package/dist/mixins/mixin-picker-editing/mixin-picker-editing.d.ts +16 -6
- package/dist/mixins/mixin-picker-editing/mixin-picker-editing.js +19 -1
- package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +5 -4
- package/dist/mixins/mixin-picker-field/mixin-picker-field.js +12 -1
- package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +67 -0
- package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +243 -0
- package/dist/utils/arrow/arrow.d.ts +15 -0
- package/dist/utils/arrow/arrow.js +2 -0
- package/dist/utils/audit-name/audit-name.d.ts +28 -0
- package/dist/utils/audit-name/audit-name.js +95 -0
- package/dist/utils/calendar/calendar.d.ts +117 -0
- package/dist/utils/calendar/calendar.js +96 -0
- package/dist/utils/chord/chord.d.ts +43 -0
- package/dist/utils/chord/chord.js +53 -0
- package/dist/utils/clock/clock.d.ts +87 -0
- package/dist/utils/clock/clock.js +68 -0
- package/dist/utils/color-name/color-name.d.ts +28 -0
- package/dist/utils/color-name/color-name.js +150 -0
- package/dist/utils/count-label/count-label.d.ts +13 -0
- package/dist/utils/count-label/count-label.js +5 -0
- package/dist/utils/css-color/css-color.d.ts +12 -0
- package/dist/utils/css-color/css-color.js +2 -2
- package/dist/utils/date-segments/date-segments.d.ts +122 -0
- package/dist/utils/date-segments/date-segments.js +206 -0
- package/dist/utils/draw-dismiss/draw-dismiss.d.ts +30 -0
- package/dist/utils/draw-dismiss/draw-dismiss.js +16 -0
- package/dist/utils/draw-icon/draw-icon.d.ts +7 -6
- package/dist/utils/draw-tooltip/draw-tooltip.d.ts +15 -21
- package/dist/utils/draw-tooltip/draw-tooltip.js +11 -5
- package/dist/utils/draw-tooltip/spoken-reading.d.ts +10 -0
- package/dist/utils/draw-tooltip/spoken-reading.js +10 -0
- package/dist/utils/follow-link/follow-link.d.ts +46 -0
- package/dist/utils/follow-link/follow-link.js +37 -0
- package/dist/utils/heading-in/heading-in.d.ts +19 -0
- package/dist/utils/heading-in/heading-in.js +14 -0
- package/dist/utils/number-format/number-format.d.ts +23 -0
- package/dist/utils/number-format/number-format.js +55 -0
- package/dist/utils/platform/platform.d.ts +23 -0
- package/dist/utils/platform/platform.js +11 -0
- package/dist/utils/range-add/range-add.d.ts +19 -0
- package/dist/utils/range-add/range-add.js +5 -0
- package/dist/utils/read-color/read-color.d.ts +14 -0
- package/dist/utils/read-color/read-color.js +13 -0
- package/dist/utils/read-series/read-series.d.ts +6 -0
- package/dist/utils/read-series/read-series.js +1 -0
- package/dist/utils/reflect-elements/reflect-elements.d.ts +37 -0
- package/dist/utils/reflect-elements/reflect-elements.js +40 -0
- package/dist/utils/relative-time/relative-time.d.ts +13 -1
- package/dist/utils/relative-time/relative-time.js +7 -4
- package/dist/utils/report-runtime/report-runtime.d.ts +5 -1
- package/dist/utils/roving-stop/roving-stop.d.ts +20 -0
- package/dist/utils/roving-stop/roving-stop.js +3 -0
- package/dist/utils/swipe/swipe.d.ts +63 -0
- package/dist/utils/swipe/swipe.js +48 -0
- package/dist/utils/tab-stops/tab-stops.d.ts +16 -0
- package/dist/utils/tab-stops/tab-stops.js +22 -0
- package/dist/utils/value-as/value-as.d.ts +20 -0
- package/dist/utils/value-as/value-as.js +16 -0
- package/dist/utils/write-time/write-time.d.ts +15 -5
- package/dist/utils/write-time/write-time.js +7 -7
- package/dist/utils/written-list/written-list.d.ts +18 -0
- package/dist/utils/written-list/written-list.js +8 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -31,6 +31,14 @@ export interface AriaForwardOptions {
|
|
|
31
31
|
named?: () => boolean;
|
|
32
32
|
/** The focusable thing in the shadow root, or null before it renders. */
|
|
33
33
|
target: () => HTMLElement | null;
|
|
34
|
+
/**
|
|
35
|
+
* How to name the control, said in a warning when it has no accessible
|
|
36
|
+
* name once it has rendered. Absent, the name is not checked.
|
|
37
|
+
*
|
|
38
|
+
* For a control named by its content or a host attribute, as a button is. A
|
|
39
|
+
* field is checked by `MixinField`, which knows its label.
|
|
40
|
+
*/
|
|
41
|
+
unnamed?: string;
|
|
34
42
|
}
|
|
35
43
|
/**
|
|
36
44
|
* Carries a host's ARIA references down to the control inside it.
|
|
@@ -93,6 +101,8 @@ export declare class AriaForwardController implements ReactiveController {
|
|
|
93
101
|
private readonly named;
|
|
94
102
|
/** What is read after the host's names. See the option. */
|
|
95
103
|
private readonly after;
|
|
104
|
+
/** How to name the control, where its name is checked. See the option. */
|
|
105
|
+
private readonly unnamed;
|
|
96
106
|
constructor(host: ReactiveControllerHost & HTMLElement, options: AriaForwardOptions);
|
|
97
107
|
/**
|
|
98
108
|
* Whether the control has a name that did not come from this controller: an
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { auditName } from '../../utils/audit-name/audit-name.js';
|
|
1
2
|
import { elementById } from '../../utils/element-by-id/element-by-id.js';
|
|
2
3
|
const shownText = (node) => {
|
|
3
4
|
if (node instanceof HTMLSlotElement) {
|
|
@@ -16,6 +17,7 @@ const RELATIONSHIPS = [
|
|
|
16
17
|
['aria-labelledby', 'ariaLabelledByElements'],
|
|
17
18
|
];
|
|
18
19
|
const STATES = [
|
|
20
|
+
'aria-current',
|
|
19
21
|
'aria-expanded',
|
|
20
22
|
'aria-haspopup',
|
|
21
23
|
'aria-controls',
|
|
@@ -36,6 +38,7 @@ export class AriaForwardController {
|
|
|
36
38
|
this.asked = options.label ?? false;
|
|
37
39
|
this.named = options.named;
|
|
38
40
|
this.after = options.labelledByAfter;
|
|
41
|
+
this.unnamed = options.unnamed;
|
|
39
42
|
host.addController(this);
|
|
40
43
|
}
|
|
41
44
|
namedByItself(target) {
|
|
@@ -120,5 +123,8 @@ export class AriaForwardController {
|
|
|
120
123
|
}
|
|
121
124
|
hostUpdated() {
|
|
122
125
|
this.forward();
|
|
126
|
+
if (this.unnamed !== undefined) {
|
|
127
|
+
auditName(this.host, this.target, this.unnamed);
|
|
128
|
+
}
|
|
123
129
|
}
|
|
124
130
|
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* The id every chart puts on its plot, and the boundary a bubble is kept inside.
|
|
4
|
+
*
|
|
5
|
+
* **A name six templates and two files have to agree on is a name that will
|
|
6
|
+
* stop agreeing**, which is `PALETTE`'s argument and it holds harder here: the
|
|
7
|
+
* failure is silent. `within` resolves by id, so a plot renamed on one side
|
|
8
|
+
* returns null, the boundary falls back to the viewport, and the bubble goes
|
|
9
|
+
* back to hanging past the drawing with nothing reported anywhere. The plot is
|
|
10
|
+
* also what takes focus, and this controller finds it by the same id.
|
|
11
|
+
*
|
|
12
|
+
* Fixed rather than generated: it is scoped to one chart's shadow root, where
|
|
13
|
+
* it is the only one. Declared here, beside the keys that read it, and
|
|
14
|
+
* re-exported from `drawTooltip`'s module, where it was first declared.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CHART_PLOT = "helix-chart-plot";
|
|
2
17
|
/** What the controller needs of its host, which is a Lit element and a count. */
|
|
3
18
|
export interface ChartPointerHost extends ReactiveControllerHost, HTMLElement {
|
|
4
19
|
/**
|
|
@@ -9,6 +24,16 @@ export interface ChartPointerHost extends ReactiveControllerHost, HTMLElement {
|
|
|
9
24
|
* Where the chart drew itself, which is where the plot is looked for.
|
|
10
25
|
*/
|
|
11
26
|
readonly renderRoot: DocumentFragment | HTMLElement;
|
|
27
|
+
/**
|
|
28
|
+
* The positions of the cells in a row that hold numbers, which the series
|
|
29
|
+
* keys move through. Absent, a reading is one series.
|
|
30
|
+
*/
|
|
31
|
+
readonly plottedColumns?: number[];
|
|
32
|
+
/**
|
|
33
|
+
* The palette slot for a cell, or `undefined` for a column no series was
|
|
34
|
+
* drawn from, such as a scatter's measure across.
|
|
35
|
+
*/
|
|
36
|
+
seriesOf?(cell: number): number | undefined;
|
|
12
37
|
}
|
|
13
38
|
/**
|
|
14
39
|
* Which reading a pointer is nearest, for a chart that draws no markers.
|
|
@@ -33,6 +58,21 @@ export interface ChartPointerHost extends ReactiveControllerHost, HTMLElement {
|
|
|
33
58
|
* lifts. The table underneath is the readable version and is what a phone
|
|
34
59
|
* should be given, so this listens for `pointermove` and ignores anything whose
|
|
35
60
|
* `pointerType` is not fine.
|
|
61
|
+
*
|
|
62
|
+
* **The keyboard moves the same reading, which is what makes it the reading
|
|
63
|
+
* and not the pointer's.** A chart's plot takes focus (`tabindex="0"` on the
|
|
64
|
+
* element carrying `CHART_PLOT`), and while it has it the arrow keys walk the
|
|
65
|
+
* readings, Home and End go to the first and the last, and the other pair of
|
|
66
|
+
* arrows walks the series within a reading. Escape puts the bubble away, as it
|
|
67
|
+
* does a tooltip. The tooltip follows because it already follows `at`, and the
|
|
68
|
+
* chart's readout, a polite live region `drawTooltip` renders, says the
|
|
69
|
+
* reading in words. It speaks only for a key: a pointer crossing a chart is a
|
|
70
|
+
* sighted reader looking, and a region speaking on every slot it crossed would
|
|
71
|
+
* talk over everything else on the page.
|
|
72
|
+
*
|
|
73
|
+
* No peer charting library ships this, which is the reason it is spelled out
|
|
74
|
+
* rather than assumed: the table beside the chart has every number, and this
|
|
75
|
+
* is for a reader who wants the picture's own order, a reading at a time.
|
|
36
76
|
*/
|
|
37
77
|
export declare class ChartPointerController implements ReactiveController {
|
|
38
78
|
private host;
|
|
@@ -40,6 +80,22 @@ export declare class ChartPointerController implements ReactiveController {
|
|
|
40
80
|
private plot;
|
|
41
81
|
/** The reading under the pointer, or -1 when the pointer is elsewhere. */
|
|
42
82
|
at: number;
|
|
83
|
+
/**
|
|
84
|
+
* How many readings stand in one column of the drawing, for a chart laid out
|
|
85
|
+
* as a grid.
|
|
86
|
+
*
|
|
87
|
+
* `1` is a row of readings, which is every chart but a heatmap. A heatmap's
|
|
88
|
+
* calendar week stands seven days to a column, so the up and down arrows move
|
|
89
|
+
* a day and the across pair moves a week, which is where the eye goes.
|
|
90
|
+
*/
|
|
91
|
+
grid: number;
|
|
92
|
+
/** Whether the reading was last moved by a key, which is when the readout speaks. */
|
|
93
|
+
keyed: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The series the keyboard is reading within a reading, as its place among
|
|
96
|
+
* the plotted columns, or -1 while the whole reading is read.
|
|
97
|
+
*/
|
|
98
|
+
series: number;
|
|
43
99
|
/**
|
|
44
100
|
* Which axis the readings are laid out along.
|
|
45
101
|
*
|
|
@@ -60,6 +116,17 @@ export declare class ChartPointerController implements ReactiveController {
|
|
|
60
116
|
constructor(host: ChartPointerHost);
|
|
61
117
|
hostConnected(): void;
|
|
62
118
|
hostDisconnected(): void;
|
|
119
|
+
/**
|
|
120
|
+
* The cell of the reading the series keys rest on, by its place in the row,
|
|
121
|
+
* or `undefined` while the whole reading is read.
|
|
122
|
+
*
|
|
123
|
+
* The tooltip marks this cell and the readout says it alone. A place in the
|
|
124
|
+
* row and not a series depth, because a row holds columns nothing was drawn
|
|
125
|
+
* from, and the bubble lists every one of them.
|
|
126
|
+
*/
|
|
127
|
+
get column(): number | undefined;
|
|
128
|
+
/** The cells a series key can rest on: every column a series was drawn from. */
|
|
129
|
+
private columns;
|
|
63
130
|
/**
|
|
64
131
|
* Tells the tooltip its anchor moved, which nothing else would.
|
|
65
132
|
*
|
|
@@ -122,6 +189,28 @@ export declare class ChartPointerController implements ReactiveController {
|
|
|
122
189
|
accepts(event: Event): boolean;
|
|
123
190
|
/** Puts the pointer on a reading, for a chart that resolves it some other way. */
|
|
124
191
|
set(at: number): void;
|
|
192
|
+
/**
|
|
193
|
+
* Whether an event came from the plot itself, which is the one element in
|
|
194
|
+
* the chart the keys are for.
|
|
195
|
+
*
|
|
196
|
+
* By the id every chart gives its plot, asked of the first node on the
|
|
197
|
+
* event's path: a key pressed in a donut's middle, which a consumer can fill
|
|
198
|
+
* with a link, is that link's and not the chart's.
|
|
199
|
+
*/
|
|
200
|
+
private fromPlot;
|
|
201
|
+
/** Puts the reading away, as Escape and leaving the plot both do. */
|
|
202
|
+
private clear;
|
|
203
|
+
private readonly key;
|
|
204
|
+
/**
|
|
205
|
+
* Shows the first reading when the plot is reached from the keyboard, so the
|
|
206
|
+
* bubble and the readout say where the reader has arrived.
|
|
207
|
+
*
|
|
208
|
+
* A pointer pressing the plot focuses it too, and is left alone: the
|
|
209
|
+
* pointer is already showing a reading, and `:focus-visible` is how the
|
|
210
|
+
* platform says which of the two arrived.
|
|
211
|
+
*/
|
|
212
|
+
private readonly arrive;
|
|
213
|
+
private readonly depart;
|
|
125
214
|
private readonly move;
|
|
126
215
|
private readonly leave;
|
|
127
216
|
}
|
|
@@ -1,9 +1,74 @@
|
|
|
1
1
|
import { isRtl } from '../../utils/is-rtl/is-rtl.js';
|
|
2
|
+
import { stepReading } from './reading-step.js';
|
|
3
|
+
export const CHART_PLOT = 'helix-chart-plot';
|
|
2
4
|
export class ChartPointerController {
|
|
3
5
|
constructor(host) {
|
|
4
6
|
this.moved = false;
|
|
5
7
|
this.at = -1;
|
|
8
|
+
this.grid = 1;
|
|
9
|
+
this.keyed = false;
|
|
10
|
+
this.series = -1;
|
|
6
11
|
this.axis = 'inline';
|
|
12
|
+
this.key = (event) => {
|
|
13
|
+
if (!this.fromPlot(event) || event.altKey || event.ctrlKey || event.metaKey) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const count = this.host.readings;
|
|
17
|
+
if (count === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (event.key === 'Escape') {
|
|
21
|
+
if (this.at >= 0) {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
this.clear();
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const next = stepReading(event.key, { at: this.at, series: this.series }, {
|
|
28
|
+
axis: this.axis,
|
|
29
|
+
count,
|
|
30
|
+
grid: this.grid,
|
|
31
|
+
rtl: isRtl(this.host),
|
|
32
|
+
series: this.columns().length,
|
|
33
|
+
});
|
|
34
|
+
if (next === undefined) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const { at, series } = next;
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
if (at === this.at && series === this.series && this.keyed) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.at = at;
|
|
43
|
+
this.series = series;
|
|
44
|
+
this.keyed = true;
|
|
45
|
+
this.moved = true;
|
|
46
|
+
this.host.requestUpdate();
|
|
47
|
+
};
|
|
48
|
+
this.arrive = (event) => {
|
|
49
|
+
if (!this.fromPlot(event) || this.at >= 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const plot = event.composedPath()[0];
|
|
53
|
+
let visible = true;
|
|
54
|
+
try {
|
|
55
|
+
visible = plot.matches(':focus-visible');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
}
|
|
59
|
+
if (!visible || this.host.readings === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.at = 0;
|
|
63
|
+
this.keyed = true;
|
|
64
|
+
this.moved = true;
|
|
65
|
+
this.host.requestUpdate();
|
|
66
|
+
};
|
|
67
|
+
this.depart = (event) => {
|
|
68
|
+
if (this.fromPlot(event) && this.keyed) {
|
|
69
|
+
this.clear();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
7
72
|
this.move = (event) => {
|
|
8
73
|
if (this.axis === 'none' || !this.accepts(event)) {
|
|
9
74
|
return;
|
|
@@ -23,12 +88,14 @@ export class ChartPointerController {
|
|
|
23
88
|
const next = Math.min(Math.max(slot, 0), count - 1);
|
|
24
89
|
if (next !== this.at) {
|
|
25
90
|
this.at = next;
|
|
91
|
+
this.keyed = false;
|
|
92
|
+
this.series = -1;
|
|
26
93
|
this.moved = true;
|
|
27
94
|
this.host.requestUpdate();
|
|
28
95
|
}
|
|
29
96
|
};
|
|
30
97
|
this.leave = () => {
|
|
31
|
-
if (this.at !== -1) {
|
|
98
|
+
if (this.at !== -1 && !this.keyed) {
|
|
32
99
|
this.at = -1;
|
|
33
100
|
this.host.requestUpdate();
|
|
34
101
|
}
|
|
@@ -39,11 +106,29 @@ export class ChartPointerController {
|
|
|
39
106
|
hostConnected() {
|
|
40
107
|
this.host.addEventListener('pointermove', this.move);
|
|
41
108
|
this.host.addEventListener('pointerleave', this.leave);
|
|
109
|
+
this.host.addEventListener('keydown', this.key);
|
|
110
|
+
this.host.addEventListener('focusin', this.arrive);
|
|
111
|
+
this.host.addEventListener('focusout', this.depart);
|
|
42
112
|
}
|
|
43
113
|
hostDisconnected() {
|
|
44
114
|
this.host.removeEventListener('pointermove', this.move);
|
|
45
115
|
this.host.removeEventListener('pointerleave', this.leave);
|
|
116
|
+
this.host.removeEventListener('keydown', this.key);
|
|
117
|
+
this.host.removeEventListener('focusin', this.arrive);
|
|
118
|
+
this.host.removeEventListener('focusout', this.depart);
|
|
46
119
|
this.at = -1;
|
|
120
|
+
this.series = -1;
|
|
121
|
+
this.keyed = false;
|
|
122
|
+
}
|
|
123
|
+
get column() {
|
|
124
|
+
return this.series < 0 ? undefined : this.columns()[this.series];
|
|
125
|
+
}
|
|
126
|
+
columns() {
|
|
127
|
+
const host = this.host;
|
|
128
|
+
const all = host.plottedColumns ?? [];
|
|
129
|
+
return host.seriesOf === undefined
|
|
130
|
+
? all
|
|
131
|
+
: all.filter((one) => host.seriesOf?.(one) !== undefined);
|
|
47
132
|
}
|
|
48
133
|
hostUpdated() {
|
|
49
134
|
if (!this.moved) {
|
|
@@ -70,8 +155,24 @@ export class ChartPointerController {
|
|
|
70
155
|
set(at) {
|
|
71
156
|
if (at !== this.at) {
|
|
72
157
|
this.at = at;
|
|
158
|
+
this.keyed = false;
|
|
73
159
|
this.moved = true;
|
|
74
160
|
this.host.requestUpdate();
|
|
75
161
|
}
|
|
76
162
|
}
|
|
163
|
+
fromPlot(event) {
|
|
164
|
+
const target = event.composedPath()[0];
|
|
165
|
+
return (target instanceof Element &&
|
|
166
|
+
target.id === CHART_PLOT &&
|
|
167
|
+
target.getRootNode() === this.host.renderRoot);
|
|
168
|
+
}
|
|
169
|
+
clear() {
|
|
170
|
+
const was = this.at;
|
|
171
|
+
this.at = -1;
|
|
172
|
+
this.series = -1;
|
|
173
|
+
this.keyed = false;
|
|
174
|
+
if (was !== -1) {
|
|
175
|
+
this.host.requestUpdate();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
77
178
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a key moves the reading of a chart, as arithmetic with no element in
|
|
3
|
+
* it.
|
|
4
|
+
*
|
|
5
|
+
* `ChartPointerController` hears the key and asks this where it goes, so the
|
|
6
|
+
* map of keys to readings is one function every chart shares, and a spec can
|
|
7
|
+
* hold it without a page.
|
|
8
|
+
*/
|
|
9
|
+
/** How the readings of a chart are laid out, which decides what each arrow does. */
|
|
10
|
+
export interface ReadingLayout {
|
|
11
|
+
/**
|
|
12
|
+
* The axis the readings run along: `inline` along the line of text, `block`
|
|
13
|
+
* down the box, as a horizontal bar chart runs its categories, and `none`
|
|
14
|
+
* for a ring or a plane, whose readings are walked in the table's order.
|
|
15
|
+
*/
|
|
16
|
+
axis: 'block' | 'inline' | 'none';
|
|
17
|
+
/** How many readings there are. */
|
|
18
|
+
count: number;
|
|
19
|
+
/** How many readings stand in one column, for a grid. `1` is a row of them. */
|
|
20
|
+
grid: number;
|
|
21
|
+
/** Whether the line of text runs right to left, which turns the inline arrows. */
|
|
22
|
+
rtl: boolean;
|
|
23
|
+
/** How many series a reading holds, which the cross pair of arrows walks. */
|
|
24
|
+
series: number;
|
|
25
|
+
}
|
|
26
|
+
/** A reading and the series within it, `-1` for none and for the whole reading. */
|
|
27
|
+
export interface ReadingPlace {
|
|
28
|
+
/** The reading, or `-1` for none. */
|
|
29
|
+
at: number;
|
|
30
|
+
/** The series within it, by its place among the plotted ones, or `-1` for all of them. */
|
|
31
|
+
series: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Which way a key moves along the readings: `1`, `-1`, or `0` for a key that
|
|
35
|
+
* moves something else.
|
|
36
|
+
*
|
|
37
|
+
* The arrows along the line on an inline chart, down the box on a block one,
|
|
38
|
+
* and a whole column across on a grid. The cross pair walks the readings too
|
|
39
|
+
* wherever a reading holds one series, so no arrow is dead.
|
|
40
|
+
*/
|
|
41
|
+
export declare const alongReadings: (key: string, layout: ReadingLayout) => number;
|
|
42
|
+
/**
|
|
43
|
+
* Which way a key moves among the series of a reading: the pair of arrows
|
|
44
|
+
* that does not walk the readings, and only where a reading holds two or more.
|
|
45
|
+
* Down the bubble's list for the arrow pointing down, or along the line.
|
|
46
|
+
*/
|
|
47
|
+
export declare const amongSeries: (key: string, layout: ReadingLayout) => number;
|
|
48
|
+
/**
|
|
49
|
+
* Where a key moves the reading, or `undefined` for a key that moves nothing,
|
|
50
|
+
* which the page keeps.
|
|
51
|
+
*
|
|
52
|
+
* Home and End go to the first and the last. From no reading, an arrow moving
|
|
53
|
+
* on starts at the first and one moving back at the last, so either arrow
|
|
54
|
+
* starts somewhere a reader expects. A series key on no reading starts on the
|
|
55
|
+
* first. Nothing walks off either end.
|
|
56
|
+
*/
|
|
57
|
+
export declare const stepReading: (key: string, from: ReadingPlace, layout: ReadingLayout) => ReadingPlace | undefined;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const vertical = (key) => (key === 'ArrowDown' ? 1 : key === 'ArrowUp' ? -1 : 0);
|
|
2
|
+
const inline = (key, rtl) => key === 'ArrowRight' ? (rtl ? -1 : 1) : key === 'ArrowLeft' ? (rtl ? 1 : -1) : 0;
|
|
3
|
+
export const alongReadings = (key, layout) => {
|
|
4
|
+
const single = layout.grid > 1 || layout.series < 2;
|
|
5
|
+
if (layout.axis === 'block') {
|
|
6
|
+
const down = vertical(key);
|
|
7
|
+
return down !== 0 || !single ? down : inline(key, layout.rtl);
|
|
8
|
+
}
|
|
9
|
+
const across = inline(key, layout.rtl);
|
|
10
|
+
if (across !== 0) {
|
|
11
|
+
return across * Math.max(1, layout.grid);
|
|
12
|
+
}
|
|
13
|
+
return single ? vertical(key) : 0;
|
|
14
|
+
};
|
|
15
|
+
export const amongSeries = (key, layout) => {
|
|
16
|
+
if (layout.grid > 1 || layout.series < 2) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
return layout.axis === 'block' ? inline(key, layout.rtl) : vertical(key);
|
|
20
|
+
};
|
|
21
|
+
export const stepReading = (key, from, layout) => {
|
|
22
|
+
const last = layout.count - 1;
|
|
23
|
+
if (last < 0) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (key === 'Home') {
|
|
27
|
+
return { at: 0, series: from.series };
|
|
28
|
+
}
|
|
29
|
+
if (key === 'End') {
|
|
30
|
+
return { at: last, series: from.series };
|
|
31
|
+
}
|
|
32
|
+
const along = alongReadings(key, layout);
|
|
33
|
+
if (along !== 0) {
|
|
34
|
+
const at = from.at < 0 ? (along > 0 ? 0 : last) : Math.min(Math.max(from.at + along, 0), last);
|
|
35
|
+
return { at, series: from.series };
|
|
36
|
+
}
|
|
37
|
+
const among = amongSeries(key, layout);
|
|
38
|
+
if (among !== 0) {
|
|
39
|
+
return {
|
|
40
|
+
at: Math.max(from.at, 0),
|
|
41
|
+
series: Math.min(Math.max(from.series + among, -1), layout.series - 1),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
};
|
|
@@ -24,10 +24,10 @@ export interface CopyOptions {
|
|
|
24
24
|
* in more places than it looks and a control that does nothing is
|
|
25
25
|
* indistinguishable from a broken one. See `write` above.
|
|
26
26
|
*
|
|
27
|
-
* **Where neither route is allowed, the
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* **Where neither route is allowed, the copy has failed**, and `failed` holds
|
|
28
|
+
* for the moment a confirmation would. A component decides whether to say so:
|
|
29
|
+
* `CopyButton` names itself with its `error-label`, where Color's chip does
|
|
30
|
+
* not flash at all.
|
|
31
31
|
*
|
|
32
32
|
* **The event is announced after the write, not before it.** It is not
|
|
33
33
|
* cancelable for the same reason: by the time anything could be said, the text
|
|
@@ -42,15 +42,20 @@ export declare class CopyController implements ReactiveController {
|
|
|
42
42
|
private readonly options;
|
|
43
43
|
/** Whether the last write succeeded and is still being confirmed. */
|
|
44
44
|
copied: boolean;
|
|
45
|
+
/** Whether the last copy put nothing on the clipboard, and is still being said. */
|
|
46
|
+
failed: boolean;
|
|
45
47
|
private timer;
|
|
46
48
|
constructor(host: ReactiveControllerHost & EventTarget, options?: CopyOptions);
|
|
47
49
|
/**
|
|
48
50
|
* Writes to the clipboard, and confirms it if the platform allowed it.
|
|
51
|
+
* Resolves to whether the text is on the clipboard.
|
|
49
52
|
*
|
|
50
53
|
* Empty text is not copied. An empty clipboard is not a thing a reader ever
|
|
51
|
-
* asks for, and confirming one is worse than doing nothing.
|
|
54
|
+
* asks for, and confirming one is worse than doing nothing. Empty text and a
|
|
55
|
+
* refused write each hold `failed` for the same moment a copy holds
|
|
56
|
+
* `copied`, for a component that says so.
|
|
52
57
|
*/
|
|
53
|
-
copy(text: string): Promise<
|
|
58
|
+
copy(text: string): Promise<boolean>;
|
|
54
59
|
hostDisconnected(): void;
|
|
55
60
|
private reset;
|
|
56
61
|
}
|
|
@@ -34,30 +34,32 @@ export class CopyController {
|
|
|
34
34
|
this.host = host;
|
|
35
35
|
this.options = options;
|
|
36
36
|
this.copied = false;
|
|
37
|
+
this.failed = false;
|
|
37
38
|
host.addController(this);
|
|
38
39
|
}
|
|
39
40
|
async copy(text) {
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (!(await write(text))) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
41
|
+
const wrote = text !== '' && (await write(text));
|
|
46
42
|
if (this.host instanceof HTMLElement && !this.host.isConnected) {
|
|
47
|
-
return;
|
|
43
|
+
return wrote;
|
|
48
44
|
}
|
|
49
|
-
this.copied =
|
|
45
|
+
this.copied = wrote;
|
|
46
|
+
this.failed = !wrote;
|
|
50
47
|
this.host.requestUpdate();
|
|
51
|
-
|
|
48
|
+
if (wrote) {
|
|
49
|
+
this.host.dispatchEvent(new HelixCopyEvent({ value: text }));
|
|
50
|
+
}
|
|
52
51
|
this.reset();
|
|
53
52
|
this.timer = setTimeout(() => {
|
|
54
53
|
this.copied = false;
|
|
54
|
+
this.failed = false;
|
|
55
55
|
this.host.requestUpdate();
|
|
56
56
|
}, this.options.after ?? 1200);
|
|
57
|
+
return wrote;
|
|
57
58
|
}
|
|
58
59
|
hostDisconnected() {
|
|
59
60
|
this.reset();
|
|
60
61
|
this.copied = false;
|
|
62
|
+
this.failed = false;
|
|
61
63
|
}
|
|
62
64
|
reset() {
|
|
63
65
|
if (this.timer !== undefined) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/** What a `CountdownController` does when its time runs out, and when it stops and starts. */
|
|
3
|
+
export interface CountdownOptions {
|
|
4
|
+
/** Runs once the time is spent. */
|
|
5
|
+
done: () => void;
|
|
6
|
+
/** Runs with `true` when the clock stops partway and `false` when it runs again. */
|
|
7
|
+
paused?: (paused: boolean) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A lifetime that pauses: a message that dismisses itself after a time, and
|
|
11
|
+
* holds that time while a reader is on it.
|
|
12
|
+
*
|
|
13
|
+
* Toast's items and Announcement each dismiss themselves after `duration`,
|
|
14
|
+
* and each stops the clock while the pointer or focus is on the message, so a
|
|
15
|
+
* reader partway through is not cut off. Resuming continues from what was
|
|
16
|
+
* left rather than starting over.
|
|
17
|
+
*
|
|
18
|
+
* **Holds are counted, not flagged.** Hover and focus stop the same clock and
|
|
19
|
+
* are independent: a pointer can leave while focus stays. Uncounted, a reader
|
|
20
|
+
* who tabs into a message and then moves the pointer off it has the clock
|
|
21
|
+
* restarted underneath them. Only the last one out starts it again.
|
|
22
|
+
*
|
|
23
|
+
* **Leaving the page pauses it**, keeping the time already spent, and drops
|
|
24
|
+
* the holds, since a pointer or focus on a detached element never leaves it.
|
|
25
|
+
* Coming back does not resume on its own: the host says whether it should,
|
|
26
|
+
* such as only while it is still showing. `DelayController` is the other wait
|
|
27
|
+
* in core, for a wait that starts over and forgets itself on leaving.
|
|
28
|
+
*/
|
|
29
|
+
export declare class CountdownController implements ReactiveController {
|
|
30
|
+
private readonly options;
|
|
31
|
+
/** When the current run began, or nothing while the clock is stopped. */
|
|
32
|
+
private startedAt;
|
|
33
|
+
/** What is left of the time, so resuming continues rather than restarts. */
|
|
34
|
+
private remaining;
|
|
35
|
+
private timer;
|
|
36
|
+
/** How many reasons there are to hold the clock. */
|
|
37
|
+
private holds;
|
|
38
|
+
constructor(host: ReactiveControllerHost, options: CountdownOptions);
|
|
39
|
+
/** Whether the clock is running now. */
|
|
40
|
+
get running(): boolean;
|
|
41
|
+
hostConnected(): void;
|
|
42
|
+
hostDisconnected(): void;
|
|
43
|
+
/** Starts the clock at `ms`. Nothing, zero or less starts nothing. */
|
|
44
|
+
start(ms: number | undefined): void;
|
|
45
|
+
/** Stops the clock and keeps the time that is left. */
|
|
46
|
+
pause(): void;
|
|
47
|
+
/** Runs the clock again from where it stopped. */
|
|
48
|
+
resume(): void;
|
|
49
|
+
/** Stops the clock where it is, as a dismissal does. */
|
|
50
|
+
cancel(): void;
|
|
51
|
+
/** One more reason to hold the clock, such as a pointer arriving. */
|
|
52
|
+
hold(): void;
|
|
53
|
+
/** One reason fewer, and the clock runs again once none is left. */
|
|
54
|
+
release(): void;
|
|
55
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class CountdownController {
|
|
2
|
+
constructor(host, options) {
|
|
3
|
+
this.options = options;
|
|
4
|
+
this.holds = 0;
|
|
5
|
+
host.addController(this);
|
|
6
|
+
}
|
|
7
|
+
get running() {
|
|
8
|
+
return this.timer !== undefined;
|
|
9
|
+
}
|
|
10
|
+
hostConnected() { }
|
|
11
|
+
hostDisconnected() {
|
|
12
|
+
this.pause();
|
|
13
|
+
this.holds = 0;
|
|
14
|
+
}
|
|
15
|
+
start(ms) {
|
|
16
|
+
if (ms === undefined || !(ms > 0)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.remaining = ms;
|
|
20
|
+
this.resume();
|
|
21
|
+
}
|
|
22
|
+
pause() {
|
|
23
|
+
if (this.timer === undefined || this.startedAt === undefined) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
clearTimeout(this.timer);
|
|
27
|
+
this.timer = undefined;
|
|
28
|
+
this.remaining = Math.max((this.remaining ?? 0) - (Date.now() - this.startedAt), 0);
|
|
29
|
+
this.startedAt = undefined;
|
|
30
|
+
this.options.paused?.(true);
|
|
31
|
+
}
|
|
32
|
+
resume() {
|
|
33
|
+
if (this.timer !== undefined || this.remaining === undefined || this.remaining <= 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.startedAt = Date.now();
|
|
37
|
+
this.options.paused?.(false);
|
|
38
|
+
this.timer = setTimeout(() => {
|
|
39
|
+
this.timer = undefined;
|
|
40
|
+
this.options.done();
|
|
41
|
+
}, this.remaining);
|
|
42
|
+
}
|
|
43
|
+
cancel() {
|
|
44
|
+
clearTimeout(this.timer);
|
|
45
|
+
this.timer = undefined;
|
|
46
|
+
}
|
|
47
|
+
hold() {
|
|
48
|
+
this.holds += 1;
|
|
49
|
+
this.pause();
|
|
50
|
+
}
|
|
51
|
+
release() {
|
|
52
|
+
this.holds = Math.max(this.holds - 1, 0);
|
|
53
|
+
if (this.holds === 0) {
|
|
54
|
+
this.resume();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/** What a `DelayController` is for. */
|
|
3
|
+
export interface DelayOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the host renders `elapsed`, and so updates when it moves.
|
|
6
|
+
* Defaults to `true`; a wait that only runs `done`, a debounce, leaves its
|
|
7
|
+
* host alone.
|
|
8
|
+
*/
|
|
9
|
+
renders?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A wait an element starts and restarts: whether it has run out, and what runs
|
|
13
|
+
* when it does.
|
|
14
|
+
*
|
|
15
|
+
* Avatar's `fallback-delay`, Image's `placeholder-delay` and Loader's `delay`
|
|
16
|
+
* each hold a region back until a wait is over, and Listbox's `filter-delay`
|
|
17
|
+
* holds a filter back until typing pauses. Each kept a timer, a flag and the
|
|
18
|
+
* clean-up by hand; this keeps the three together, and clears the timer when
|
|
19
|
+
* the element leaves the page.
|
|
20
|
+
*
|
|
21
|
+
* `elapsed` is what a template reads, and the host updates when it moves,
|
|
22
|
+
* unless `renders` is false.
|
|
23
|
+
* Leaving the page stops the wait without forgetting that it ran out, so a
|
|
24
|
+
* loader moved to another place in the page does not wait again.
|
|
25
|
+
*/
|
|
26
|
+
export declare class DelayController implements ReactiveController {
|
|
27
|
+
private readonly host;
|
|
28
|
+
private readonly options;
|
|
29
|
+
/** Whether the last wait started has run out. */
|
|
30
|
+
elapsed: boolean;
|
|
31
|
+
private timer;
|
|
32
|
+
private done;
|
|
33
|
+
constructor(host: ReactiveControllerHost, options?: DelayOptions);
|
|
34
|
+
/** Whether a wait is running. */
|
|
35
|
+
get pending(): boolean;
|
|
36
|
+
hostConnected(): void;
|
|
37
|
+
hostDisconnected(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Starts the wait over: `elapsed` goes back to false, and after `ms`
|
|
40
|
+
* milliseconds it turns true and `done` runs. No `ms` starts no wait, which
|
|
41
|
+
* leaves `elapsed` false.
|
|
42
|
+
*/
|
|
43
|
+
start(ms?: number, done?: () => void): void;
|
|
44
|
+
/** Stops a running wait, leaving `elapsed` as it is. */
|
|
45
|
+
cancel(): void;
|
|
46
|
+
/** Ends a running wait now, as though it had run out. Nothing happens where none is running. */
|
|
47
|
+
flush(): void;
|
|
48
|
+
private finish;
|
|
49
|
+
private mark;
|
|
50
|
+
}
|