@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
|
@@ -41,6 +41,18 @@ export interface Color {
|
|
|
41
41
|
}
|
|
42
42
|
/** The syntaxes a value can be written in. */
|
|
43
43
|
export type ColorFormat = 'hex' | 'hsl' | 'rgb';
|
|
44
|
+
/** HSB to bytes, which is the road every written syntax goes down. */
|
|
45
|
+
export declare const toRgb: (color: Color) => {
|
|
46
|
+
blue: number;
|
|
47
|
+
green: number;
|
|
48
|
+
red: number;
|
|
49
|
+
};
|
|
50
|
+
/** HSB to HSL, which is a different pair of numbers for the same color. */
|
|
51
|
+
export declare const toHsl: (color: Color) => {
|
|
52
|
+
hue: number;
|
|
53
|
+
lightness: number;
|
|
54
|
+
saturation: number;
|
|
55
|
+
};
|
|
44
56
|
/**
|
|
45
57
|
* Any of the syntaxes to the working model, or `null` where it is not a color.
|
|
46
58
|
*
|
|
@@ -40,7 +40,7 @@ const rgbToHsb = (red, green, blue, alpha) => {
|
|
|
40
40
|
saturation: Math.round(high === 0 ? 0 : (span / high) * 100),
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
|
-
const toRgb = (color) => {
|
|
43
|
+
export const toRgb = (color) => {
|
|
44
44
|
const hue = (((color.hue % 360) + 360) % 360) / 60;
|
|
45
45
|
const saturation = color.saturation / 100;
|
|
46
46
|
const brightness = color.brightness / 100;
|
|
@@ -62,7 +62,7 @@ const toRgb = (color) => {
|
|
|
62
62
|
red: Math.round((red + base) * 255),
|
|
63
63
|
};
|
|
64
64
|
};
|
|
65
|
-
const toHsl = (color) => {
|
|
65
|
+
export const toHsl = (color) => {
|
|
66
66
|
const saturation = color.saturation / 100;
|
|
67
67
|
const brightness = color.brightness / 100;
|
|
68
68
|
const lightness = (brightness * (2 - saturation)) / 2;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/** One part of a date or a time a reader sets on its own. */
|
|
2
|
+
export type SegmentKind = 'day' | 'dayPeriod' | 'hour' | 'minute' | 'month' | 'second' | 'year';
|
|
3
|
+
/** What a segmented field holds: a date, or a time of day. */
|
|
4
|
+
export type SegmentedKind = 'date' | 'time';
|
|
5
|
+
/**
|
|
6
|
+
* The parts set so far, each a number: the month 1 to 12, the hour 0 to 23
|
|
7
|
+
* whatever clock it is shown on, and the half of the day 0 for before noon and
|
|
8
|
+
* 1 for after. A part not yet set is absent.
|
|
9
|
+
*/
|
|
10
|
+
export type Segments = Partial<Record<SegmentKind, number>>;
|
|
11
|
+
/** One place in the field: a segment, or the text a locale writes between two. */
|
|
12
|
+
export type SegmentSlot = {
|
|
13
|
+
kind: SegmentKind;
|
|
14
|
+
} | {
|
|
15
|
+
literal: string;
|
|
16
|
+
};
|
|
17
|
+
/** How the field is laid out and which clock its hour is on. */
|
|
18
|
+
export interface SegmentOptions {
|
|
19
|
+
/** `h12` or `h23`, or absent for the locale's own. */
|
|
20
|
+
hourCycle?: 'h12' | 'h23' | undefined;
|
|
21
|
+
/** Whether a time carries its seconds. */
|
|
22
|
+
seconds?: boolean | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The segments in the order the locale writes them, with what it writes
|
|
26
|
+
* between them, read off `Intl`'s own rendering of a sample.
|
|
27
|
+
*
|
|
28
|
+
* `en-US` is month, day, year with slashes; `de-DE` day, month, year with
|
|
29
|
+
* dots; `ja-JP` year, month, day. A time follows the clock: a twelve hour
|
|
30
|
+
* locale ends with the half of the day.
|
|
31
|
+
*/
|
|
32
|
+
export declare const segmentLayout: (kind: SegmentedKind, locale: string, options?: SegmentOptions) => SegmentSlot[];
|
|
33
|
+
/** Whether a layout writes the hour on a twelve hour clock. */
|
|
34
|
+
export declare const isTwelveHourLayout: (layout: readonly SegmentSlot[]) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The lowest and highest a segment can be, as it is shown: the hour on a twelve
|
|
37
|
+
* hour clock is 1 to 12, and a day goes as high as its month allows.
|
|
38
|
+
*/
|
|
39
|
+
export declare const segmentRange: (kind: SegmentKind, segments: Segments, twelve: boolean) => {
|
|
40
|
+
max: number;
|
|
41
|
+
min: number;
|
|
42
|
+
};
|
|
43
|
+
/** A segment's number as it is shown: the hour on the clock the field is on. */
|
|
44
|
+
export declare const shownValue: (kind: SegmentKind, segments: Segments, twelve: boolean) => number | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Sets a segment from the number shown for it, which on a twelve hour clock
|
|
47
|
+
* is put back together with the half of the day, and keeps the day inside its
|
|
48
|
+
* month.
|
|
49
|
+
*/
|
|
50
|
+
export declare const setSegment: (segments: Segments, kind: SegmentKind, shown: number | undefined, twelve: boolean) => Segments;
|
|
51
|
+
/**
|
|
52
|
+
* A segment moved by a key's step, wrapping at its ends as a dial does, and
|
|
53
|
+
* starting from where a reader would look for it when it is empty: the lowest
|
|
54
|
+
* going up, the highest going down, and this year for a year.
|
|
55
|
+
*/
|
|
56
|
+
export declare const stepSegment: (segments: Segments, kind: SegmentKind, by: number, twelve: boolean, thisYear?: number) => Segments;
|
|
57
|
+
/**
|
|
58
|
+
* A digit typed into a segment, given what was typed into it already.
|
|
59
|
+
*
|
|
60
|
+
* The digits run on while the number they make still fits and could take
|
|
61
|
+
* another, so `1` then `2` in a month is December; a digit that would overflow
|
|
62
|
+
* starts the number over. `advance` says the segment is full, so focus moves
|
|
63
|
+
* on, which is React Aria's typing: `3` in a month is March and moves to the
|
|
64
|
+
* day, `1` waits for a second digit.
|
|
65
|
+
*/
|
|
66
|
+
export declare const typeDigit: (segments: Segments, kind: SegmentKind, typed: string, digit: string, twelve: boolean) => {
|
|
67
|
+
advance: boolean;
|
|
68
|
+
segments: Segments;
|
|
69
|
+
typed: string;
|
|
70
|
+
};
|
|
71
|
+
/** Where a segment stands when a key arrives: its kind, the field's parts, and what was typed into it. */
|
|
72
|
+
export interface SegmentKeyState {
|
|
73
|
+
/** The segment the key was pressed on. */
|
|
74
|
+
kind: SegmentKind;
|
|
75
|
+
/** The locale the meridiem is written in, whose words a letter picks. */
|
|
76
|
+
locale: string;
|
|
77
|
+
/** The field's parts. */
|
|
78
|
+
segments: Segments;
|
|
79
|
+
/** Whether the field is on a twelve hour clock. */
|
|
80
|
+
twelve: boolean;
|
|
81
|
+
/** The digits typed into the segment so far. */
|
|
82
|
+
typed: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What an editing key does to a segment, or `null` for a key that edits
|
|
86
|
+
* nothing.
|
|
87
|
+
*
|
|
88
|
+
* The arrows step it and wrap, Home and End take it to its lowest and highest,
|
|
89
|
+
* Backspace and Delete empty it, a digit is typed as `typeDigit` types it, and
|
|
90
|
+
* on the meridiem a letter picks the half whose word starts with it. `advance`
|
|
91
|
+
* says the segment is full and focus moves on.
|
|
92
|
+
*/
|
|
93
|
+
export declare const segmentKey: (key: string, { kind, locale, segments, twelve, typed }: SegmentKeyState) => {
|
|
94
|
+
advance: boolean;
|
|
95
|
+
segments: Segments;
|
|
96
|
+
typed: string;
|
|
97
|
+
} | null;
|
|
98
|
+
/** The segments a value names, or none where it names nothing whole. */
|
|
99
|
+
export declare const segmentsOf: (kind: SegmentedKind, value: string) => Segments;
|
|
100
|
+
/**
|
|
101
|
+
* The value the segments add up to, or the empty string while any segment the
|
|
102
|
+
* layout shows is unset.
|
|
103
|
+
*
|
|
104
|
+
* A date that does not exist, the thirtieth of February, cannot be reached,
|
|
105
|
+
* the day being kept inside its month as it is set.
|
|
106
|
+
*/
|
|
107
|
+
export declare const segmentsValue: (kind: SegmentedKind, segments: Segments, layout: readonly SegmentSlot[]) => string;
|
|
108
|
+
/** Whether a reader has set some segments and not all of them. */
|
|
109
|
+
export declare const isPartial: (kind: SegmentedKind, segments: Segments, layout: readonly SegmentSlot[]) => boolean;
|
|
110
|
+
/**
|
|
111
|
+
* What a segment shows: its number, padded the way the locale pads it, the
|
|
112
|
+
* locale's own word for the half of the day, or dashes while it is unset.
|
|
113
|
+
*/
|
|
114
|
+
export declare const segmentText: (kind: SegmentKind, segments: Segments, twelve: boolean, locale: string) => string;
|
|
115
|
+
/**
|
|
116
|
+
* What a screen reader hears for a segment's value: what it shows, and for a
|
|
117
|
+
* month its name as well, "8 – August", which is React Aria's reading. A
|
|
118
|
+
* month heard as a number alone leaves a reader counting.
|
|
119
|
+
*/
|
|
120
|
+
export declare const segmentValueText: (kind: SegmentKind, segments: Segments, twelve: boolean, locale: string) => string;
|
|
121
|
+
/** What a locale calls a segment, such as "month" or "AM/PM", from `Intl`. */
|
|
122
|
+
export declare const segmentName: (kind: SegmentKind, locale: string) => string;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const SAMPLE = new Date(Date.UTC(2006, 10, 22, 15, 4, 5));
|
|
2
|
+
export const segmentLayout = (kind, locale, options = {}) => {
|
|
3
|
+
const format = kind === 'date'
|
|
4
|
+
? new Intl.DateTimeFormat(locale || undefined, {
|
|
5
|
+
day: '2-digit',
|
|
6
|
+
month: '2-digit',
|
|
7
|
+
timeZone: 'UTC',
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
})
|
|
10
|
+
: new Intl.DateTimeFormat(locale || undefined, {
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
timeZone: 'UTC',
|
|
14
|
+
...(options.seconds ? { second: '2-digit' } : {}),
|
|
15
|
+
...(options.hourCycle ? { hourCycle: options.hourCycle } : {}),
|
|
16
|
+
});
|
|
17
|
+
const known = new Set(['day', 'dayPeriod', 'hour', 'minute', 'month', 'second', 'year']);
|
|
18
|
+
const slots = [];
|
|
19
|
+
for (const part of format.formatToParts(SAMPLE)) {
|
|
20
|
+
if (known.has(part.type)) {
|
|
21
|
+
slots.push({ kind: part.type });
|
|
22
|
+
}
|
|
23
|
+
else if (part.type === 'literal' && slots.length > 0) {
|
|
24
|
+
slots.push({ literal: part.value });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
while (slots.length > 0 && 'literal' in slots.at(-1)) {
|
|
28
|
+
slots.pop();
|
|
29
|
+
}
|
|
30
|
+
return slots;
|
|
31
|
+
};
|
|
32
|
+
export const isTwelveHourLayout = (layout) => layout.some((slot) => 'kind' in slot && slot.kind === 'dayPeriod');
|
|
33
|
+
const daysIn = (month, year) => month === undefined ? 31 : new Date(Date.UTC(year ?? 2000, month, 0)).getUTCDate();
|
|
34
|
+
export const segmentRange = (kind, segments, twelve) => {
|
|
35
|
+
switch (kind) {
|
|
36
|
+
case 'year':
|
|
37
|
+
return { max: 9999, min: 1 };
|
|
38
|
+
case 'month':
|
|
39
|
+
return { max: 12, min: 1 };
|
|
40
|
+
case 'day':
|
|
41
|
+
return { max: daysIn(segments.month, segments.year), min: 1 };
|
|
42
|
+
case 'hour':
|
|
43
|
+
return twelve ? { max: 12, min: 1 } : { max: 23, min: 0 };
|
|
44
|
+
case 'minute':
|
|
45
|
+
case 'second':
|
|
46
|
+
return { max: 59, min: 0 };
|
|
47
|
+
case 'dayPeriod':
|
|
48
|
+
return { max: 1, min: 0 };
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
export const shownValue = (kind, segments, twelve) => {
|
|
52
|
+
if (kind === 'dayPeriod') {
|
|
53
|
+
return segments.hour === undefined ? segments.dayPeriod : segments.hour >= 12 ? 1 : 0;
|
|
54
|
+
}
|
|
55
|
+
const value = segments[kind];
|
|
56
|
+
if (kind === 'hour' && twelve && value !== undefined) {
|
|
57
|
+
return value % 12 === 0 ? 12 : value % 12;
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
};
|
|
61
|
+
export const setSegment = (segments, kind, shown, twelve) => {
|
|
62
|
+
const next = { ...segments };
|
|
63
|
+
if (shown === undefined) {
|
|
64
|
+
delete next[kind];
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
if (kind === 'hour' && twelve) {
|
|
68
|
+
const half = segments.hour !== undefined ? (segments.hour >= 12 ? 1 : 0) : (segments.dayPeriod ?? 0);
|
|
69
|
+
next.hour = (shown % 12) + half * 12;
|
|
70
|
+
}
|
|
71
|
+
else if (kind === 'dayPeriod') {
|
|
72
|
+
next.dayPeriod = shown;
|
|
73
|
+
if (next.hour !== undefined) {
|
|
74
|
+
next.hour = (next.hour % 12) + shown * 12;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
next[kind] = shown;
|
|
79
|
+
}
|
|
80
|
+
if (next.day !== undefined) {
|
|
81
|
+
next.day = Math.min(next.day, daysIn(next.month, next.year));
|
|
82
|
+
}
|
|
83
|
+
return next;
|
|
84
|
+
};
|
|
85
|
+
export const stepSegment = (segments, kind, by, twelve, thisYear = new Date().getFullYear()) => {
|
|
86
|
+
const { max, min } = segmentRange(kind, segments, twelve);
|
|
87
|
+
const shown = shownValue(kind, segments, twelve);
|
|
88
|
+
let next;
|
|
89
|
+
if (shown === undefined) {
|
|
90
|
+
next = kind === 'year' ? thisYear : by > 0 ? min : max;
|
|
91
|
+
}
|
|
92
|
+
else if (kind === 'year') {
|
|
93
|
+
next = Math.min(max, Math.max(min, shown + by));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const span = max - min + 1;
|
|
97
|
+
next = ((((shown - min + by) % span) + span) % span) + min;
|
|
98
|
+
}
|
|
99
|
+
return setSegment(segments, kind, next, twelve);
|
|
100
|
+
};
|
|
101
|
+
export const typeDigit = (segments, kind, typed, digit, twelve) => {
|
|
102
|
+
const { max, min } = segmentRange(kind, segments, twelve);
|
|
103
|
+
let text = `${typed}${digit}`;
|
|
104
|
+
if (Number(text) > max) {
|
|
105
|
+
text = digit;
|
|
106
|
+
}
|
|
107
|
+
const value = Number(text);
|
|
108
|
+
const full = text.length >= String(max).length || value * 10 > max;
|
|
109
|
+
const next = value >= min ? setSegment(segments, kind, value, twelve) : segments;
|
|
110
|
+
return { advance: full && value >= min, segments: next, typed: full ? '' : text };
|
|
111
|
+
};
|
|
112
|
+
export const segmentKey = (key, { kind, locale, segments, twelve, typed }) => {
|
|
113
|
+
const done = (next) => next === undefined ? null : { advance: false, segments: next, typed: '' };
|
|
114
|
+
if (key === 'ArrowUp' || key === 'ArrowDown') {
|
|
115
|
+
return done(stepSegment(segments, kind, key === 'ArrowUp' ? 1 : -1, twelve));
|
|
116
|
+
}
|
|
117
|
+
if (key === 'Home' || key === 'End') {
|
|
118
|
+
const { max, min } = segmentRange(kind, segments, twelve);
|
|
119
|
+
return done(setSegment(segments, kind, key === 'Home' ? min : max, twelve));
|
|
120
|
+
}
|
|
121
|
+
if (key === 'Backspace' || key === 'Delete') {
|
|
122
|
+
return done(setSegment(segments, kind, undefined, twelve));
|
|
123
|
+
}
|
|
124
|
+
if (/^\d$/.test(key) && kind !== 'dayPeriod') {
|
|
125
|
+
return typeDigit(segments, kind, typed, key, twelve);
|
|
126
|
+
}
|
|
127
|
+
if (kind === 'dayPeriod' && key.length === 1) {
|
|
128
|
+
const letter = key.toLowerCase();
|
|
129
|
+
const words = [0, 1].map((half) => segmentText('dayPeriod', { dayPeriod: half }, true, locale).toLowerCase());
|
|
130
|
+
const half = words.findIndex((word) => word.startsWith(letter));
|
|
131
|
+
return done(half >= 0 ? setSegment(segments, kind, half, twelve) : undefined);
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
};
|
|
135
|
+
export const segmentsOf = (kind, value) => {
|
|
136
|
+
if (kind === 'date') {
|
|
137
|
+
const parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
138
|
+
return parts ? { day: Number(parts[3]), month: Number(parts[2]), year: Number(parts[1]) } : {};
|
|
139
|
+
}
|
|
140
|
+
const parts = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value);
|
|
141
|
+
if (!parts) {
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
const hour = Number(parts[1]);
|
|
145
|
+
return {
|
|
146
|
+
dayPeriod: hour >= 12 ? 1 : 0,
|
|
147
|
+
hour,
|
|
148
|
+
minute: Number(parts[2]),
|
|
149
|
+
...(parts[3] === undefined ? {} : { second: Number(parts[3]) }),
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
export const segmentsValue = (kind, segments, layout) => {
|
|
153
|
+
const shown = layout.flatMap((slot) => ('kind' in slot ? [slot.kind] : []));
|
|
154
|
+
const needed = shown.filter((one) => one !== 'dayPeriod');
|
|
155
|
+
if (needed.some((one) => segments[one] === undefined)) {
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
const pad = (number, width = 2) => String(number ?? 0).padStart(width, '0');
|
|
159
|
+
if (kind === 'date') {
|
|
160
|
+
return `${pad(segments.year, 4)}-${pad(segments.month)}-${pad(segments.day)}`;
|
|
161
|
+
}
|
|
162
|
+
const clock = `${pad(segments.hour)}:${pad(segments.minute)}`;
|
|
163
|
+
return shown.includes('second') ? `${clock}:${pad(segments.second)}` : clock;
|
|
164
|
+
};
|
|
165
|
+
export const isPartial = (kind, segments, layout) => segmentsValue(kind, segments, layout) === '' &&
|
|
166
|
+
layout.some((slot) => 'kind' in slot && slot.kind !== 'dayPeriod' && segments[slot.kind] !== undefined);
|
|
167
|
+
export const segmentText = (kind, segments, twelve, locale) => {
|
|
168
|
+
const shown = shownValue(kind, segments, twelve);
|
|
169
|
+
if (kind === 'dayPeriod') {
|
|
170
|
+
const at = new Date(Date.UTC(2000, 0, 1, (shown ?? 0) === 1 ? 15 : 3));
|
|
171
|
+
const word = new Intl.DateTimeFormat(locale || undefined, {
|
|
172
|
+
hour: 'numeric',
|
|
173
|
+
hour12: true,
|
|
174
|
+
timeZone: 'UTC',
|
|
175
|
+
})
|
|
176
|
+
.formatToParts(at)
|
|
177
|
+
.find((part) => part.type === 'dayPeriod')?.value ?? (shown === 1 ? 'PM' : 'AM');
|
|
178
|
+
return shown === undefined ? '–'.repeat(word.length) : word;
|
|
179
|
+
}
|
|
180
|
+
const width = kind === 'year' ? 4 : 2;
|
|
181
|
+
if (shown === undefined) {
|
|
182
|
+
return '–'.repeat(width);
|
|
183
|
+
}
|
|
184
|
+
return kind === 'hour' && twelve ? String(shown) : String(shown).padStart(width, '0');
|
|
185
|
+
};
|
|
186
|
+
export const segmentValueText = (kind, segments, twelve, locale) => {
|
|
187
|
+
const text = segmentText(kind, segments, twelve, locale);
|
|
188
|
+
const month = segments.month;
|
|
189
|
+
if (kind !== 'month' || month === undefined) {
|
|
190
|
+
return text;
|
|
191
|
+
}
|
|
192
|
+
const name = new Intl.DateTimeFormat(locale || undefined, {
|
|
193
|
+
month: 'long',
|
|
194
|
+
timeZone: 'UTC',
|
|
195
|
+
}).format(new Date(Date.UTC(2000, month - 1, 1)));
|
|
196
|
+
return `${month} – ${name}`;
|
|
197
|
+
};
|
|
198
|
+
export const segmentName = (kind, locale) => {
|
|
199
|
+
try {
|
|
200
|
+
const names = new Intl.DisplayNames(locale || undefined, { type: 'dateTimeField' });
|
|
201
|
+
return names.of(kind === 'dayPeriod' ? 'dayPeriod' : kind) ?? kind;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return kind;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
/** What a dismiss button says, does and holds. */
|
|
3
|
+
export interface DrawDismissOptions {
|
|
4
|
+
/** Whether the button is disabled, as a disabled tag's is. */
|
|
5
|
+
disabled?: boolean | undefined;
|
|
6
|
+
/** The button's accessible name, such as "Dismiss" or "Remove Design". */
|
|
7
|
+
label: string;
|
|
8
|
+
/**
|
|
9
|
+
* What the button holds: the `dismiss-icon` slot around `drawDismissGlyph()`,
|
|
10
|
+
* written in the component's own `protected render…()` so a tier can move it.
|
|
11
|
+
*/
|
|
12
|
+
mark: unknown;
|
|
13
|
+
/** What a press does. */
|
|
14
|
+
onDismiss: () => void;
|
|
15
|
+
/** The button's `tabindex`, where it is not in the page's own order. */
|
|
16
|
+
tabindex?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A dismiss button, `<button part="dismiss" type="button">`, named by `label`
|
|
20
|
+
* and holding `mark`.
|
|
21
|
+
*
|
|
22
|
+
* **A plain `<button>` in the shadow root, never a nested component**, so the
|
|
23
|
+
* styled tier reaches the control itself through `[part="dismiss"]`. Toast,
|
|
24
|
+
* Callout, Dialog and Tag draw it, and each documents `dismiss` and
|
|
25
|
+
* `dismiss-mark` and the `dismiss-icon` slot. The slot stays in each
|
|
26
|
+
* component's own render seam, where its fallback is a tier's to replace.
|
|
27
|
+
*/
|
|
28
|
+
export declare const drawDismiss: ({ disabled, label, mark, onDismiss, tabindex, }: DrawDismissOptions) => TemplateResult;
|
|
29
|
+
/** The cross a dismiss button shows where nothing is slotted: `<svg part="dismiss-mark">`. */
|
|
30
|
+
export declare const drawDismissGlyph: () => TemplateResult;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { x } from '@fusion.dev/helix-icons';
|
|
2
|
+
import { html, nothing } from 'lit';
|
|
3
|
+
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
4
|
+
export const drawDismiss = ({ disabled = false, label, mark, onDismiss, tabindex, }) => html `<button
|
|
5
|
+
part="dismiss"
|
|
6
|
+
type="button"
|
|
7
|
+
aria-label=${label}
|
|
8
|
+
?disabled=${disabled}
|
|
9
|
+
tabindex=${tabindex ?? nothing}
|
|
10
|
+
@click=${onDismiss}
|
|
11
|
+
>
|
|
12
|
+
${mark}
|
|
13
|
+
</button>`;
|
|
14
|
+
export const drawDismissGlyph = () => html `<svg part="dismiss-mark" viewBox=${x.viewBox} fill="currentColor" aria-hidden="true">
|
|
15
|
+
${unsafeSVG(x.content)}
|
|
16
|
+
</svg>`;
|
|
@@ -10,16 +10,17 @@ export interface DrawIconOptions {
|
|
|
10
10
|
/**
|
|
11
11
|
* Draws an icon that was imported by name, rather than looked up by string.
|
|
12
12
|
*
|
|
13
|
-
* **`<helix-icon name="play">`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* path data: measured at 50,814 bytes raw and 15,010 gzipped for 102 icons, and
|
|
13
|
+
* **`<helix-icon name="play">` looks its glyph up in a registry at runtime.**
|
|
14
|
+
* The registry holds the whole set once `@fusion.dev/helix-headless/icon`
|
|
15
|
+
* registers it, and no bundler can shake a lookup by string, so every consumer
|
|
16
|
+
* of any component that renders one ships all of the generated path data: measured at 50,814 bytes raw and 15,010 gzipped for 102 icons, and
|
|
17
17
|
* growing about 147 gzipped bytes per icon added. A named import ships one path
|
|
18
18
|
* and is checked when the package compiles rather than by a name lookup later.
|
|
19
19
|
*
|
|
20
20
|
* That element still earns its place as *public* API, where a consumer writes a
|
|
21
|
-
* name in markup and a lookup is the only thing that can work
|
|
22
|
-
*
|
|
21
|
+
* name in markup and a lookup is the only thing that can work, and
|
|
22
|
+
* `@fusion.dev/helix-headless/icon/root` names glyphs without registering the
|
|
23
|
+
* set. What it should not be is how this library draws its own marks.
|
|
23
24
|
*
|
|
24
25
|
* **There is no `part` option, and there cannot be.** A part name behind an
|
|
25
26
|
* interpolation is a part the manifest cannot see, so the component's own
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
import { nothing, type TemplateResult } from 'lit';
|
|
2
2
|
import { type StaticValue } from 'lit/static-html.js';
|
|
3
|
+
import { CHART_PLOT, type ChartPointerController } from '../../controllers/chart-pointer-controller/chart-pointer-controller.js';
|
|
3
4
|
import type { Row } from '../read-series/read-series.js';
|
|
4
|
-
|
|
5
|
-
* The id every chart puts on its plot, and the boundary a bubble is kept inside.
|
|
6
|
-
*
|
|
7
|
-
* **A name six templates and this file have to agree on is a name that will
|
|
8
|
-
* stop agreeing**, which is `PALETTE`'s argument and it holds harder here: the
|
|
9
|
-
* failure is silent. `within` resolves by id, so a plot renamed on one side
|
|
10
|
-
* returns null, the boundary falls back to the viewport,
|
|
11
|
-
* and the bubble goes back to hanging past the drawing with nothing reported
|
|
12
|
-
* anywhere.
|
|
13
|
-
*
|
|
14
|
-
* Fixed rather than generated, for the reason the mark's id below gives: it is
|
|
15
|
-
* scoped to one chart's shadow root, where it is the only one.
|
|
16
|
-
*/
|
|
17
|
-
export declare const CHART_PLOT = "helix-chart-plot";
|
|
5
|
+
export { CHART_PLOT };
|
|
18
6
|
/** Where a tooltip points, in the plot's own percentages, and which side it prefers. */
|
|
19
7
|
export interface TooltipAt {
|
|
20
8
|
/**
|
|
@@ -53,6 +41,12 @@ export interface TooltipAt {
|
|
|
53
41
|
*/
|
|
54
42
|
y: string;
|
|
55
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* What the keyboard is reading, which `drawTooltip` says aloud and marks.
|
|
46
|
+
*
|
|
47
|
+
* The chart's `pointer` is one, and is what every chart passes.
|
|
48
|
+
*/
|
|
49
|
+
export type ReadingCursor = Pick<ChartPointerController, 'column' | 'keyed'>;
|
|
56
50
|
/**
|
|
57
51
|
* What a pointer resting on a reading shows, as `Tooltip` pointed at a mark.
|
|
58
52
|
*
|
|
@@ -86,11 +80,11 @@ export interface TooltipAt {
|
|
|
86
80
|
* here, including columns nothing was plotted from, because a note beside the
|
|
87
81
|
* figures is most of the reason to want a tooltip.
|
|
88
82
|
*
|
|
89
|
-
* **
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* the
|
|
83
|
+
* **The bubble is hidden from assistive technology, and the readout is what is
|
|
84
|
+
* heard.** The bubble carries `aria-hidden`, and so does the mark, because a
|
|
85
|
+
* bubble of figures announced on every move is noise. Given `reading`, which
|
|
86
|
+
* is the chart's `pointer`, this also renders the `live` part: a polite region
|
|
87
|
+
* that says the reading in words when a key moves it, and is empty otherwise.
|
|
88
|
+
* The cell the series keys rest on is named `tooltip-cell-current` as well.
|
|
95
89
|
*/
|
|
96
|
-
export declare const drawTooltip: (rows: Row[], at: number, tag: StaticValue, where?: TooltipAt, slotOf?: (cell: number) => number | undefined) => TemplateResult | typeof nothing;
|
|
90
|
+
export declare const drawTooltip: (rows: Row[], at: number, tag: StaticValue, where?: TooltipAt, slotOf?: (cell: number) => number | undefined, reading?: ReadingCursor) => TemplateResult | typeof nothing;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { nothing } from 'lit';
|
|
2
2
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
3
3
|
import { html } from 'lit/static-html.js';
|
|
4
|
+
import { CHART_PLOT, } from '../../controllers/chart-pointer-controller/chart-pointer-controller.js';
|
|
4
5
|
import { seriesIndex } from '../../mixins/mixin-chart-source/mixin-chart-source.js';
|
|
5
|
-
|
|
6
|
+
import { spokenReading } from './spoken-reading.js';
|
|
7
|
+
export { CHART_PLOT };
|
|
6
8
|
const CHART_TOOLTIP = 'helix-chart-tooltip';
|
|
7
|
-
export const drawTooltip = (rows, at, tag, where, slotOf) => {
|
|
9
|
+
export const drawTooltip = (rows, at, tag, where, slotOf, reading) => {
|
|
8
10
|
const row = rows[at];
|
|
11
|
+
const live = reading === undefined || rows.length === 0
|
|
12
|
+
? nothing
|
|
13
|
+
: html `<span aria-live="polite" part="live" role="status">${reading.keyed && row ? spokenReading(row, reading.column) : ''}</span>`;
|
|
9
14
|
if (!row || rows.length === 0) {
|
|
10
|
-
return
|
|
15
|
+
return live;
|
|
11
16
|
}
|
|
12
17
|
const slot = 100 / rows.length;
|
|
13
18
|
const place = where ?? {
|
|
@@ -45,7 +50,7 @@ export const drawTooltip = (rows, at, tag, where, slotOf) => {
|
|
|
45
50
|
const series = cell.value === undefined ? undefined : answer;
|
|
46
51
|
return html `<span
|
|
47
52
|
data-series=${series ?? nothing}
|
|
48
|
-
part="tooltip-cell"
|
|
53
|
+
part="tooltip-cell${index === reading?.column ? ' tooltip-cell-current' : ''}"
|
|
49
54
|
style=${series === undefined ? nothing : `--chart-series: var(--chart-series-${series})`}
|
|
50
55
|
>
|
|
51
56
|
<span part="tooltip-name">${cell.name}</span>
|
|
@@ -53,5 +58,6 @@ export const drawTooltip = (rows, at, tag, where, slotOf) => {
|
|
|
53
58
|
</span>`;
|
|
54
59
|
})}
|
|
55
60
|
</div>
|
|
56
|
-
</${tag}
|
|
61
|
+
</${tag}>
|
|
62
|
+
${live}`;
|
|
57
63
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Row } from '../read-series/read-series.js';
|
|
2
|
+
/**
|
|
3
|
+
* A reading in words, for a chart's readout: the row's name and every cell
|
|
4
|
+
* that says something, or the one cell the series keys rest on.
|
|
5
|
+
*
|
|
6
|
+
* The table's own text, as the bubble shows it, so the words and the picture
|
|
7
|
+
* agree, and nothing in it is English: the names are the table's headings. A
|
|
8
|
+
* blank cell says nothing and is left out.
|
|
9
|
+
*/
|
|
10
|
+
export declare const spokenReading: (row: Row, column?: number) => string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const spokenReading = (row, column) => {
|
|
2
|
+
const cell = column === undefined ? undefined : row.cells[column];
|
|
3
|
+
if (cell) {
|
|
4
|
+
return `${row.label}, ${cell.name} ${cell.text}`.trim();
|
|
5
|
+
}
|
|
6
|
+
const said = row.cells
|
|
7
|
+
.filter((one) => one.text.trim() !== '')
|
|
8
|
+
.map((one) => `${one.name} ${one.text}`.trim());
|
|
9
|
+
return said.length === 0 ? row.label : `${row.label}: ${said.join(', ')}`;
|
|
10
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Where a row that is a link goes, in the words an `<a>` takes them. */
|
|
2
|
+
export interface LinkTarget {
|
|
3
|
+
/** Whether following the link downloads what it names. */
|
|
4
|
+
download?: boolean | undefined;
|
|
5
|
+
/** The URL. */
|
|
6
|
+
href: string;
|
|
7
|
+
/** The link's `rel`. */
|
|
8
|
+
rel?: string | undefined;
|
|
9
|
+
/** Where the link opens, such as `_blank`. */
|
|
10
|
+
target?: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** The keys held as a link is followed, which say where a browser opens it. */
|
|
13
|
+
export interface LinkModifiers {
|
|
14
|
+
altKey?: boolean | undefined;
|
|
15
|
+
ctrlKey?: boolean | undefined;
|
|
16
|
+
metaKey?: boolean | undefined;
|
|
17
|
+
shiftKey?: boolean | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Follows a link from an element that is not an `<a>`, the way the browser
|
|
21
|
+
* follows one: in this tab, in the `target` named, or in a new tab or window
|
|
22
|
+
* where Command, Control or Shift is held.
|
|
23
|
+
*
|
|
24
|
+
* **An option cannot hold an anchor.** `role="option"` sits on the option's
|
|
25
|
+
* host, where `aria-activedescendant` can name it, and an `<a>` inside the
|
|
26
|
+
* host is exposed as a link inside the option, which loses the option its name
|
|
27
|
+
* (Menu Item's `href` records the measurement). So the option stays an option
|
|
28
|
+
* and the link is followed through an anchor made for the moment, which is
|
|
29
|
+
* React Aria's way for its own link items: the browser decides what the keys
|
|
30
|
+
* mean, a new tab or a download, and nothing here guesses.
|
|
31
|
+
*
|
|
32
|
+
* The anchor is in the document only while it is clicked, and its click does
|
|
33
|
+
* not bubble, so nothing on the page hears a click on something it never drew.
|
|
34
|
+
*/
|
|
35
|
+
export declare const followLink: (document: Document, link: LinkTarget, modifiers?: LinkModifiers) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Runs `then` once a link option has been followed, for an owner whose panel
|
|
38
|
+
* closes when a choice is made: Select and Combobox close on the listbox's
|
|
39
|
+
* change, and a link changes nothing, so they hear the option's own
|
|
40
|
+
* `helix-select` instead.
|
|
41
|
+
*
|
|
42
|
+
* After every listener has had the event, so a page that cancels it, to route
|
|
43
|
+
* there itself, keeps the panel as it was, the way the option keeps the
|
|
44
|
+
* browser from following.
|
|
45
|
+
*/
|
|
46
|
+
export declare const afterLinkFollowed: (event: Event, then: () => void) => void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const followLink = (document, link, modifiers = {}) => {
|
|
2
|
+
const anchor = document.createElement('a');
|
|
3
|
+
anchor.href = link.href;
|
|
4
|
+
if (link.target) {
|
|
5
|
+
anchor.target = link.target;
|
|
6
|
+
}
|
|
7
|
+
if (link.rel) {
|
|
8
|
+
anchor.rel = link.rel;
|
|
9
|
+
}
|
|
10
|
+
if (link.download) {
|
|
11
|
+
anchor.download = '';
|
|
12
|
+
}
|
|
13
|
+
anchor.hidden = true;
|
|
14
|
+
document.body.append(anchor);
|
|
15
|
+
try {
|
|
16
|
+
anchor.dispatchEvent(new MouseEvent('click', {
|
|
17
|
+
altKey: Boolean(modifiers.altKey),
|
|
18
|
+
cancelable: true,
|
|
19
|
+
ctrlKey: Boolean(modifiers.ctrlKey),
|
|
20
|
+
metaKey: Boolean(modifiers.metaKey),
|
|
21
|
+
shiftKey: Boolean(modifiers.shiftKey),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
anchor.remove();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export const afterLinkFollowed = (event, then) => {
|
|
29
|
+
if (event.detail?.href === undefined) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
queueMicrotask(() => {
|
|
33
|
+
if (!event.defaultPrevented) {
|
|
34
|
+
then();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first heading among some elements or inside them, in order, or `null`.
|
|
3
|
+
*
|
|
4
|
+
* What names a panel when nothing else does: Popover's panel and a Tour stop
|
|
5
|
+
* by the first heading in their content. An element reference rather than an
|
|
6
|
+
* id, because the heading is in the light DOM and the panel in a shadow root,
|
|
7
|
+
* which an IDREF cannot cross.
|
|
8
|
+
*/
|
|
9
|
+
export declare const headingIn: (elements: readonly Element[]) => Element | null;
|
|
10
|
+
/**
|
|
11
|
+
* The first heading among some elements or inside them, or the elements
|
|
12
|
+
* themselves where none is a heading or holds one.
|
|
13
|
+
*
|
|
14
|
+
* What names Dialog and Drawer by their `header` slot. A header can hold a
|
|
15
|
+
* close button beside its title, and a name that read "Delete project Close"
|
|
16
|
+
* would be worse than the title alone; a header with no heading in it is
|
|
17
|
+
* still all the name there is.
|
|
18
|
+
*/
|
|
19
|
+
export declare const headingsIn: (elements: readonly Element[]) => Element[];
|