@fusion.dev/helix-core 0.148.0 → 0.149.1
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 +8518 -6050
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +1 -1
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +2 -2
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +5 -4
- package/dist/controllers/machine-controller/machine-controller.d.ts +69 -0
- package/dist/controllers/machine-controller/machine-controller.js +78 -0
- package/dist/controllers/machine-controller/machine.d.ts +93 -0
- package/dist/controllers/machine-controller/machine.js +20 -0
- package/dist/controllers/reorder-controller/reorder-controller.d.ts +285 -0
- package/dist/controllers/reorder-controller/reorder-controller.js +497 -0
- package/dist/controllers/source-controller/source-controller.js +1 -1
- package/dist/controllers/virtualize-controller/heights.js +1 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +117 -3
- package/dist/controllers/virtualize-controller/virtualize-controller.js +134 -32
- package/dist/events/before-reorder.d.ts +14 -0
- package/dist/events/before-reorder.js +11 -0
- package/dist/events/details.d.ts +28 -0
- package/dist/events/events.d.ts +4 -2
- package/dist/events/events.js +3 -1
- package/dist/events/names.js +1 -1
- package/dist/events/reorder.d.ts +18 -12
- package/dist/events/reordered.d.ts +4 -4
- package/dist/helix-element/helix-element.js +2 -1
- package/dist/helix-element/react-audit-off.d.ts +11 -0
- package/dist/helix-element/react-audit-off.js +1 -0
- package/dist/helix-element/react-audit.d.ts +20 -0
- package/dist/helix-element/react-audit.js +56 -0
- package/dist/helix-element/react-bridge.d.ts +2 -22
- package/dist/helix-element/react-bridge.js +1 -56
- package/dist/index.d.ts +10 -4
- package/dist/index.js +10 -4
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +2 -2
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +3 -0
- package/dist/mixins/mixin-collection/mixin-collection.js +4 -0
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +3 -0
- package/dist/mixins/mixin-field/mixin-field.js +1 -1
- package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +12 -0
- package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +15 -2
- package/dist/mixins/mixin-tokens/mixin-tokens.d.ts +5 -0
- package/dist/mixins/mixin-tokens/mixin-tokens.js +13 -2
- package/dist/mixins/mixin-viewport/mixin-viewport.d.ts +17 -15
- package/dist/mixins/mixin-viewport/mixin-viewport.js +15 -34
- package/dist/utils/calendar/calendar.d.ts +27 -0
- package/dist/utils/calendar/calendar.js +17 -0
- package/dist/utils/count-label/count-label.d.ts +7 -4
- package/dist/utils/count-label/count-label.js +2 -5
- package/dist/utils/date-segments/date-segments.d.ts +2 -1
- package/dist/utils/date-segments/date-segments.js +10 -0
- package/dist/utils/locale-of/locale-of.d.ts +5 -0
- package/dist/utils/locale-of/locale-of.js +8 -2
- package/dist/utils/reorder/drag-preview.d.ts +46 -0
- package/dist/utils/reorder/drag-preview.js +58 -0
- package/dist/utils/reorder/reorder.d.ts +178 -0
- package/dist/utils/reorder/reorder.js +112 -0
- package/dist/utils/report-runtime/report-runtime.d.ts +7 -4
- package/dist/utils/slotted-words/slotted-words.d.ts +3 -2
- package/dist/utils/slotted-words/slotted-words.js +3 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/words/index.d.ts +3 -0
- package/dist/words/index.js +3 -0
- package/dist/words/message.d.ts +47 -0
- package/dist/words/message.js +152 -0
- package/dist/words/registry.d.ts +89 -0
- package/dist/words/registry.js +101 -0
- package/dist/words/words-locale.d.ts +34 -0
- package/dist/words/words-locale.js +30 -0
- package/dist/words/words.d.ts +52 -0
- package/dist/words/words.js +100 -0
- package/package.json +40 -25
|
@@ -8,9 +8,9 @@ import { html, nothing } from 'lit';
|
|
|
8
8
|
import { property, state } from 'lit/decorators.js';
|
|
9
9
|
import { FullscreenController } from '../../controllers/fullscreen-controller/fullscreen-controller.js';
|
|
10
10
|
import { IdleController } from '../../controllers/idle-controller/idle-controller.js';
|
|
11
|
-
import { fillTemplate } from '../../utils/fill-template/fill-template.js';
|
|
12
11
|
import { inlineStep } from '../../utils/inline-step/inline-step.js';
|
|
13
12
|
import { isRtl } from '../../utils/is-rtl/is-rtl.js';
|
|
13
|
+
import { formatWords, words } from '../../words/words.js';
|
|
14
14
|
const HINT_SHOWS = 1600;
|
|
15
15
|
export const MixinViewport = (Base) => {
|
|
16
16
|
class Viewport extends Base {
|
|
@@ -38,11 +38,10 @@ export const MixinViewport = (Base) => {
|
|
|
38
38
|
this.handleGestureWheel = (event) => {
|
|
39
39
|
if (this.gestures === 'cooperative' && !(event.ctrlKey || event.metaKey)) {
|
|
40
40
|
const mac = typeof navigator !== 'undefined' && navigator.userAgent.includes('Mac');
|
|
41
|
-
|
|
42
|
-
this.showHint(fillTemplate(words, { key: mac ? '⌘' : 'Ctrl' }), event);
|
|
41
|
+
this.showHint(formatWords(this, 'wheelHintLabel', { key: mac ? '⌘' : 'Ctrl' }), event);
|
|
43
42
|
}
|
|
44
43
|
else if (this.gestures === 'activate' && !this.gesturesTaken) {
|
|
45
|
-
this.showHint(this.activateHintLabel
|
|
44
|
+
this.showHint(this.activateHintLabel, event);
|
|
46
45
|
}
|
|
47
46
|
};
|
|
48
47
|
this.handleGestureTouch = (event) => {
|
|
@@ -51,10 +50,10 @@ export const MixinViewport = (Base) => {
|
|
|
51
50
|
return;
|
|
52
51
|
}
|
|
53
52
|
if (this.gestures === 'cooperative') {
|
|
54
|
-
this.showHint(this.twoFingerHintLabel
|
|
53
|
+
this.showHint(this.twoFingerHintLabel, finger);
|
|
55
54
|
}
|
|
56
55
|
else if (this.gestures === 'activate' && !this.gesturesTaken) {
|
|
57
|
-
this.showHint(this.activateHintLabel
|
|
56
|
+
this.showHint(this.activateHintLabel, finger);
|
|
58
57
|
}
|
|
59
58
|
};
|
|
60
59
|
this.handleTake = (event) => {
|
|
@@ -133,17 +132,7 @@ export const MixinViewport = (Base) => {
|
|
|
133
132
|
}
|
|
134
133
|
};
|
|
135
134
|
}
|
|
136
|
-
|
|
137
|
-
return 'View controls';
|
|
138
|
-
}
|
|
139
|
-
get restingGestureLabels() {
|
|
140
|
-
return {
|
|
141
|
-
activate: 'Press to use it',
|
|
142
|
-
twoFinger: 'Use two fingers to move it',
|
|
143
|
-
using: 'In use. Press Escape to scroll the page again.',
|
|
144
|
-
wheel: 'Use {key} and scroll to zoom',
|
|
145
|
-
};
|
|
146
|
-
}
|
|
135
|
+
static { this.words = ''; }
|
|
147
136
|
get gestureMode() {
|
|
148
137
|
if (this.gestures === 'cooperative') {
|
|
149
138
|
return 'cooperative';
|
|
@@ -215,7 +204,7 @@ export const MixinViewport = (Base) => {
|
|
|
215
204
|
?hidden=${this.hint === ''}
|
|
216
205
|
style=${at ? `--_viewport-hint-x: ${(at.x * 100).toFixed(2)}%; --_viewport-hint-y: ${(at.y * 100).toFixed(2)}%` : nothing}
|
|
217
206
|
>${this.hint}</div>
|
|
218
|
-
<div part="gesture-status" aria-hidden="true" ?hidden=${!this.gesturesTaken}>${this.usingLabel
|
|
207
|
+
<div part="gesture-status" aria-hidden="true" ?hidden=${!this.gesturesTaken}>${this.usingLabel}</div>`;
|
|
219
208
|
}
|
|
220
209
|
panEvent(_detail) {
|
|
221
210
|
throw new Error(`<${this.localName}> composes MixinViewport and must implement panEvent(), the surface naming no event of its own.`);
|
|
@@ -287,7 +276,7 @@ export const MixinViewport = (Base) => {
|
|
|
287
276
|
return nothing;
|
|
288
277
|
}
|
|
289
278
|
const [up, start, end, down] = ['up', 'start', 'end', 'down'].map((way) => this.panGlyph(way));
|
|
290
|
-
return html `<div part="controls" role="group" aria-label=${this.controlsLabel
|
|
279
|
+
return html `<div part="controls" role="group" aria-label=${this.controlsLabel}>
|
|
291
280
|
${pan
|
|
292
281
|
? html `<button part="pan-up" aria-label=${this.panLabel('up')} type="button" @click=${() => this.pan('up')}>
|
|
293
282
|
<slot name="pan-up-icon">${up}</slot>
|
|
@@ -325,19 +314,14 @@ export const MixinViewport = (Base) => {
|
|
|
325
314
|
</div>`;
|
|
326
315
|
}
|
|
327
316
|
}
|
|
328
|
-
__decorate([
|
|
329
|
-
property({ attribute: 'activate-hint-label' })
|
|
330
|
-
], Viewport.prototype, "activateHintLabel", void 0);
|
|
331
317
|
__decorate([
|
|
332
318
|
property({ reflect: true })
|
|
333
319
|
], Viewport.prototype, "controls", void 0);
|
|
334
|
-
__decorate([
|
|
335
|
-
property({ attribute: 'controls-label' })
|
|
336
|
-
], Viewport.prototype, "controlsLabel", void 0);
|
|
337
320
|
__decorate([
|
|
338
321
|
property({ attribute: 'controls-placement', reflect: true })
|
|
339
322
|
], Viewport.prototype, "controlsPlacement", void 0);
|
|
340
323
|
__decorate([
|
|
324
|
+
words,
|
|
341
325
|
property({ attribute: 'fullscreen-label' })
|
|
342
326
|
], Viewport.prototype, "fullscreenLabel", void 0);
|
|
343
327
|
__decorate([
|
|
@@ -347,30 +331,26 @@ export const MixinViewport = (Base) => {
|
|
|
347
331
|
property({ attribute: 'idle-delay', type: Number })
|
|
348
332
|
], Viewport.prototype, "idleDelay", void 0);
|
|
349
333
|
__decorate([
|
|
334
|
+
words,
|
|
350
335
|
property({ attribute: 'pan-down-label' })
|
|
351
336
|
], Viewport.prototype, "panDownLabel", void 0);
|
|
352
337
|
__decorate([
|
|
338
|
+
words,
|
|
353
339
|
property({ attribute: 'pan-left-label' })
|
|
354
340
|
], Viewport.prototype, "panLeftLabel", void 0);
|
|
355
341
|
__decorate([
|
|
342
|
+
words,
|
|
356
343
|
property({ attribute: 'pan-right-label' })
|
|
357
344
|
], Viewport.prototype, "panRightLabel", void 0);
|
|
358
345
|
__decorate([
|
|
359
346
|
property({ attribute: 'pan-step', type: Number })
|
|
360
347
|
], Viewport.prototype, "panStep", void 0);
|
|
361
348
|
__decorate([
|
|
349
|
+
words,
|
|
362
350
|
property({ attribute: 'pan-up-label' })
|
|
363
351
|
], Viewport.prototype, "panUpLabel", void 0);
|
|
364
352
|
__decorate([
|
|
365
|
-
|
|
366
|
-
], Viewport.prototype, "twoFingerHintLabel", void 0);
|
|
367
|
-
__decorate([
|
|
368
|
-
property({ attribute: 'using-label' })
|
|
369
|
-
], Viewport.prototype, "usingLabel", void 0);
|
|
370
|
-
__decorate([
|
|
371
|
-
property({ attribute: 'wheel-hint-label' })
|
|
372
|
-
], Viewport.prototype, "wheelHintLabel", void 0);
|
|
373
|
-
__decorate([
|
|
353
|
+
words,
|
|
374
354
|
property({ attribute: 'zoom-in-label' })
|
|
375
355
|
], Viewport.prototype, "zoomInLabel", void 0);
|
|
376
356
|
__decorate([
|
|
@@ -389,6 +369,7 @@ export const MixinViewport = (Base) => {
|
|
|
389
369
|
state()
|
|
390
370
|
], Viewport.prototype, "hintAt", void 0);
|
|
391
371
|
__decorate([
|
|
372
|
+
words,
|
|
392
373
|
property({ attribute: 'zoom-out-label' })
|
|
393
374
|
], Viewport.prototype, "zoomOutLabel", void 0);
|
|
394
375
|
return Viewport;
|
|
@@ -16,6 +16,33 @@ export declare const localToday: (now?: Date) => string;
|
|
|
16
16
|
* neither, as Firefox does not, or where the tag cannot be read.
|
|
17
17
|
*/
|
|
18
18
|
export declare const weekStartOf: (locale: string) => number;
|
|
19
|
+
/**
|
|
20
|
+
* The calendars a date family draws and writes: those whose months are the
|
|
21
|
+
* Gregorian months, so a grid of Gregorian days is one of their months.
|
|
22
|
+
*
|
|
23
|
+
* `buddhist` is Thailand's (2569 for 2026), `japanese` the Japanese eras
|
|
24
|
+
* (Reiwa 8), and `roc` Taiwan's Minguo years (115). They differ from
|
|
25
|
+
* `gregory` in the year alone, and `iso8601` in the order the parts are
|
|
26
|
+
* written.
|
|
27
|
+
*/
|
|
28
|
+
export type DateCalendar = 'buddhist' | 'gregory' | 'iso8601' | 'japanese' | 'roc';
|
|
29
|
+
/**
|
|
30
|
+
* The language tag a date family formats with: `locale`, with the calendar it
|
|
31
|
+
* writes dates in.
|
|
32
|
+
*
|
|
33
|
+
* That is `calendar` where it is one of the five `DateCalendar` names, then
|
|
34
|
+
* the locale's own calendar where its months are the Gregorian months, then
|
|
35
|
+
* `gregory`. **A calendar with months of its own, such as `persian` for `fa-IR`
|
|
36
|
+
* or `islamic-umalqura` for `ar-SA` in some engines, is written as `gregory`.**
|
|
37
|
+
* The arithmetic is Gregorian, so a heading in Mehr 1405 over the days of
|
|
38
|
+
* October 2026 named a month the grid did not show.
|
|
39
|
+
*
|
|
40
|
+
* `locale` unchanged where nothing moves, so a page in a Gregorian locale
|
|
41
|
+
* formats exactly as it did. Empty is the runtime's own locale. A tag `Intl`
|
|
42
|
+
* refuses throws, as every formatter given one does. Remembered per locale and
|
|
43
|
+
* calendar, since reading a locale's calendar builds a formatter.
|
|
44
|
+
*/
|
|
45
|
+
export declare const calendarLocale: (locale: string, calendar?: string) => string;
|
|
19
46
|
/** A month as `YYYY-MM`. */
|
|
20
47
|
export declare const monthOf: (date: Date) => string;
|
|
21
48
|
/** A date some days away, which may be negative, at midnight UTC. */
|
|
@@ -15,6 +15,23 @@ export const weekStartOf = (locale) => {
|
|
|
15
15
|
return 0;
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
+
const DRAWN = ['buddhist', 'gregory', 'iso8601', 'japanese', 'roc'];
|
|
19
|
+
const tagged = new Map();
|
|
20
|
+
export const calendarLocale = (locale, calendar = '') => {
|
|
21
|
+
const key = `${locale} ${calendar}`;
|
|
22
|
+
let tag = tagged.get(key);
|
|
23
|
+
if (tag === undefined) {
|
|
24
|
+
const own = new Intl.DateTimeFormat(locale || undefined).resolvedOptions();
|
|
25
|
+
const wanted = DRAWN.includes(calendar)
|
|
26
|
+
? calendar
|
|
27
|
+
: DRAWN.includes(own.calendar)
|
|
28
|
+
? ''
|
|
29
|
+
: 'gregory';
|
|
30
|
+
tag = wanted ? `${new Intl.Locale(locale || own.locale, { calendar: wanted })}` : locale;
|
|
31
|
+
tagged.set(key, tag);
|
|
32
|
+
}
|
|
33
|
+
return tag;
|
|
34
|
+
};
|
|
18
35
|
export const monthOf = (date) => `${String(date.getUTCFullYear()).padStart(4, '0')}-${String(date.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
19
36
|
export const addDays = (date, days) => utcDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days);
|
|
20
37
|
export const sameMonth = (left, right) => left.getUTCFullYear() === right.getUTCFullYear() && left.getUTCMonth() === right.getUTCMonth();
|
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* **`Intl.PluralRules` picks the sentence**, because the line between "one"
|
|
6
6
|
* and "other" is the language's: French says "0 résultat" where English says
|
|
7
|
-
* "0 results".
|
|
8
|
-
*
|
|
7
|
+
* "0 results". Each sentence is a message, so `other` can hold every form a
|
|
8
|
+
* language has, `{count, plural, one {# wynik} few {# wyniki} many {# wyników}
|
|
9
|
+
* other {# wyniku}}`, and an empty `one` leaves the choice to it.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Kept for the families whose singular property is still read. A family on the
|
|
12
|
+
* words registry formats its one plural message with `formatWords`. Listbox
|
|
13
|
+
* says how many options a filter left this way, which Combobox and Command
|
|
14
|
+
* hand down to it.
|
|
12
15
|
*/
|
|
13
16
|
export declare const countLabel: (count: number, one: string, other: string, locale?: string) => string;
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export const countLabel = (count, one, other, locale = '') => fillTemplate(new Intl.PluralRules(locale || undefined).select(count) === 'one' ? one : other, {
|
|
4
|
-
count: formatNumbers(locale)(count),
|
|
5
|
-
});
|
|
1
|
+
import { formatMessage } from '../../words/message.js';
|
|
2
|
+
export const countLabel = (count, one, other, locale = '') => formatMessage(one && new Intl.PluralRules(locale || undefined).select(count) === 'one' ? one : other, { count }, locale);
|
|
@@ -26,7 +26,8 @@ export interface SegmentOptions {
|
|
|
26
26
|
* between them, read off `Intl`'s own rendering of a sample.
|
|
27
27
|
*
|
|
28
28
|
* `en-US` is month, day, year with slashes; `de-DE` day, month, year with
|
|
29
|
-
* dots; `ja-JP` year, month, day
|
|
29
|
+
* dots; `ja-JP` year, month, day; the `iso8601` calendar year, month, day
|
|
30
|
+
* with hyphens. A time follows the clock: a twelve hour
|
|
30
31
|
* locale ends with the half of the day.
|
|
31
32
|
*/
|
|
32
33
|
export declare const segmentLayout: (kind: SegmentedKind, locale: string, options?: SegmentOptions) => SegmentSlot[];
|
|
@@ -14,6 +14,16 @@ export const segmentLayout = (kind, locale, options = {}) => {
|
|
|
14
14
|
...(options.seconds ? { second: '2-digit' } : {}),
|
|
15
15
|
...(options.hourCycle ? { hourCycle: options.hourCycle } : {}),
|
|
16
16
|
});
|
|
17
|
+
if (kind === 'date' &&
|
|
18
|
+
(/-ca-iso8601/i.test(locale) || format.resolvedOptions().calendar === 'iso8601')) {
|
|
19
|
+
return [
|
|
20
|
+
{ kind: 'year' },
|
|
21
|
+
{ literal: '-' },
|
|
22
|
+
{ kind: 'month' },
|
|
23
|
+
{ literal: '-' },
|
|
24
|
+
{ kind: 'day' },
|
|
25
|
+
];
|
|
26
|
+
}
|
|
17
27
|
const known = new Set(['day', 'dayPeriod', 'hour', 'minute', 'month', 'second', 'year']);
|
|
18
28
|
const slots = [];
|
|
19
29
|
for (const part of format.formatToParts(SAMPLE)) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime's own locale: in a browser, the language its interface is set
|
|
3
|
+
* to, which is what every `Intl` call given no locale uses.
|
|
4
|
+
*/
|
|
5
|
+
export declare const runtimeLocale: () => string;
|
|
1
6
|
/**
|
|
2
7
|
* The locale an element speaks: `own`, the element's own `locale` property,
|
|
3
8
|
* where one is set, and otherwise the `lang` of the nearest element with one,
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
let runtime;
|
|
2
|
+
export const runtimeLocale = () => {
|
|
3
|
+
runtime ??= new Intl.Locale(new Intl.DateTimeFormat().resolvedOptions().locale);
|
|
4
|
+
return runtime.toString();
|
|
5
|
+
};
|
|
2
6
|
const usable = (lang) => {
|
|
3
7
|
try {
|
|
4
8
|
const asked = new Intl.Locale(lang);
|
|
5
|
-
|
|
6
|
-
return asked.region === undefined && asked.language === runtime
|
|
9
|
+
runtimeLocale();
|
|
10
|
+
return asked.region === undefined && asked.language === runtime?.language
|
|
11
|
+
? ''
|
|
12
|
+
: asked.toString();
|
|
7
13
|
}
|
|
8
14
|
catch {
|
|
9
15
|
return '';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The copy of an item a pointer drags: what the item draws, through every
|
|
3
|
+
* shadow root and slot in it, rebuilt as plain elements with the styles the
|
|
4
|
+
* browser computed for each written on it.
|
|
5
|
+
*
|
|
6
|
+
* `ReorderController` puts the copy in the host's own shadow root, in the top
|
|
7
|
+
* layer, so the page's markup is never touched and no scrolling box clips it.
|
|
8
|
+
* A copy is inert: it has no custom elements, no slots, and no attributes but
|
|
9
|
+
* a picture's, so nothing in it upgrades, takes the host's children, or joins
|
|
10
|
+
* a radio group. Pseudo-elements are not copied.
|
|
11
|
+
*/
|
|
12
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
13
|
+
/**
|
|
14
|
+
* The part a host draws for the drag preview: an empty manual popover,
|
|
15
|
+
* hidden from assistive technology, which `ReorderController` fills with a
|
|
16
|
+
* copy of the item a pointer drags and shows in the top layer. Closed, it
|
|
17
|
+
* takes no room. Drawn while `on`, which is while the host reorders.
|
|
18
|
+
*/
|
|
19
|
+
export declare const drawDragPreview: (on: boolean) => TemplateResult | typeof nothing;
|
|
20
|
+
/** A point in the viewport, where the boxes of a copy are measured from. */
|
|
21
|
+
interface Origin {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The box an element draws: its own, or the boxes of what it draws together,
|
|
27
|
+
* for an element that is `display: contents`, such as a row of a grid.
|
|
28
|
+
*/
|
|
29
|
+
export declare const boxOf: (element: Element) => DOMRect;
|
|
30
|
+
/**
|
|
31
|
+
* A copy of what a node draws, for a drag preview, with every style the
|
|
32
|
+
* browser computed for each part written on it. The custom properties are
|
|
33
|
+
* left out, having done their work, and so are the logical properties, whose
|
|
34
|
+
* physical twins are written: two of a pair in one style do not agree on
|
|
35
|
+
* which wins.
|
|
36
|
+
*
|
|
37
|
+
* A `placed` node is put where it is with `position: absolute`, measured from
|
|
38
|
+
* `origin`, the padding box it is placed in, rather than laid out again. The
|
|
39
|
+
* root of a preview is placed in the preview, whose padding box is the root's
|
|
40
|
+
* box as `boxOf` measures it, and so is whatever a grid lays out, since a
|
|
41
|
+
* subgrid has no tracks of its own once it is copied out of its grid. An
|
|
42
|
+
* element that is `display: contents`, such as a row whose cells sit in the
|
|
43
|
+
* grid of its table, has each of its cells put where it is.
|
|
44
|
+
*/
|
|
45
|
+
export declare const dragPreviewOf: (node: Node, placed: boolean, origin: Origin) => Node[];
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { html, nothing } from 'lit';
|
|
2
|
+
export const drawDragPreview = (on) => on ? html `<div part="drag-preview" popover="manual" aria-hidden="true" inert></div>` : nothing;
|
|
3
|
+
const drawn = (node) => {
|
|
4
|
+
const assigned = node instanceof HTMLSlotElement ? node.assignedNodes({ flatten: true }) : [];
|
|
5
|
+
return [...(assigned.length ? assigned : (node.shadowRoot ?? node).childNodes)];
|
|
6
|
+
};
|
|
7
|
+
export const boxOf = (element) => {
|
|
8
|
+
if (getComputedStyle(element).display !== 'contents') {
|
|
9
|
+
return element.getBoundingClientRect();
|
|
10
|
+
}
|
|
11
|
+
let [left, top, right, bottom] = [Infinity, Infinity, -Infinity, -Infinity];
|
|
12
|
+
for (const node of drawn(element)) {
|
|
13
|
+
const box = node instanceof Element && boxOf(node);
|
|
14
|
+
if (box && (box.width || box.height)) {
|
|
15
|
+
left = Math.min(left, box.left);
|
|
16
|
+
top = Math.min(top, box.top);
|
|
17
|
+
right = Math.max(right, box.right);
|
|
18
|
+
bottom = Math.max(bottom, box.bottom);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return left <= right ? new DOMRect(left, top, right - left, bottom - top) : new DOMRect();
|
|
22
|
+
};
|
|
23
|
+
export const dragPreviewOf = (node, placed, origin) => {
|
|
24
|
+
if (node instanceof Text) {
|
|
25
|
+
return [node.cloneNode()];
|
|
26
|
+
}
|
|
27
|
+
const style = node instanceof Element ? getComputedStyle(node) : undefined;
|
|
28
|
+
if (!style || style.display === 'none') {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const element = node;
|
|
32
|
+
const tag = element.localName;
|
|
33
|
+
const whole = /^(img|svg)$/.test(tag);
|
|
34
|
+
const copy = whole
|
|
35
|
+
? element.cloneNode(true)
|
|
36
|
+
: document.createElement(/-|^slot$/.test(tag) ? 'div' : tag);
|
|
37
|
+
const contents = style.display === 'contents';
|
|
38
|
+
const box = element.getBoundingClientRect();
|
|
39
|
+
let written = '';
|
|
40
|
+
for (let index = 0; index < style.length; index += 1) {
|
|
41
|
+
const name = style.item(index);
|
|
42
|
+
if (!/^--|block|inline|start-|end-/.test(name)) {
|
|
43
|
+
written += `${name}:${style.getPropertyValue(name)};`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
copy.style.cssText =
|
|
47
|
+
placed && !contents
|
|
48
|
+
? `${written}position:absolute;inset:auto;left:${box.left - origin.x}px;top:${box.top - origin.y}px;width:${box.width}px;height:${box.height}px;margin:0;box-sizing:border-box;grid-area:auto`
|
|
49
|
+
: written;
|
|
50
|
+
if (!whole) {
|
|
51
|
+
const inner = !contents && (placed || style.position !== 'static')
|
|
52
|
+
? { x: box.left + element.clientLeft, y: box.top + element.clientTop }
|
|
53
|
+
: origin;
|
|
54
|
+
const grid = contents ? placed : style.display.endsWith('grid');
|
|
55
|
+
copy.append(...drawn(element).flatMap((child) => dragPreviewOf(child, grid, inner)));
|
|
56
|
+
}
|
|
57
|
+
return [copy];
|
|
58
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The state and the arithmetic of moving one item to a new place in its
|
|
3
|
+
* collection: rows of a list or a data table, or a table's columns.
|
|
4
|
+
*
|
|
5
|
+
* Pure, so every transition and every sum has a spec with no DOM.
|
|
6
|
+
* `ReorderController` holds the state, listens to the pointer and the keys, and
|
|
7
|
+
* runs the effects each change of state calls for.
|
|
8
|
+
*/
|
|
9
|
+
/** Which way a collection runs: rows down the block axis, columns along the inline one. */
|
|
10
|
+
export type ReorderAxis = 'block' | 'inline';
|
|
11
|
+
/** What holds the item: a key or a press on its grip, or a pointer dragging it. */
|
|
12
|
+
export type ReorderHold = 'keyboard' | 'pointer';
|
|
13
|
+
/**
|
|
14
|
+
* Where a move stands.
|
|
15
|
+
*
|
|
16
|
+
* - `idle`: nothing is held.
|
|
17
|
+
* - `pressed`: a pointer is down on a grip and has not traveled far enough to
|
|
18
|
+
* be a drag.
|
|
19
|
+
* - `held`: an item is held, and `to` is where it would go.
|
|
20
|
+
* - `dropped` and `canceled`: the move has ended, for one step, so the
|
|
21
|
+
* controller can run what each calls for before it settles to `idle`.
|
|
22
|
+
*
|
|
23
|
+
* `from` and `to` count from 0 in the order the items are drawn.
|
|
24
|
+
*/
|
|
25
|
+
export type ReorderState = {
|
|
26
|
+
phase: 'idle';
|
|
27
|
+
} | {
|
|
28
|
+
from: number;
|
|
29
|
+
key: string;
|
|
30
|
+
phase: 'pressed';
|
|
31
|
+
pointer: number;
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
} | {
|
|
35
|
+
by: ReorderHold;
|
|
36
|
+
from: number;
|
|
37
|
+
key: string;
|
|
38
|
+
phase: 'held';
|
|
39
|
+
to: number;
|
|
40
|
+
} | {
|
|
41
|
+
by: ReorderHold;
|
|
42
|
+
from: number;
|
|
43
|
+
key: string;
|
|
44
|
+
phase: 'dropped';
|
|
45
|
+
to: number;
|
|
46
|
+
} | {
|
|
47
|
+
by: ReorderHold;
|
|
48
|
+
key: string;
|
|
49
|
+
phase: 'canceled';
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* What can happen to a move.
|
|
53
|
+
*
|
|
54
|
+
* - `press`: a pointer went down on a grip.
|
|
55
|
+
* - `drag`: the pointer moved, and `to` is the place under it.
|
|
56
|
+
* - `release`: the pointer went up.
|
|
57
|
+
* - `pick`: a grip was pressed with a key or a click. It holds the item, or
|
|
58
|
+
* drops the item that grip already holds.
|
|
59
|
+
* - `step`: a key moved the held item to `to`.
|
|
60
|
+
* - `target`: a click on another item dropped the held one at `to`.
|
|
61
|
+
* - `drop` and `cancel`: Enter or Space, and Escape.
|
|
62
|
+
* - `settle`: the controller has run the effects of a drop or a cancel.
|
|
63
|
+
*/
|
|
64
|
+
export type ReorderInput = {
|
|
65
|
+
from: number;
|
|
66
|
+
key: string;
|
|
67
|
+
pointer: number;
|
|
68
|
+
type: 'press';
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
} | {
|
|
72
|
+
slop: number;
|
|
73
|
+
to: number;
|
|
74
|
+
type: 'drag';
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
} | {
|
|
78
|
+
type: 'release';
|
|
79
|
+
} | {
|
|
80
|
+
from: number;
|
|
81
|
+
key: string;
|
|
82
|
+
type: 'pick';
|
|
83
|
+
} | {
|
|
84
|
+
to: number;
|
|
85
|
+
type: 'step';
|
|
86
|
+
} | {
|
|
87
|
+
to: number;
|
|
88
|
+
type: 'target';
|
|
89
|
+
} | {
|
|
90
|
+
type: 'cancel';
|
|
91
|
+
} | {
|
|
92
|
+
type: 'drop';
|
|
93
|
+
} | {
|
|
94
|
+
type: 'settle';
|
|
95
|
+
};
|
|
96
|
+
/** The state with nothing held. */
|
|
97
|
+
export declare const IDLE: ReorderState;
|
|
98
|
+
/**
|
|
99
|
+
* The state after an input, or `null` where the input changes nothing.
|
|
100
|
+
*
|
|
101
|
+
* A press becomes a drag only once the pointer has traveled `slop` pixels, so a
|
|
102
|
+
* click on a grip stays a click. A click on a grip holds the item for the
|
|
103
|
+
* keys, and a second click on the same grip drops it, which is the move a
|
|
104
|
+
* single pointer makes without dragging.
|
|
105
|
+
*/
|
|
106
|
+
export declare const reorderStep: (state: ReorderState, input: ReorderInput) => ReorderState | null;
|
|
107
|
+
/**
|
|
108
|
+
* How many of the other items the pointer has passed: the place the held item
|
|
109
|
+
* goes, among the others.
|
|
110
|
+
*
|
|
111
|
+
* `centers` are the middles of the other items along the axis, in the order
|
|
112
|
+
* drawn. `reversed` is a row laid out from the right, a right-to-left page,
|
|
113
|
+
* where passing an item means being left of its middle.
|
|
114
|
+
*/
|
|
115
|
+
export declare const passedCount: (centers: readonly number[], at: number, reversed?: boolean) => number;
|
|
116
|
+
/**
|
|
117
|
+
* The middles of the items along the axis, from their boxes, for
|
|
118
|
+
* `passedCount`.
|
|
119
|
+
*
|
|
120
|
+
* An item with no box is one a window did not draw. One before every drawn
|
|
121
|
+
* item counts as passed, one after them as not passed, and one between two
|
|
122
|
+
* drawn items as where the drawn one before it is. `reversed` is the same
|
|
123
|
+
* question on a row laid out from the right.
|
|
124
|
+
*/
|
|
125
|
+
export declare const centersOf: (spans: readonly ({
|
|
126
|
+
end: number;
|
|
127
|
+
start: number;
|
|
128
|
+
} | undefined)[], reversed?: boolean) => number[];
|
|
129
|
+
/**
|
|
130
|
+
* Where a key moves a held item, or `null` for a key that does not move one.
|
|
131
|
+
*
|
|
132
|
+
* The arrows along the axis move it one place, and Home and End move it to
|
|
133
|
+
* either end. Along the inline axis the arrows follow the writing direction,
|
|
134
|
+
* so on a right-to-left page the left arrow moves it forward. The answer is
|
|
135
|
+
* held to the collection, so an arrow at an end returns where it already is.
|
|
136
|
+
*/
|
|
137
|
+
export declare const reorderKey: (key: string, { axis, count, rtl, to }: {
|
|
138
|
+
axis: ReorderAxis;
|
|
139
|
+
count: number;
|
|
140
|
+
rtl: boolean;
|
|
141
|
+
to: number;
|
|
142
|
+
}) => number | null;
|
|
143
|
+
/**
|
|
144
|
+
* Where the drop indicator goes for a held item: before or after the item at
|
|
145
|
+
* an index of the order drawn, or nowhere while the item would stay where it
|
|
146
|
+
* is.
|
|
147
|
+
*
|
|
148
|
+
* Moving back, the item lands before the one now at `to`. Moving forward, it
|
|
149
|
+
* lands after it.
|
|
150
|
+
*/
|
|
151
|
+
export declare const dropMark: (from: number, to: number) => {
|
|
152
|
+
edge: 'after' | 'before';
|
|
153
|
+
index: number;
|
|
154
|
+
} | undefined;
|
|
155
|
+
/**
|
|
156
|
+
* Every key in a new order: `key` taken out and put back at `to` among the
|
|
157
|
+
* shown ones.
|
|
158
|
+
*
|
|
159
|
+
* `all` is the whole order, which can hold keys that are not drawn: a hidden
|
|
160
|
+
* column, or a row a search left out. `shown` is the order drawn. The key goes
|
|
161
|
+
* after the shown one it now follows, or before the first shown one where it
|
|
162
|
+
* moves to the front, so a key that is not drawn keeps its place among the
|
|
163
|
+
* rest.
|
|
164
|
+
*/
|
|
165
|
+
export declare const placeKey: (all: readonly string[], shown: readonly string[], key: string, to: number) => string[];
|
|
166
|
+
/**
|
|
167
|
+
* How far to scroll a box in one frame while a held item is near one of its
|
|
168
|
+
* edges, in pixels: negative toward the start, positive toward the end, and 0
|
|
169
|
+
* away from both.
|
|
170
|
+
*
|
|
171
|
+
* `at` is the pointer along the axis, and `start` and `end` are the box's
|
|
172
|
+
* edges. The speed rises from nothing at `zone` pixels in to `most` at the
|
|
173
|
+
* edge and past it.
|
|
174
|
+
*/
|
|
175
|
+
export declare const edgeSpeed: (at: number, start: number, end: number, { most, zone }?: {
|
|
176
|
+
most?: number;
|
|
177
|
+
zone?: number;
|
|
178
|
+
}) => number;
|