@fusion.dev/helix-core 0.147.0 → 0.149.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.
Files changed (124) hide show
  1. package/custom-elements.json +16160 -12084
  2. package/dist/controllers/aria-forward-controller/aria-forward-controller.js +1 -1
  3. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +2 -2
  4. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +5 -4
  5. package/dist/controllers/children-controller/children-controller.d.ts +28 -0
  6. package/dist/controllers/children-controller/children-controller.js +18 -0
  7. package/dist/controllers/countdown-controller/countdown-controller.d.ts +15 -1
  8. package/dist/controllers/countdown-controller/countdown-controller.js +27 -2
  9. package/dist/controllers/floating-controller/floating-controller.d.ts +12 -1
  10. package/dist/controllers/floating-controller/floating-controller.js +2 -1
  11. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +25 -0
  12. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +8 -0
  13. package/dist/controllers/machine-controller/machine-controller.d.ts +69 -0
  14. package/dist/controllers/machine-controller/machine-controller.js +78 -0
  15. package/dist/controllers/machine-controller/machine.d.ts +93 -0
  16. package/dist/controllers/machine-controller/machine.js +20 -0
  17. package/dist/controllers/open-phase-controller/open-phase-controller.d.ts +10 -0
  18. package/dist/controllers/open-phase-controller/open-phase-controller.js +7 -4
  19. package/dist/controllers/option-window-controller/option-window-controller.d.ts +96 -0
  20. package/dist/controllers/option-window-controller/option-window-controller.js +81 -0
  21. package/dist/controllers/popover-controller/popover-controller.js +1 -1
  22. package/dist/controllers/reorder-controller/reorder-controller.d.ts +285 -0
  23. package/dist/controllers/reorder-controller/reorder-controller.js +497 -0
  24. package/dist/controllers/source-controller/source-controller.js +1 -1
  25. package/dist/controllers/virtualize-controller/heights.js +1 -1
  26. package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +117 -3
  27. package/dist/controllers/virtualize-controller/virtualize-controller.js +134 -32
  28. package/dist/events/before-reorder.d.ts +14 -0
  29. package/dist/events/before-reorder.js +11 -0
  30. package/dist/events/details.d.ts +28 -0
  31. package/dist/events/events.d.ts +4 -2
  32. package/dist/events/events.js +3 -1
  33. package/dist/events/names.js +1 -1
  34. package/dist/events/reorder.d.ts +18 -12
  35. package/dist/events/reordered.d.ts +4 -4
  36. package/dist/helix-element/helix-element.d.ts +14 -5
  37. package/dist/helix-element/helix-element.js +6 -3
  38. package/dist/helix-element/react-audit-off.d.ts +11 -0
  39. package/dist/helix-element/react-audit-off.js +1 -0
  40. package/dist/helix-element/react-audit.d.ts +20 -0
  41. package/dist/helix-element/react-audit.js +56 -0
  42. package/dist/helix-element/react-bridge.d.ts +2 -22
  43. package/dist/helix-element/react-bridge.js +1 -56
  44. package/dist/index.d.ts +34 -14
  45. package/dist/index.js +32 -12
  46. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +7 -2
  47. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +6 -0
  48. package/dist/mixins/mixin-collection/mixin-collection.js +4 -0
  49. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +3 -0
  50. package/dist/mixins/mixin-field/mixin-field.js +1 -1
  51. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +12 -0
  52. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +15 -2
  53. package/dist/mixins/mixin-tokens/mixin-tokens.d.ts +5 -0
  54. package/dist/mixins/mixin-tokens/mixin-tokens.js +13 -2
  55. package/dist/mixins/mixin-viewport/mixin-viewport.d.ts +17 -15
  56. package/dist/mixins/mixin-viewport/mixin-viewport.js +23 -41
  57. package/dist/utils/assign-slots/assign-slots.d.ts +18 -0
  58. package/dist/utils/assign-slots/assign-slots.js +14 -0
  59. package/dist/utils/autofill-select/autofill-select.d.ts +40 -0
  60. package/dist/utils/autofill-select/autofill-select.js +14 -0
  61. package/dist/utils/calendar/calendar.d.ts +27 -0
  62. package/dist/utils/calendar/calendar.js +17 -0
  63. package/dist/utils/center-in-view/center-in-view.d.ts +18 -0
  64. package/dist/utils/center-in-view/center-in-view.js +14 -0
  65. package/dist/utils/chord/key-names.d.ts +30 -0
  66. package/dist/utils/chord/key-names.js +52 -0
  67. package/dist/utils/column-step/column-step.d.ts +13 -0
  68. package/dist/utils/column-step/column-step.js +9 -0
  69. package/dist/utils/count-label/count-label.d.ts +7 -4
  70. package/dist/utils/count-label/count-label.js +2 -5
  71. package/dist/utils/data-options/data-options.d.ts +60 -0
  72. package/dist/utils/data-options/data-options.js +46 -0
  73. package/dist/utils/date-segments/date-segments.d.ts +2 -1
  74. package/dist/utils/date-segments/date-segments.js +10 -0
  75. package/dist/utils/draw-tooltip/draw-tooltip.d.ts +7 -2
  76. package/dist/utils/draw-tooltip/draw-tooltip.js +1 -1
  77. package/dist/utils/draw-tooltip/spoken-reading.d.ts +3 -2
  78. package/dist/utils/draw-tooltip/spoken-reading.js +3 -2
  79. package/dist/utils/export-parts/export-parts.d.ts +14 -0
  80. package/dist/utils/export-parts/export-parts.js +1 -0
  81. package/dist/utils/fill-template/fill-template.d.ts +9 -0
  82. package/dist/utils/fill-template/fill-template.js +3 -0
  83. package/dist/utils/filter-marks/filter-marks.d.ts +36 -0
  84. package/dist/utils/filter-marks/filter-marks.js +41 -0
  85. package/dist/utils/focus-target/focus-target.d.ts +12 -0
  86. package/dist/utils/focus-target/focus-target.js +9 -0
  87. package/dist/utils/focusable-elements/focusable-elements.d.ts +17 -0
  88. package/dist/utils/focusable-elements/focusable-elements.js +8 -0
  89. package/dist/utils/grid-move/grid-move.d.ts +56 -0
  90. package/dist/utils/grid-move/grid-move.js +28 -0
  91. package/dist/utils/locale-of/locale-of.d.ts +5 -0
  92. package/dist/utils/locale-of/locale-of.js +8 -2
  93. package/dist/utils/number-format/number-format.d.ts +7 -0
  94. package/dist/utils/number-format/number-format.js +1 -0
  95. package/dist/utils/reorder/drag-preview.d.ts +46 -0
  96. package/dist/utils/reorder/drag-preview.js +58 -0
  97. package/dist/utils/reorder/reorder.d.ts +178 -0
  98. package/dist/utils/reorder/reorder.js +112 -0
  99. package/dist/utils/report-runtime/report-runtime.d.ts +14 -5
  100. package/dist/utils/report-values/report-values.d.ts +16 -4
  101. package/dist/utils/report-values/report-values.js +2 -2
  102. package/dist/utils/request-change/request-change.d.ts +11 -0
  103. package/dist/utils/request-change/request-change.js +10 -0
  104. package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +17 -0
  105. package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +1 -0
  106. package/dist/utils/slot-filled/slot-filled.d.ts +8 -0
  107. package/dist/utils/slot-filled/slot-filled.js +1 -0
  108. package/dist/utils/slotted-words/slotted-words.d.ts +3 -2
  109. package/dist/utils/slotted-words/slotted-words.js +3 -1
  110. package/dist/utils/toolbar-around/toolbar-around.d.ts +18 -0
  111. package/dist/utils/toolbar-around/toolbar-around.js +5 -0
  112. package/dist/version.d.ts +1 -1
  113. package/dist/version.js +1 -1
  114. package/dist/words/index.d.ts +3 -0
  115. package/dist/words/index.js +3 -0
  116. package/dist/words/message.d.ts +47 -0
  117. package/dist/words/message.js +152 -0
  118. package/dist/words/registry.d.ts +89 -0
  119. package/dist/words/registry.js +101 -0
  120. package/dist/words/words-locale.d.ts +34 -0
  121. package/dist/words/words-locale.js +30 -0
  122. package/dist/words/words.d.ts +52 -0
  123. package/dist/words/words.js +100 -0
  124. package/package.json +40 -25
@@ -52,6 +52,11 @@ export declare class MixinTokensInterface {
52
52
  * either way and this is what says what pressing it does.
53
53
  */
54
54
  collapseLabel: string;
55
+ /**
56
+ * The count of the tokens past `max-tags`, as it is shown, with `{count}`
57
+ * for the number, written in the element's locale. `+{count}` by default.
58
+ */
59
+ restText: string;
55
60
  /**
56
61
  * The cap in force right now, which is none while the reader has expanded it.
57
62
  *
@@ -8,6 +8,7 @@ import { caretUp } from '@fusion.dev/helix-icons';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { drawIcon } from '../../utils/draw-icon/draw-icon.js';
11
+ import { formatWords, words } from '../../words/words.js';
11
12
  export const MixinTokens = (Base) => {
12
13
  class TokensElement extends Base {
13
14
  constructor() {
@@ -16,11 +17,13 @@ export const MixinTokens = (Base) => {
16
17
  this.expandable = false;
17
18
  this.expandLabel = 'Show all';
18
19
  this.maxTags = 0;
20
+ this.restText = '+{count}';
19
21
  this.expanded = false;
20
22
  this.toggleExpanded = () => {
21
23
  this.expanded = !this.expanded;
22
24
  };
23
25
  }
26
+ static { this.words = ''; }
24
27
  get shownLimit() {
25
28
  return this.expanded ? 0 : this.maxTags;
26
29
  }
@@ -29,7 +32,9 @@ export const MixinTokens = (Base) => {
29
32
  }
30
33
  renderRest(over) {
31
34
  if (!this.expandable) {
32
- return over > 0 ? html `<span part="rest">${`+${over}`}</span>` : nothing;
35
+ return over > 0
36
+ ? html `<span part="rest">${formatWords(this, 'restText', { count: over })}</span>`
37
+ : nothing;
33
38
  }
34
39
  if (over < 1 && !this.expanded) {
35
40
  return nothing;
@@ -39,7 +44,7 @@ export const MixinTokens = (Base) => {
39
44
  type="button"
40
45
  aria-label=${this.expanded ? this.collapseLabel : this.expandLabel}
41
46
  @click=${this.toggleExpanded}
42
- >${this.expanded ? drawIcon(caretUp) : `+${over}`}</button
47
+ >${this.expanded ? drawIcon(caretUp) : formatWords(this, 'restText', { count: over })}</button
43
48
  >`;
44
49
  }
45
50
  hideOverflow(slot) {
@@ -64,17 +69,23 @@ export const MixinTokens = (Base) => {
64
69
  removeLastToken() { }
65
70
  }
66
71
  __decorate([
72
+ words,
67
73
  property({ attribute: 'collapse-label' })
68
74
  ], TokensElement.prototype, "collapseLabel", void 0);
69
75
  __decorate([
70
76
  property({ type: Boolean })
71
77
  ], TokensElement.prototype, "expandable", void 0);
72
78
  __decorate([
79
+ words,
73
80
  property({ attribute: 'expand-label' })
74
81
  ], TokensElement.prototype, "expandLabel", void 0);
75
82
  __decorate([
76
83
  property({ attribute: 'max-tags', type: Number })
77
84
  ], TokensElement.prototype, "maxTags", void 0);
85
+ __decorate([
86
+ words,
87
+ property({ attribute: 'rest-text' })
88
+ ], TokensElement.prototype, "restText", void 0);
78
89
  __decorate([
79
90
  state()
80
91
  ], TokensElement.prototype, "expanded", void 0);
@@ -15,7 +15,13 @@ export type ViewportGestures = 'activate' | 'always' | 'cooperative';
15
15
  * the cooperative set, or none, while an `activate` surface waits to be pressed.
16
16
  */
17
17
  export type ViewportGestureMode = 'cooperative' | 'free' | 'off';
18
- /** The words a surface says about its gestures, where the page does not say them. */
18
+ /**
19
+ * The words a surface says about its gestures.
20
+ *
21
+ * @deprecated Each family declares its gesture words as word properties,
22
+ * `activate-hint-label` and its three, which a page registers by locale.
23
+ * Removed in the next major version.
24
+ */
19
25
  export interface ViewportGestureLabels {
20
26
  /** The hint while an `activate` surface waits to be pressed. */
21
27
  activate: string;
@@ -38,18 +44,16 @@ export declare class ViewportElement {
38
44
  fullscreenLabel: string;
39
45
  /** Which gestures the surface takes from the page. Unset is the family's own. */
40
46
  gestures?: ViewportGestures;
41
- /** The hint while a surface with `gestures="activate"` waits to be pressed. */
42
- activateHintLabel?: string;
43
- /** The hint when one finger swipes over a surface with `gestures="cooperative"`. */
44
- twoFingerHintLabel?: string;
45
- /** The line while a surface with `gestures="activate"` has the gestures. */
46
- usingLabel?: string;
47
- /** The hint when the wheel scrolls past a surface with `gestures="cooperative"`, with `{key}`. */
48
- wheelHintLabel?: string;
47
+ /** The hint while a surface with `gestures="activate"` waits to be pressed, a word property each family declares. */
48
+ activateHintLabel: string;
49
+ /** The hint when one finger swipes over a surface with `gestures="cooperative"`, a word property each family declares. */
50
+ twoFingerHintLabel: string;
51
+ /** The line while a surface with `gestures="activate"` has the gestures, a word property each family declares. */
52
+ usingLabel: string;
53
+ /** The hint when the wheel scrolls past a surface with `gestures="cooperative"`, with `{key}`, a word property each family declares. */
54
+ wheelHintLabel: string;
49
55
  /** @internal Which gestures the thing drawing the surface takes right now. */
50
56
  get gestureMode(): ViewportGestureMode;
51
- /** What the surface says about its gestures where the page does not say it, which each family says for itself. */
52
- protected get restingGestureLabels(): ViewportGestureLabels;
53
57
  /** Whether an `activate` surface has been pressed, and has the gestures. */
54
58
  protected gesturesTaken: boolean;
55
59
  /** Whether a gesture that started with this event is the surface's to answer. */
@@ -92,10 +96,8 @@ export declare class ViewportElement {
92
96
  protected readonly fullscreen: FullscreenController;
93
97
  /** The key relay, bound by the family's own frame. */
94
98
  protected handleKeydown: (event: KeyboardEvent) => void;
95
- /** The name of the group of controls, read by screen readers. */
96
- controlsLabel: string | undefined;
97
- /** What the group of controls is called where the page does not say, which each family says for itself. */
98
- protected get restingControlsLabel(): string;
99
+ /** The name of the group of controls, read by screen readers, a word property each family declares. */
100
+ controlsLabel: string;
99
101
  /** The pan event, built and not dispatched, for a family that names its own. */
100
102
  protected panEvent(detail: ViewportPanDetail): Event;
101
103
  /** The zoom event, built and not dispatched, for a family that names its own. */
@@ -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
- const words = this.wheelHintLabel ?? this.restingGestureLabels.wheel;
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 ?? this.restingGestureLabels.activate, event);
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 ?? this.restingGestureLabels.twoFinger, finger);
53
+ this.showHint(this.twoFingerHintLabel, finger);
55
54
  }
56
55
  else if (this.gestures === 'activate' && !this.gesturesTaken) {
57
- this.showHint(this.activateHintLabel ?? this.restingGestureLabels.activate, finger);
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
- get restingControlsLabel() {
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 ?? this.restingGestureLabels.using}</div>`;
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.`);
@@ -286,27 +275,28 @@ export const MixinViewport = (Base) => {
286
275
  if (!pan && !zoom && !fullscreen && more === nothing) {
287
276
  return nothing;
288
277
  }
289
- return html `<div part="controls" role="group" aria-label=${this.controlsLabel ?? this.restingControlsLabel}>
278
+ const [up, start, end, down] = ['up', 'start', 'end', 'down'].map((way) => this.panGlyph(way));
279
+ return html `<div part="controls" role="group" aria-label=${this.controlsLabel}>
290
280
  ${pan
291
281
  ? html `<button part="pan-up" aria-label=${this.panLabel('up')} type="button" @click=${() => this.pan('up')}>
292
- ${this.panGlyph('up')}
282
+ <slot name="pan-up-icon">${up}</slot>
293
283
  </button>
294
284
  <button part="pan-start" aria-label=${this.panLabel('start')} type="button" @click=${() => this.pan('start')}>
295
- ${this.panGlyph('start')}
285
+ <slot name="pan-start-icon">${start}</slot>
296
286
  </button>
297
287
  <button part="pan-end" aria-label=${this.panLabel('end')} type="button" @click=${() => this.pan('end')}>
298
- ${this.panGlyph('end')}
288
+ <slot name="pan-end-icon">${end}</slot>
299
289
  </button>
300
290
  <button part="pan-down" aria-label=${this.panLabel('down')} type="button" @click=${() => this.pan('down')}>
301
- ${this.panGlyph('down')}
291
+ <slot name="pan-down-icon">${down}</slot>
302
292
  </button>`
303
293
  : nothing}
304
294
  ${zoom
305
295
  ? html `<button part="zoom-in" aria-label=${this.zoomInLabel} type="button" @click=${() => this.zoom(1)}>
306
- +
296
+ <slot name="zoom-in-icon">+</slot>
307
297
  </button>
308
298
  <button part="zoom-out" aria-label=${this.zoomOutLabel} type="button" @click=${() => this.zoom(-1)}>
309
- −
299
+ <slot name="zoom-out-icon">−</slot>
310
300
  </button>`
311
301
  : nothing}
312
302
  ${more}
@@ -318,25 +308,20 @@ export const MixinViewport = (Base) => {
318
308
  type="button"
319
309
  @click=${this.toggleFullscreen}
320
310
  >
321
- ⤢
311
+ <slot name="fullscreen-icon">⤢</slot>
322
312
  </button>`
323
313
  : nothing}
324
314
  </div>`;
325
315
  }
326
316
  }
327
- __decorate([
328
- property({ attribute: 'activate-hint-label' })
329
- ], Viewport.prototype, "activateHintLabel", void 0);
330
317
  __decorate([
331
318
  property({ reflect: true })
332
319
  ], Viewport.prototype, "controls", void 0);
333
- __decorate([
334
- property({ attribute: 'controls-label' })
335
- ], Viewport.prototype, "controlsLabel", void 0);
336
320
  __decorate([
337
321
  property({ attribute: 'controls-placement', reflect: true })
338
322
  ], Viewport.prototype, "controlsPlacement", void 0);
339
323
  __decorate([
324
+ words,
340
325
  property({ attribute: 'fullscreen-label' })
341
326
  ], Viewport.prototype, "fullscreenLabel", void 0);
342
327
  __decorate([
@@ -346,30 +331,26 @@ export const MixinViewport = (Base) => {
346
331
  property({ attribute: 'idle-delay', type: Number })
347
332
  ], Viewport.prototype, "idleDelay", void 0);
348
333
  __decorate([
334
+ words,
349
335
  property({ attribute: 'pan-down-label' })
350
336
  ], Viewport.prototype, "panDownLabel", void 0);
351
337
  __decorate([
338
+ words,
352
339
  property({ attribute: 'pan-left-label' })
353
340
  ], Viewport.prototype, "panLeftLabel", void 0);
354
341
  __decorate([
342
+ words,
355
343
  property({ attribute: 'pan-right-label' })
356
344
  ], Viewport.prototype, "panRightLabel", void 0);
357
345
  __decorate([
358
346
  property({ attribute: 'pan-step', type: Number })
359
347
  ], Viewport.prototype, "panStep", void 0);
360
348
  __decorate([
349
+ words,
361
350
  property({ attribute: 'pan-up-label' })
362
351
  ], Viewport.prototype, "panUpLabel", void 0);
363
352
  __decorate([
364
- property({ attribute: 'two-finger-hint-label' })
365
- ], Viewport.prototype, "twoFingerHintLabel", void 0);
366
- __decorate([
367
- property({ attribute: 'using-label' })
368
- ], Viewport.prototype, "usingLabel", void 0);
369
- __decorate([
370
- property({ attribute: 'wheel-hint-label' })
371
- ], Viewport.prototype, "wheelHintLabel", void 0);
372
- __decorate([
353
+ words,
373
354
  property({ attribute: 'zoom-in-label' })
374
355
  ], Viewport.prototype, "zoomInLabel", void 0);
375
356
  __decorate([
@@ -388,6 +369,7 @@ export const MixinViewport = (Base) => {
388
369
  state()
389
370
  ], Viewport.prototype, "hintAt", void 0);
390
371
  __decorate([
372
+ words,
391
373
  property({ attribute: 'zoom-out-label' })
392
374
  ], Viewport.prototype, "zoomOutLabel", void 0);
393
375
  return Viewport;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Sends each child to the slot it names, and every other child to the default
3
+ * slot, in a shadow root that assigns its slots by hand.
4
+ *
5
+ * **A root that assigns by hand routes nothing by itself**, its named slots
6
+ * included, so a header, a footer or an empty state slotted by name reaches
7
+ * nothing unless it is assigned here too. `place` says where a child goes when
8
+ * the host decides it, a row to its list or a card to the slot the layout drew
9
+ * for it, and a child it gives no slot to goes where its `slot` attribute says,
10
+ * as it would under the default assignment.
11
+ *
12
+ * **A slot already holding exactly what it would be given is left alone**, so
13
+ * calling this after every update fires no `slotchange` that nothing changed.
14
+ *
15
+ * Pair it with `ChildrenController`, which calls back when a child arrives
16
+ * after the first render, the case no `slotchange` reports.
17
+ */
18
+ export declare const assignSlots: (root: ParentNode | null | undefined, children: Iterable<Element>, place?: (child: Element) => string | undefined) => void;
@@ -0,0 +1,14 @@
1
+ export const assignSlots = (root, children, place = () => undefined) => {
2
+ const named = new Map();
3
+ for (const child of children) {
4
+ const name = place(child) ?? child.getAttribute('slot') ?? '';
5
+ named.set(name, [...(named.get(name) ?? []), child]);
6
+ }
7
+ for (const slot of root?.querySelectorAll?.('slot') ?? []) {
8
+ const wanted = named.get(slot.name) ?? [];
9
+ const had = slot.assignedNodes();
10
+ if (had.length !== wanted.length || had.some((one, at) => one !== wanted[at])) {
11
+ slot.assign?.(...wanted);
12
+ }
13
+ }
14
+ };
@@ -0,0 +1,40 @@
1
+ import { nothing, type TemplateResult } from 'lit';
2
+ /** What the hidden native select offers a browser's autofill, and what it does with the answer. */
3
+ export interface DrawAutofillOptions {
4
+ /** The autofill token, such as `country` or `cc-exp-month`. Empty draws nothing. */
5
+ autocomplete: string;
6
+ /** Whether the select is disabled, which takes it out of autofill too. */
7
+ disabled: boolean;
8
+ /** Whether more than one option can be chosen. */
9
+ multiple: boolean;
10
+ /** Takes what the browser filled in, as a value: several are joined by commas. */
11
+ onFill: (value: string) => void;
12
+ /** The options, each with its value and its words, in order. */
13
+ options: readonly {
14
+ text: string;
15
+ value: string;
16
+ }[];
17
+ /** The value now, several joined by commas. */
18
+ value: string;
19
+ }
20
+ /**
21
+ * A native `<select>` that a browser's autofill can fill, holding the same
22
+ * options as the custom select beside it. Select, Combobox and Listbox draw it
23
+ * under `autocomplete`.
24
+ *
25
+ * **A browser fills native fields only.** It reads `autocomplete`, picks an
26
+ * option by its value or its words, and sends `change`, which `onFill` turns
27
+ * into the custom select's value. Chromium fills a select inside a shadow root
28
+ * when its host is inside the form being filled.
29
+ *
30
+ * **The select's own `input` stops here.** It is composed, so it crossed the
31
+ * shadow root and reached the host before `change` did, with the host's value
32
+ * still the old one: measured in Chromium, a fill sent a page's `input`
33
+ * listener two events, the first carrying the old value. The host sends its
34
+ * own `input` and `change` once the value is taken.
35
+ *
36
+ * **Hidden from everyone but the browser**: clipped to a pixel, `aria-hidden`,
37
+ * and out of the tab order, with no `name`, so the form still receives the
38
+ * value once, from the custom select itself.
39
+ */
40
+ export declare const drawAutofill: ({ autocomplete, disabled, multiple, onFill, options, value, }: DrawAutofillOptions) => TemplateResult | typeof nothing;
@@ -0,0 +1,14 @@
1
+ import { html, nothing } from 'lit';
2
+ const HIDDEN = 'position:fixed;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none';
3
+ export const drawAutofill = ({ autocomplete, disabled, multiple, onFill, options, value, }) => {
4
+ if (autocomplete === '') {
5
+ return nothing;
6
+ }
7
+ const chosen = value.split(',');
8
+ const fill = (event) => onFill([...event.currentTarget.selectedOptions]
9
+ .map((option) => option.value)
10
+ .filter(Boolean)
11
+ .join(','));
12
+ const stop = (event) => event.stopPropagation();
13
+ return html `<select aria-hidden="true" autocomplete=${autocomplete} style=${HIDDEN} tabindex="-1" ?disabled=${disabled} ?multiple=${multiple} @change=${fill} @input=${stop}><option value=""></option>${options.map((option) => html `<option value=${option.value} ?selected=${chosen.includes(option.value)}>${option.text}</option>`)}</select>`;
14
+ };
@@ -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();
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Scrolls `box` to put `item`, one of the things it scrolls, in its middle.
3
+ *
4
+ * For a list read as a dial rather than scanned, as Time Picker's wheels read
5
+ * a Listbox. A delta from the two boxes rather than `offsetTop`: a slotted
6
+ * item's `offsetParent` is whatever is positioned above it, which is not the
7
+ * box and is not guaranteed to be anything.
8
+ *
9
+ * **Twice, and the second pass is not superstition.** The first delta is
10
+ * computed against whatever layout is current when this is called, and inside
11
+ * a panel that has just been revealed it is a frame behind: measured, one call
12
+ * landed a whole row short, 672 where 704 was wanted, and calling it again
13
+ * from a settled layout put it exactly on center. Reading a rect forces
14
+ * layout, so the second pass sees the result of the first and corrects the
15
+ * remainder. It stops as soon as there is nothing left to correct, and where
16
+ * either box has no height, as on a page not laid out.
17
+ */
18
+ export declare const centerInView: (box: HTMLElement, item: Element) => void;
@@ -0,0 +1,14 @@
1
+ export const centerInView = (box, item) => {
2
+ for (let pass = 0; pass < 2; pass += 1) {
3
+ const outer = box.getBoundingClientRect();
4
+ const inner = item.getBoundingClientRect();
5
+ if (outer.height === 0 || inner.height === 0) {
6
+ return;
7
+ }
8
+ const off = inner.top + inner.height / 2 - (outer.top + outer.height / 2);
9
+ if (Math.abs(off) < 1) {
10
+ return;
11
+ }
12
+ box.scrollTop += off;
13
+ }
14
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The words and glyphs a chord is drawn with, per platform.
3
+ *
4
+ * In core beside `ariaChord`, which says a chord to a reader as this draws it
5
+ * for the eye: Kbd draws a chord with it, and Speed Dial draws an action's
6
+ * shortcut with it where nothing is slotted, without importing Kbd.
7
+ */
8
+ /** A chord's keys as they are shown, and what is shown between two of them. */
9
+ export interface KeyNames {
10
+ /** Each key's label, in the order the chord names them. */
11
+ keys: string[];
12
+ /** What goes between two keys: `+`, a hyphen, or nothing. */
13
+ separator: string;
14
+ }
15
+ /**
16
+ * The labels of a chord's keys on a platform, with `own` labels in place of
17
+ * the platform's, and what goes between two keys.
18
+ *
19
+ * `parts` are the chord's names as `chordParts` splits them. A name `own`
20
+ * holds takes that label, a name the platform knows takes its glyph or word,
21
+ * and any other single character is shown in capitals.
22
+ *
23
+ * **On an Apple keyboard glyphs run together and words are joined with a
24
+ * hyphen**, as Apple writes `Command-K`. A label of more than one character
25
+ * from `own` is a word, so `{"mod": "Command"}` shows `Command-K` there, where
26
+ * nothing between the two ran them into `CommandK`. The platform's own `Esc`
27
+ * and `Space` keep running on from a glyph, as `⌘Esc` always has. Everywhere
28
+ * else keys are joined with `+`.
29
+ */
30
+ export declare const keyNames: (parts: readonly string[], apple: boolean, own?: Readonly<Record<string, string>>) => KeyNames;
@@ -0,0 +1,52 @@
1
+ const APPLE = {
2
+ alt: '⌥',
3
+ backspace: '⌫',
4
+ cmd: '⌘',
5
+ ctrl: '⌃',
6
+ del: '⌦',
7
+ down: '↓',
8
+ enter: '↵',
9
+ esc: 'Esc',
10
+ left: '←',
11
+ meta: '⌘',
12
+ mod: '⌘',
13
+ opt: '⌥',
14
+ right: '→',
15
+ shift: '⇧',
16
+ space: 'Space',
17
+ tab: '⇥',
18
+ up: '↑',
19
+ };
20
+ const ELSEWHERE = {
21
+ alt: 'Alt',
22
+ backspace: 'Backspace',
23
+ cmd: 'Win',
24
+ ctrl: 'Ctrl',
25
+ del: 'Del',
26
+ down: '↓',
27
+ enter: 'Enter',
28
+ esc: 'Esc',
29
+ left: '←',
30
+ meta: 'Win',
31
+ mod: 'Ctrl',
32
+ opt: 'Alt',
33
+ right: '→',
34
+ shift: 'Shift',
35
+ space: 'Space',
36
+ tab: 'Tab',
37
+ up: '↑',
38
+ };
39
+ export const keyNames = (parts, apple, own = {}) => {
40
+ const names = apple ? APPLE : ELSEWHERE;
41
+ let worded = false;
42
+ const keys = parts.map((part) => {
43
+ const name = part.toLowerCase();
44
+ const mine = own[name];
45
+ if (mine !== undefined) {
46
+ worded ||= [...mine].length > 1;
47
+ return mine;
48
+ }
49
+ return names[name] ?? (part.length === 1 ? part.toUpperCase() : part);
50
+ });
51
+ return { keys, separator: apple ? (worded ? '-' : '') : '+' };
52
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Where Up or Down moves in a list laid out in columns, as a grid of tiles or
3
+ * a wrapping row is: to the next item after `at` (with `by` of `1`) or the
4
+ * last one before it (`-1`) that starts at the same inline offset, which is
5
+ * the item above or below it. `at` itself where there is none, so the key
6
+ * stops at the edge, as React Aria's grid layout stops.
7
+ *
8
+ * Read from where the items are drawn rather than from a column count, so it
9
+ * holds for a grid of any width, a row that wraps, and a column cut short.
10
+ * `starts` are the items' inline offsets in the order of the list, such as
11
+ * each one's `getBoundingClientRect().left`.
12
+ */
13
+ export declare const columnStep: (starts: readonly number[], at: number, by: -1 | 1) => number;
@@ -0,0 +1,9 @@
1
+ export const columnStep = (starts, at, by) => {
2
+ const column = starts[at];
3
+ for (let index = at + by; index >= 0 && index < starts.length; index += by) {
4
+ if (starts[index] === column) {
5
+ return index;
6
+ }
7
+ }
8
+ return at;
9
+ };
@@ -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". A language with more forms than two writes its `one` in the
8
- * first sentence and the rest in the second.
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
- * Data Table says how many rows a search left this way, and Listbox how many
11
- * options a filter left, which Combobox and Command hand down to it.
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 { fillTemplate } from '../fill-template/fill-template.js';
2
- import { formatNumbers } from '../format-numbers/format-numbers.js';
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);