@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
@@ -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;
@@ -0,0 +1,112 @@
1
+ export const IDLE = { phase: 'idle' };
2
+ export const reorderStep = (state, input) => {
3
+ switch (input.type) {
4
+ case 'press':
5
+ return state.phase === 'idle'
6
+ ? {
7
+ from: input.from,
8
+ key: input.key,
9
+ phase: 'pressed',
10
+ pointer: input.pointer,
11
+ x: input.x,
12
+ y: input.y,
13
+ }
14
+ : null;
15
+ case 'drag':
16
+ if (state.phase === 'pressed') {
17
+ return Math.hypot(input.x - state.x, input.y - state.y) < input.slop
18
+ ? null
19
+ : { by: 'pointer', from: state.from, key: state.key, phase: 'held', to: input.to };
20
+ }
21
+ return state.phase === 'held' && state.by === 'pointer' && state.to !== input.to
22
+ ? { ...state, to: input.to }
23
+ : null;
24
+ case 'release':
25
+ if (state.phase === 'pressed') {
26
+ return IDLE;
27
+ }
28
+ return state.phase === 'held' && state.by === 'pointer'
29
+ ? { ...state, phase: 'dropped' }
30
+ : null;
31
+ case 'pick':
32
+ if (state.phase === 'idle') {
33
+ return { by: 'keyboard', from: input.from, key: input.key, phase: 'held', to: input.from };
34
+ }
35
+ return state.phase === 'held' && state.by === 'keyboard' && state.key === input.key
36
+ ? { ...state, phase: 'dropped' }
37
+ : null;
38
+ case 'step':
39
+ return state.phase === 'held' && state.by === 'keyboard' && state.to !== input.to
40
+ ? { ...state, to: input.to }
41
+ : null;
42
+ case 'target':
43
+ return state.phase === 'held' && state.by === 'keyboard'
44
+ ? { ...state, phase: 'dropped', to: input.to }
45
+ : null;
46
+ case 'drop':
47
+ return state.phase === 'held' ? { ...state, phase: 'dropped' } : null;
48
+ case 'cancel':
49
+ if (state.phase === 'pressed') {
50
+ return IDLE;
51
+ }
52
+ return state.phase === 'held' ? { by: state.by, key: state.key, phase: 'canceled' } : null;
53
+ case 'settle':
54
+ return state.phase === 'dropped' || state.phase === 'canceled' ? IDLE : null;
55
+ }
56
+ };
57
+ export const passedCount = (centers, at, reversed = false) => centers.filter((center) => (reversed ? at < center : at > center)).length;
58
+ export const centersOf = (spans, reversed = false) => {
59
+ const drawn = spans.flatMap((span, index) => (span ? [index] : []));
60
+ const first = drawn[0] ?? spans.length;
61
+ const last = drawn[drawn.length - 1] ?? -1;
62
+ let before = reversed ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
63
+ return spans.map((span, index) => {
64
+ if (span) {
65
+ before = (span.start + span.end) / 2;
66
+ return before;
67
+ }
68
+ if (index < first) {
69
+ return reversed ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
70
+ }
71
+ return index > last ? (reversed ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY) : before;
72
+ });
73
+ };
74
+ export const reorderKey = (key, { axis, count, rtl, to }) => {
75
+ const last = Math.max(0, count - 1);
76
+ if (key === 'Home') {
77
+ return 0;
78
+ }
79
+ if (key === 'End') {
80
+ return last;
81
+ }
82
+ const back = axis === 'block' ? 'ArrowUp' : rtl ? 'ArrowRight' : 'ArrowLeft';
83
+ const forward = axis === 'block' ? 'ArrowDown' : rtl ? 'ArrowLeft' : 'ArrowRight';
84
+ if (key !== back && key !== forward) {
85
+ return null;
86
+ }
87
+ return Math.min(last, Math.max(0, to + (key === forward ? 1 : -1)));
88
+ };
89
+ export const dropMark = (from, to) => to === from ? undefined : { edge: to < from ? 'before' : 'after', index: to };
90
+ export const placeKey = (all, shown, key, to) => {
91
+ const others = shown.filter((one) => one !== key);
92
+ const rest = all.filter((one) => one !== key);
93
+ const beside = others[to - 1] ?? others[0];
94
+ if (beside === undefined) {
95
+ return [...all];
96
+ }
97
+ rest.splice(rest.indexOf(beside) + (to > 0 ? 1 : 0), 0, key);
98
+ return rest;
99
+ };
100
+ export const edgeSpeed = (at, start, end, { most = 12, zone = 40 } = {}) => {
101
+ const reach = Math.min(zone, (end - start) / 2);
102
+ if (reach <= 0) {
103
+ return 0;
104
+ }
105
+ if (at < start + reach) {
106
+ return -Math.round(most * Math.min(1, (start + reach - at) / reach)) || 0;
107
+ }
108
+ if (at > end - reach) {
109
+ return Math.round(most * Math.min(1, (at - (end - reach)) / reach));
110
+ }
111
+ return 0;
112
+ };
@@ -18,8 +18,10 @@
18
18
  */
19
19
  /** What went wrong, stable across versions, so it can be counted. */
20
20
  export type RuntimeKind =
21
+ /** A Blockquote's `cite` holds words, where it takes the source's URL. */
22
+ 'cite-not-url'
21
23
  /** A deprecated property, attribute or slot was used; the message names what replaces it. */
22
- 'deprecated'
24
+ | 'deprecated'
23
25
  /** Another class asked for a tag that is already registered: two copies of Helix. */
24
26
  | 'duplicate-registration'
25
27
  /** The browser refused fullscreen, usually for a frame without the permission. */
@@ -28,7 +30,7 @@ export type RuntimeKind =
28
30
  | 'min-out-of-range'
29
31
  /** A Stat was given one of Trend's directions, `up`, `down` or `neutral`, which lay nothing out. */
30
32
  | 'misread-direction'
31
- /** A React handler prop names an event the element never dispatches itself. */
33
+ /** A React handler prop names an event the element never dispatches itself. Development builds only. */
32
34
  | 'misplaced-handler'
33
35
  /** An Image has no `alt`, so a screen reader announces its file name. */
34
36
  | 'missing-alt'
@@ -57,9 +59,12 @@ export type RuntimeKind =
57
59
  | 'picture-in-picture-refused'
58
60
  /** A Grid's `min` is no length it can write, so the grid lays out by its column count. */
59
61
  | 'refused-length'
60
- /** A React handler prop is a removed callback, which never ran from JSX. */
62
+ /** A React handler prop is a removed callback, which never ran from JSX. Development builds only. */
61
63
  | 'removed-callback'
62
- /** A React handler prop uses Solid's `on:`, which React reads as another event's name. */
64
+ /**
65
+ * A React handler prop uses Solid's `on:`, which React reads as another event's name.
66
+ * Development builds only.
67
+ */
63
68
  | 'solid-handler-syntax'
64
69
  /** A stencil's regions went unused: no named parts, or parts too small. */
65
70
  | 'stencil-regions-unused'
@@ -71,7 +76,7 @@ export type RuntimeKind =
71
76
  | 'two-default-operators'
72
77
  /** A Quote's text starts or ends with a quotation mark, so the browser's marks double it. */
73
78
  | 'typed-marks'
74
- /** A React handler prop names an event nothing dispatches. */
79
+ /** A React handler prop names an event nothing dispatches. Development builds only. */
75
80
  | 'unknown-event'
76
81
  /** An Icon's `name` is in no icon set it can read, so it draws nothing. */
77
82
  | 'unknown-icon'
@@ -79,8 +84,12 @@ export type RuntimeKind =
79
84
  | 'unlinked-trigger'
80
85
  /** A Text is rendered as a label, a deprecated value whose shadow root reaches no control. */
81
86
  | 'unreachable-label'
87
+ /** A QR code holds a logo at a correction level below `H`, so it may not scan. */
88
+ | 'unscannable-logo'
82
89
  /** A virtualized list's box has no height of its own, so its window never settles. */
83
90
  | 'unsettled-window'
91
+ /** A native button in a Button Group with `selection-mode`, which cannot be told it is pressed. */
92
+ | 'untold-toggle'
84
93
  /** A slotted trigger is a plain element, which cannot be told it opens something. */
85
94
  | 'untold-trigger'
86
95
  /** A tooltip wraps its trigger, so its text reaches no screen reader. */
@@ -9,13 +9,25 @@
9
9
  * sent the first and Date Picker the second, so what a server received for the
10
10
  * same answer depended on which control asked for it.
11
11
  *
12
- * **The joined string is the state**, which is what the browser hands back to
13
- * `formStateRestoreCallback` and what `value` holds, so a restored control
14
- * reads the shape it wrote.
12
+ * **The joined string is the state by default**, which is what the browser
13
+ * hands back to `formStateRestoreCallback` and what `value` holds, so a
14
+ * restored control reads the shape it wrote. A control whose answers can hold
15
+ * a comma asks for `state: 'entries'`, and the browser hands back the form
16
+ * data itself, one entry per answer, so `b,1` comes back as one answer and not
17
+ * two.
15
18
  *
16
19
  * **Nothing without a `name`.** A plain string is skipped by the form when its
17
20
  * control has no name, but `FormData` carries its own names and its entries
18
21
  * are appended whatever the element is called, so an unnamed control would
19
22
  * submit empty-named values.
20
23
  */
21
- export declare const reportValues: (internals: ElementInternals, name: string, values: readonly string[]) => void;
24
+ /** How `reportValues` saves the state the browser restores. */
25
+ export interface ReportValuesOptions {
26
+ /**
27
+ * What the browser hands back to `formStateRestoreCallback`: `joined`, the
28
+ * answers joined with commas, or `entries`, the form data with one entry per
29
+ * answer. Defaults to `joined`.
30
+ */
31
+ state?: 'entries' | 'joined';
32
+ }
33
+ export declare const reportValues: (internals: ElementInternals, name: string, values: readonly string[], { state }?: ReportValuesOptions) => void;
@@ -1,4 +1,4 @@
1
- export const reportValues = (internals, name, values) => {
1
+ export const reportValues = (internals, name, values, { state = 'joined' } = {}) => {
2
2
  if (name === '' || values.length === 0) {
3
3
  internals.setFormValue(null);
4
4
  return;
@@ -7,5 +7,5 @@ export const reportValues = (internals, name, values) => {
7
7
  for (const value of values) {
8
8
  data.append(name, value);
9
9
  }
10
- internals.setFormValue(data, values.join(','));
10
+ internals.setFormValue(data, state === 'entries' ? data : values.join(','));
11
11
  };
@@ -35,3 +35,14 @@ export interface ChangeRequestOptions<T> {
35
35
  next: T;
36
36
  }
37
37
  export declare const requestChange: <T>({ apply, event, host, next, }: ChangeRequestOptions<T>) => boolean;
38
+ /**
39
+ * Asks before a reader's press moves a value, then applies it and says so:
40
+ * `helix-before-change` with the value it would leave, which a listener may
41
+ * cancel, and `change` once it has moved.
42
+ *
43
+ * **The order every selectable set in the library keeps**, so a page reads the
44
+ * same pair from a list, a tag group, a heatmap or a legend: the cancelable
45
+ * question first, the platform's `change` after, bubbling and composed as a
46
+ * native control's does. Returns whether the value moved.
47
+ */
48
+ export declare const requestValue: (host: EventTarget, next: string, apply: (value: string) => void) => boolean;
@@ -1,3 +1,4 @@
1
+ import { HelixBeforeChangeEvent } from '../../events/before-change.js';
1
2
  export const requestChange = ({ apply, event, host, next, }) => {
2
3
  if (!host.dispatchEvent(event(next))) {
3
4
  return false;
@@ -5,3 +6,12 @@ export const requestChange = ({ apply, event, host, next, }) => {
5
6
  apply(next);
6
7
  return true;
7
8
  };
9
+ export const requestValue = (host, next, apply) => requestChange({
10
+ apply: (value) => {
11
+ apply(value);
12
+ host.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
13
+ },
14
+ event: (value) => new HelixBeforeChangeEvent({ value }),
15
+ host,
16
+ next,
17
+ });
@@ -27,6 +27,23 @@ export interface ScrollItemOptions {
27
27
  * bar with it.
28
28
  */
29
29
  export declare const scrollingAncestor: (item: HTMLElement, options?: ScrollerOptions) => HTMLElement | null;
30
+ /** What `scrollRoom` takes off a box that scrolls. */
31
+ export interface ScrollRoomOptions {
32
+ /**
33
+ * How much of the box its own content covers while it scrolls, a sticky row
34
+ * of headings, in pixels. Defaults to `0`.
35
+ */
36
+ covered?: number;
37
+ }
38
+ /**
39
+ * How much of a list a reader sees at once, for Page Up and Page Down: the
40
+ * box's own height where it scrolls, less what covers it, and the window's
41
+ * where it does not and the page scrolls instead.
42
+ *
43
+ * **A pixel of slack**, since a box whose content rounds to one pixel more
44
+ * than its height does not scroll, and was paging by the window's height.
45
+ */
46
+ export declare const scrollRoom: (box: Element | null | undefined, { covered }?: ScrollRoomOptions) => number;
30
47
  /** Where `scrollingAncestor` looks, and what it takes for a scroller. */
31
48
  export interface ScrollerOptions {
32
49
  /** The axis whose overflow counts, or either where absent. */
@@ -15,6 +15,7 @@ export const scrollingAncestor = (item, options = {}) => {
15
15
  }
16
16
  return null;
17
17
  };
18
+ export const scrollRoom = (box, { covered = 0 } = {}) => box && box.scrollHeight > box.clientHeight + 1 ? box.clientHeight - covered : window.innerHeight;
18
19
  const overflows = (node, axis) => {
19
20
  const tall = node.scrollHeight > node.clientHeight;
20
21
  const wide = node.scrollWidth > node.clientWidth;
@@ -22,3 +22,11 @@
22
22
  * anything", and a slot showing its fallback does render.
23
23
  */
24
24
  export declare const slotFilled: (slot: HTMLSlotElement | null | undefined) => boolean;
25
+ /**
26
+ * The slot of a name in a shadow root, or its default slot for `''`.
27
+ *
28
+ * `slot[name=""]` matches nothing, since the default slot has no `name`
29
+ * attribute at all, which is the one thing every hand-written lookup had to
30
+ * remember.
31
+ */
32
+ export declare const slotNamed: (root: ParentNode | null | undefined, name: string) => HTMLSlotElement | null;
@@ -1 +1,2 @@
1
1
  export const slotFilled = (slot) => (slot?.assignedNodes({ flatten: true }) ?? []).some((node) => node.nodeType === Node.ELEMENT_NODE || node.textContent?.trim() !== '');
2
+ export const slotNamed = (root, name) => root?.querySelector?.(name === '' ? 'slot:not([name])' : `slot[name="${name}"]`) ?? null;
@@ -5,7 +5,8 @@
5
5
  * For a component that says what the page put in it, which is the page's own
6
6
  * text and not anything this library wrote: an empty state's heading and
7
7
  * message, a banner's message. Text nodes count toward the default slot, as
8
- * they are assigned to it. Nothing on a server, whose element has no children
9
- * to read.
8
+ * they are assigned to it; comments, such as the markers a template writes
9
+ * around its text, are left out. Nothing on a server, whose element has no
10
+ * children to read.
10
11
  */
11
12
  export declare const slottedWords: (host: Element, slots: readonly string[]) => string;
@@ -1,5 +1,7 @@
1
1
  export const slottedWords = (host, slots) => [...(host.childNodes ?? [])]
2
- .filter((node) => (node instanceof Element ? slots.includes(node.slot) : slots.includes('')))
2
+ .filter((node) => node instanceof Element
3
+ ? slots.includes(node.slot)
4
+ : node.nodeType === 3 && slots.includes(''))
3
5
  .map((node) => node.textContent?.replace(/\s+/g, ' ').trim() ?? '')
4
6
  .filter(Boolean)
5
7
  .join(' ');
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The toolbar an element sits in, or `null`.
3
+ *
4
+ * Read from the parent up, so a toolbar is never its own. One answer for every
5
+ * family that changes inside a toolbar: Toolbar becomes a group of the outer
6
+ * one, a multiple Toggle Group becomes a group, and Divider turns across the
7
+ * toolbar's axis. By tag and by role, since a family other than Toolbar cannot
8
+ * import Toolbar's class; Toolbar adds its own class for a third tag.
9
+ *
10
+ * `closest` stops at a shadow root, which is what a slotted child wants: its
11
+ * toolbar is its light-DOM parent or above.
12
+ */
13
+ export declare const toolbarAround: (element: Element) => Element | null;
14
+ /**
15
+ * The axis a toolbar runs along: its own `orientation`, else the page's
16
+ * `aria-orientation`, else horizontal, which is the role's default.
17
+ */
18
+ export declare const toolbarAxis: (toolbar: Element) => 'horizontal' | 'vertical';
@@ -0,0 +1,5 @@
1
+ const TOOLBAR = '[role="toolbar"], helix-toolbar, helix-headless-toolbar';
2
+ export const toolbarAround = (element) => element.parentElement?.closest(TOOLBAR) ?? null;
3
+ export const toolbarAxis = (toolbar) => (toolbar.getAttribute('orientation') ?? toolbar.getAttribute('aria-orientation')) === 'vertical'
4
+ ? 'vertical'
5
+ : 'horizontal';
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * release, so the two never disagree; a spec beside this holds them to it.
7
7
  * Nothing else in the running code knows which release it is.
8
8
  */
9
- export declare const VERSION = "0.147.0";
9
+ export declare const VERSION = "0.149.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.147.0';
1
+ export const VERSION = '0.149.0';
@@ -0,0 +1,3 @@
1
+ export { formatMessage, type Message, type MessageOptions, type MessageTable, type MessageValues, } from './message.js';
2
+ export { clearWords, type HelixWords, localeChain, refreshWords, registerWords, setDefaultWordsLocale, type Words, } from './registry.js';
3
+ export { handedLocale, wordsLocale } from './words-locale.js';
@@ -0,0 +1,3 @@
1
+ export { formatMessage, } from './message.js';
2
+ export { clearWords, localeChain, refreshWords, registerWords, setDefaultWordsLocale, } from './registry.js';
3
+ export { handedLocale, wordsLocale } from './words-locale.js';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A sentence a component says, written in a small subset of ICU
3
+ * MessageFormat, the syntax translation tools already read:
4
+ *
5
+ * - `{name}`, a value put in where the sentence says. A number is written in
6
+ * the element's locale.
7
+ * - `{n, number}`, `{n, number, percent}` and `{n, number, integer}`.
8
+ * - `{n, plural, =0 {...} one {# ...} other {...}}`, with `offset:`, and
9
+ * `{n, selectordinal, ...}` in the same shape. `#` is the number.
10
+ * - `{x, select, a {...} other {...}}`.
11
+ * - `''` for one apostrophe, and `'{'` for a brace written as text. A lone
12
+ * apostrophe is text, so "L'élément" needs no escaping.
13
+ */
14
+ export type Message = string;
15
+ /** A word table, such as `color-words`. Its entries merge one by one. */
16
+ export type MessageTable = Readonly<Record<string, Message>>;
17
+ /** The values a message puts in, by name. */
18
+ export type MessageValues = Readonly<Record<string, string | number>>;
19
+ /** How a message is formatted. */
20
+ export interface MessageOptions {
21
+ /**
22
+ * Wraps each value in Unicode isolates (U+2068 and U+2069), so a number or
23
+ * a Latin name keeps its order inside a right-to-left sentence.
24
+ */
25
+ isolate?: boolean;
26
+ /**
27
+ * The locale whose plural rules pick a form, where it is not the locale
28
+ * the numbers are written in. A message from the English fallback counts
29
+ * with English rules on a Polish page, because its forms are English.
30
+ */
31
+ plurals?: string;
32
+ }
33
+ /**
34
+ * A message with its values put in, its numbers written in `locale`, and its
35
+ * plural forms picked by the rules of `options.plurals`, or of `locale`.
36
+ *
37
+ * **A value that is missing leaves its placeholder as written**, so
38
+ * `{count}` shows on the page as the mistake it is. An unknown argument type
39
+ * writes the value as text, so a type added later changes only the messages
40
+ * that use it.
41
+ *
42
+ * ```js
43
+ * formatMessage('{count, plural, one {# wynik} few {# wyniki} many {# wyników} other {# wyniku}}', { count: 5 }, 'pl')
44
+ * // '5 wyników'
45
+ * ```
46
+ */
47
+ export declare const formatMessage: (message: Message, values: MessageValues, locale: string, options?: MessageOptions) => string;