@fusion.dev/helix-core 0.145.0 → 0.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +6585 -1920
- package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +3 -2
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +8 -17
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +71 -3
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +50 -9
- package/dist/controllers/chart-pointer-controller/reading-step.d.ts +7 -1
- package/dist/controllers/chart-pointer-controller/reading-step.js +5 -0
- package/dist/controllers/copy-controller/copy-controller.d.ts +4 -1
- package/dist/controllers/copy-controller/copy-controller.js +2 -1
- package/dist/controllers/countdown-controller/countdown-controller.d.ts +4 -1
- package/dist/controllers/countdown-controller/countdown-controller.js +3 -1
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +14 -55
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +7 -39
- package/dist/controllers/relative-clock-controller/relative-clock-controller.d.ts +44 -0
- package/dist/controllers/relative-clock-controller/relative-clock-controller.js +31 -0
- package/dist/controllers/source-controller/source-controller.d.ts +10 -3
- package/dist/controllers/source-controller/source-controller.js +9 -4
- package/dist/controllers/swipe-controller/swipe-controller.d.ts +10 -1
- package/dist/controllers/swipe-controller/swipe-controller.js +1 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +48 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.js +52 -7
- package/dist/events/action.d.ts +12 -0
- package/dist/events/action.js +6 -0
- package/dist/events/activate.d.ts +22 -0
- package/dist/events/activate.js +11 -0
- package/dist/events/aura-fallback.d.ts +12 -0
- package/dist/events/aura-fallback.js +11 -0
- package/dist/events/band.d.ts +13 -0
- package/dist/events/band.js +11 -0
- package/dist/events/before-activate.d.ts +24 -0
- package/dist/events/before-activate.js +11 -0
- package/dist/events/before-change.d.ts +26 -0
- package/dist/events/before-change.js +11 -0
- package/dist/events/before-close.d.ts +6 -0
- package/dist/events/before-close.js +11 -0
- package/dist/events/before-collapse.d.ts +6 -0
- package/dist/events/before-collapse.js +11 -0
- package/dist/events/before-edit.d.ts +6 -0
- package/dist/events/before-edit.js +11 -0
- package/dist/events/before-move.d.ts +17 -0
- package/dist/events/before-move.js +11 -0
- package/dist/events/before-open.d.ts +17 -0
- package/dist/events/before-open.js +11 -0
- package/dist/events/before-panel-resize.d.ts +13 -0
- package/dist/events/before-panel-resize.js +11 -0
- package/dist/events/before-pin.d.ts +6 -0
- package/dist/events/before-pin.js +11 -0
- package/dist/events/before-reconnect.d.ts +14 -0
- package/dist/events/before-reconnect.js +11 -0
- package/dist/events/before-relabel.d.ts +15 -0
- package/dist/events/before-relabel.js +11 -0
- package/dist/events/before-resize.d.ts +6 -0
- package/dist/events/before-resize.js +11 -0
- package/dist/events/before-split.d.ts +12 -0
- package/dist/events/before-split.js +11 -0
- package/dist/events/before-toggle.d.ts +23 -0
- package/dist/events/before-toggle.js +11 -0
- package/dist/events/canvas-edit.d.ts +16 -0
- package/dist/events/canvas-edit.js +11 -0
- package/dist/events/canvas-pan.d.ts +13 -0
- package/dist/events/canvas-pan.js +11 -0
- package/dist/events/canvas-view.d.ts +14 -0
- package/dist/events/canvas-view.js +11 -0
- package/dist/events/canvas-zoom.d.ts +6 -0
- package/dist/events/canvas-zoom.js +11 -0
- package/dist/events/caret.d.ts +17 -0
- package/dist/events/caret.js +11 -0
- package/dist/events/clear.d.ts +11 -0
- package/dist/events/clear.js +6 -0
- package/dist/events/collapse.d.ts +12 -0
- package/dist/events/collapse.js +11 -0
- package/dist/events/complete.d.ts +26 -0
- package/dist/events/complete.js +11 -0
- package/dist/events/connect.d.ts +14 -0
- package/dist/events/connect.js +11 -0
- package/dist/events/console.d.ts +13 -0
- package/dist/events/console.js +11 -0
- package/dist/events/copy-error.d.ts +9 -0
- package/dist/events/copy-error.js +6 -0
- package/dist/events/copy.d.ts +12 -0
- package/dist/events/copy.js +10 -0
- package/dist/events/create.d.ts +18 -0
- package/dist/events/create.js +11 -0
- package/dist/events/details.d.ts +459 -0
- package/dist/events/details.js +1 -0
- package/dist/events/dismiss.d.ts +16 -0
- package/dist/events/dismiss.js +11 -0
- package/dist/events/edit.d.ts +12 -0
- package/dist/events/edit.js +11 -0
- package/dist/events/end.d.ts +9 -0
- package/dist/events/end.js +6 -0
- package/dist/events/events.d.ts +113 -1478
- package/dist/events/events.js +93 -863
- package/dist/events/expand.d.ts +10 -0
- package/dist/events/expand.js +11 -0
- package/dist/events/facet.d.ts +17 -0
- package/dist/events/facet.js +11 -0
- package/dist/events/filter.d.ts +12 -0
- package/dist/events/filter.js +11 -0
- package/dist/events/filtered.d.ts +12 -0
- package/dist/events/filtered.js +11 -0
- package/dist/events/graph-filter.d.ts +10 -0
- package/dist/events/graph-filter.js +11 -0
- package/dist/events/graph-layout.d.ts +10 -0
- package/dist/events/graph-layout.js +11 -0
- package/dist/events/graph-pan.d.ts +9 -0
- package/dist/events/graph-pan.js +11 -0
- package/dist/events/graph-part-change.d.ts +10 -0
- package/dist/events/graph-part-change.js +11 -0
- package/dist/events/graph-ready.d.ts +8 -0
- package/dist/events/graph-ready.js +6 -0
- package/dist/events/graph-signal.d.ts +11 -0
- package/dist/events/graph-signal.js +11 -0
- package/dist/events/graph-zoom.d.ts +6 -0
- package/dist/events/graph-zoom.js +11 -0
- package/dist/events/history.d.ts +16 -0
- package/dist/events/history.js +11 -0
- package/dist/events/load-more.d.ts +12 -0
- package/dist/events/load-more.js +6 -0
- package/dist/events/load.d.ts +12 -0
- package/dist/events/load.js +6 -0
- package/dist/events/map-cluster.d.ts +15 -0
- package/dist/events/map-cluster.js +11 -0
- package/dist/events/map-pan.d.ts +18 -0
- package/dist/events/map-pan.js +11 -0
- package/dist/events/map-part-change.d.ts +16 -0
- package/dist/events/map-part-change.js +11 -0
- package/dist/events/map-ready.d.ts +15 -0
- package/dist/events/map-ready.js +6 -0
- package/dist/events/map-rotate.d.ts +9 -0
- package/dist/events/map-rotate.js +11 -0
- package/dist/events/map-tilt.d.ts +9 -0
- package/dist/events/map-tilt.js +11 -0
- package/dist/events/map-zoom.d.ts +13 -0
- package/dist/events/map-zoom.js +11 -0
- package/dist/events/move.d.ts +11 -0
- package/dist/events/move.js +11 -0
- package/dist/events/names.d.ts +18 -0
- package/dist/events/names.js +13 -0
- package/dist/events/navigate.d.ts +13 -0
- package/dist/events/navigate.js +11 -0
- package/dist/events/next.d.ts +23 -0
- package/dist/events/next.js +11 -0
- package/dist/events/panel-resize.d.ts +12 -0
- package/dist/events/panel-resize.js +11 -0
- package/dist/events/pin.d.ts +12 -0
- package/dist/events/pin.js +11 -0
- package/dist/events/press.d.ts +19 -0
- package/dist/events/press.js +11 -0
- package/dist/events/preview.d.ts +14 -0
- package/dist/events/preview.js +11 -0
- package/dist/events/previous.d.ts +6 -0
- package/dist/events/previous.js +11 -0
- package/dist/events/range-change.d.ts +10 -0
- package/dist/events/range-change.js +11 -0
- package/dist/events/read.d.ts +19 -0
- package/dist/events/read.js +11 -0
- package/dist/events/reconnect.d.ts +6 -0
- package/dist/events/reconnect.js +11 -0
- package/dist/events/reject.d.ts +13 -0
- package/dist/events/reject.js +11 -0
- package/dist/events/relabel.d.ts +6 -0
- package/dist/events/relabel.js +11 -0
- package/dist/events/remove.d.ts +12 -0
- package/dist/events/remove.js +11 -0
- package/dist/events/reorder.d.ts +18 -0
- package/dist/events/reorder.js +11 -0
- package/dist/events/reordered.d.ts +12 -0
- package/dist/events/reordered.js +11 -0
- package/dist/events/resize.d.ts +19 -0
- package/dist/events/resize.js +11 -0
- package/dist/events/resized.d.ts +16 -0
- package/dist/events/resized.js +11 -0
- package/dist/events/sample-end.d.ts +5 -0
- package/dist/events/sample-end.js +6 -0
- package/dist/events/sample-start.d.ts +18 -0
- package/dist/events/sample-start.js +6 -0
- package/dist/events/scrolled.d.ts +32 -0
- package/dist/events/scrolled.js +11 -0
- package/dist/events/select.d.ts +12 -0
- package/dist/events/select.js +11 -0
- package/dist/events/selected.d.ts +12 -0
- package/dist/events/selected.js +11 -0
- package/dist/events/send.d.ts +18 -0
- package/dist/events/send.js +11 -0
- package/dist/events/shortcut.d.ts +13 -0
- package/dist/events/shortcut.js +11 -0
- package/dist/events/sort.d.ts +17 -0
- package/dist/events/sort.js +11 -0
- package/dist/events/sorted.d.ts +15 -0
- package/dist/events/sorted.js +11 -0
- package/dist/events/split.d.ts +12 -0
- package/dist/events/split.js +11 -0
- package/dist/events/toggle.d.ts +15 -0
- package/dist/events/toggle.js +11 -0
- package/dist/events/tour-ask.d.ts +16 -0
- package/dist/events/tour-ask.js +11 -0
- package/dist/events/tour-status.d.ts +14 -0
- package/dist/events/tour-status.js +11 -0
- package/dist/events/typing.d.ts +10 -0
- package/dist/events/typing.js +11 -0
- package/dist/helix-element/helix-element.js +9 -43
- package/dist/helix-element/react-bridge.d.ts +23 -0
- package/dist/helix-element/react-bridge.js +2 -2
- package/dist/helix-form-element/helix-form-element.d.ts +5 -20
- package/dist/helix-form-element/helix-form-element.js +4 -56
- package/dist/index.d.ts +30 -9
- package/dist/index.js +30 -9
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +24 -2
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +23 -1
- package/dist/mixins/mixin-controller-active/mixin-controller-active.js +4 -3
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +5 -2
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +2 -1
- package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +4 -2
- package/dist/mixins/mixin-controller-value/mixin-controller-value.js +7 -2
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +44 -7
- package/dist/mixins/mixin-field/mixin-field.js +2 -2
- package/dist/mixins/mixin-picker-feedback/mixin-picker-feedback.d.ts +42 -0
- package/dist/mixins/mixin-picker-feedback/mixin-picker-feedback.js +46 -0
- package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +7 -4
- package/dist/mixins/mixin-picker-field/mixin-picker-field.js +6 -4
- package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +19 -9
- package/dist/utils/audit-name/audit-name-off.d.ts +10 -0
- package/dist/utils/audit-name/audit-name-off.js +1 -0
- package/dist/utils/audit-name/audit-name.d.ts +26 -8
- package/dist/utils/audit-name/audit-name.js +22 -30
- package/dist/utils/audit-source/audit-source-off.d.ts +10 -0
- package/dist/utils/audit-source/audit-source-off.js +1 -0
- package/dist/utils/audit-source/audit-source.d.ts +19 -0
- package/dist/utils/audit-source/audit-source.js +27 -0
- package/dist/utils/calendar/calendar.js +7 -7
- package/dist/utils/call-listener/call-listener.d.ts +16 -0
- package/dist/utils/call-listener/call-listener.js +17 -0
- package/dist/utils/chord/chord.d.ts +14 -0
- package/dist/utils/chord/chord.js +1 -0
- package/dist/utils/clamp/clamp.d.ts +7 -0
- package/dist/utils/clamp/clamp.js +1 -0
- package/dist/utils/css-color/css-color.js +1 -1
- package/dist/utils/draw-tooltip/draw-tooltip.d.ts +1 -1
- package/dist/utils/draw-tooltip/draw-tooltip.js +3 -1
- package/dist/utils/draw-tooltip/spoken-reading.d.ts +6 -2
- package/dist/utils/draw-tooltip/spoken-reading.js +5 -4
- package/dist/utils/elapsed-time/elapsed-time.d.ts +1 -1
- package/dist/utils/elapsed-time/elapsed-time.js +2 -5
- package/dist/utils/heading-level/heading-level.d.ts +8 -0
- package/dist/utils/heading-level/heading-level.js +4 -0
- package/dist/utils/host-words/host-words.d.ts +16 -0
- package/dist/utils/host-words/host-words.js +13 -0
- package/dist/utils/implicit-submit/implicit-submit.d.ts +25 -0
- package/dist/utils/implicit-submit/implicit-submit.js +52 -0
- package/dist/utils/load-more/load-more.d.ts +25 -0
- package/dist/utils/load-more/load-more.js +1 -0
- package/dist/utils/locale-of/locale-of.d.ts +18 -0
- package/dist/utils/locale-of/locale-of.js +26 -0
- package/dist/utils/name-text/name-text.d.ts +12 -0
- package/dist/utils/name-text/name-text.js +25 -0
- package/dist/utils/nearest/nearest.d.ts +9 -0
- package/dist/utils/nearest/nearest.js +9 -0
- package/dist/utils/number-format/number-format.d.ts +11 -1
- package/dist/utils/number-format/number-format.js +17 -5
- package/dist/utils/own-rule/own-rule.d.ts +16 -0
- package/dist/utils/own-rule/own-rule.js +7 -0
- package/dist/utils/plain-click/plain-click.d.ts +10 -0
- package/dist/utils/plain-click/plain-click.js +1 -0
- package/dist/utils/platform/platform.d.ts +8 -0
- package/dist/utils/platform/platform.js +1 -0
- package/dist/utils/press-type/press-type.d.ts +20 -0
- package/dist/utils/press-type/press-type.js +15 -0
- package/dist/utils/read-series/read-series.d.ts +14 -0
- package/dist/utils/read-series/read-series.js +15 -0
- package/dist/utils/relative-time/relative-time.d.ts +8 -0
- package/dist/utils/relative-time/relative-time.js +5 -2
- package/dist/utils/report-render/report-render.js +2 -14
- package/dist/utils/report-runtime/report-runtime.d.ts +11 -4
- package/dist/utils/report-runtime/report-runtime.js +2 -14
- package/dist/utils/report-usage/report-usage.js +2 -14
- package/dist/utils/row-data/row-data.d.ts +15 -0
- package/dist/utils/row-data/row-data.js +17 -0
- package/dist/utils/row-order/row-order.d.ts +141 -0
- package/dist/utils/row-order/row-order.js +76 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +33 -0
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +13 -3
- package/dist/utils/slotted-glyph/slotted-glyph.d.ts +33 -0
- package/dist/utils/slotted-glyph/slotted-glyph.js +27 -0
- package/dist/utils/tell-disclosure/tell-disclosure.d.ts +4 -0
- package/dist/utils/tree-arrow/tree-arrow.d.ts +23 -0
- package/dist/utils/tree-arrow/tree-arrow.js +9 -0
- package/dist/utils/typeahead/typeahead.d.ts +75 -0
- package/dist/utils/typeahead/typeahead.js +37 -0
- package/dist/utils/value-as/value-as.d.ts +9 -0
- package/dist/utils/value-as/value-as.js +9 -1
- package/dist/utils/verdict-flags/verdict-flags.d.ts +13 -0
- package/dist/utils/verdict-flags/verdict-flags.js +6 -0
- package/dist/utils/watch-labels/watch-labels.d.ts +16 -0
- package/dist/utils/watch-labels/watch-labels.js +34 -0
- package/dist/utils/words-converter/words-converter.d.ts +14 -0
- package/dist/utils/words-converter/words-converter.js +15 -0
- package/dist/utils/written-list/written-list.d.ts +60 -0
- package/dist/utils/written-list/written-list.js +46 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +29 -5
|
@@ -8,6 +8,8 @@ import { html, nothing } from 'lit';
|
|
|
8
8
|
import { property, state } from 'lit/decorators.js';
|
|
9
9
|
import { isPartial, isTwelveHourLayout, segmentKey, segmentLayout, segmentName, segmentRange, segmentsOf, segmentsValue, segmentText, segmentValueText, shownValue, } from '../../utils/date-segments/date-segments.js';
|
|
10
10
|
import { inlineStep } from '../../utils/inline-step/inline-step.js';
|
|
11
|
+
import { appleTouch } from '../../utils/platform/platform.js';
|
|
12
|
+
import { verdictFlags } from '../../utils/verdict-flags/verdict-flags.js';
|
|
11
13
|
export const MixinSegmentedField = (Base) => {
|
|
12
14
|
class SegmentedFieldElement extends Base {
|
|
13
15
|
constructor() {
|
|
@@ -17,6 +19,7 @@ export const MixinSegmentedField = (Base) => {
|
|
|
17
19
|
this.parts = {};
|
|
18
20
|
this.typed = '';
|
|
19
21
|
this.own = false;
|
|
22
|
+
this.touchApple = appleTouch(typeof navigator === 'undefined' ? undefined : navigator);
|
|
20
23
|
this.entered = undefined;
|
|
21
24
|
this.handleSegmentKeydown = (event) => {
|
|
22
25
|
const element = event.currentTarget;
|
|
@@ -169,7 +172,7 @@ export const MixinSegmentedField = (Base) => {
|
|
|
169
172
|
}
|
|
170
173
|
const anchor = this.anchor ?? undefined;
|
|
171
174
|
const partial = isPartial(this.segmentedKind, this.parts, this.layout);
|
|
172
|
-
const
|
|
175
|
+
const fault = verdictFlags(this.validate());
|
|
173
176
|
if (partial) {
|
|
174
177
|
this.internals.setValidity({ badInput: true }, this.badInputMessage ||
|
|
175
178
|
(this.segmentedKind === 'date' ? 'Enter a whole date.' : 'Enter a whole time.'), anchor);
|
|
@@ -178,8 +181,8 @@ export const MixinSegmentedField = (Base) => {
|
|
|
178
181
|
this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage ||
|
|
179
182
|
(this.segmentedKind === 'date' ? 'Enter a date.' : 'Enter a time.'), anchor);
|
|
180
183
|
}
|
|
181
|
-
else if (
|
|
182
|
-
this.internals.setValidity(
|
|
184
|
+
else if (fault !== null) {
|
|
185
|
+
this.internals.setValidity(fault[0], fault[1], anchor);
|
|
183
186
|
}
|
|
184
187
|
else {
|
|
185
188
|
this.internals.setValidity({});
|
|
@@ -210,17 +213,24 @@ export const MixinSegmentedField = (Base) => {
|
|
|
210
213
|
const { max, min } = segmentRange(kind, this.parts, twelve);
|
|
211
214
|
const text = segmentText(kind, this.parts, twelve, locale);
|
|
212
215
|
const empty = shown === undefined;
|
|
216
|
+
const id = `${this.controlId}-${kind}`;
|
|
217
|
+
const editable = !this.isDisabled && !this.readOnly;
|
|
218
|
+
const spin = !this.touchApple;
|
|
213
219
|
return html `<span
|
|
214
220
|
part="segment${empty ? ' segment-placeholder' : ''}"
|
|
221
|
+
id=${id}
|
|
215
222
|
data-segment=${kind}
|
|
216
|
-
role
|
|
223
|
+
role=${spin ? 'spinbutton' : 'textbox'}
|
|
217
224
|
tabindex=${this.isDisabled ? '-1' : '0'}
|
|
218
225
|
inputmode=${kind === 'dayPeriod' ? nothing : 'numeric'}
|
|
219
|
-
|
|
220
|
-
aria-
|
|
221
|
-
aria-
|
|
222
|
-
aria-
|
|
223
|
-
aria-
|
|
226
|
+
enterkeyhint=${editable ? 'next' : nothing}
|
|
227
|
+
aria-label=${this.labeled ? `${segmentName(kind, locale)},` : segmentName(kind, locale)}
|
|
228
|
+
aria-labelledby=${this.labeled ? `${id} ${this.labelId}` : nothing}
|
|
229
|
+
aria-invalid=${this.invalidity ?? nothing}
|
|
230
|
+
aria-valuemin=${spin ? min : nothing}
|
|
231
|
+
aria-valuemax=${spin ? max : nothing}
|
|
232
|
+
aria-valuenow=${spin ? (shown ?? nothing) : nothing}
|
|
233
|
+
aria-valuetext=${spin ? (empty ? this.emptySegmentLabel : segmentValueText(kind, this.parts, twelve, locale)) : nothing}
|
|
224
234
|
aria-disabled=${this.isDisabled ? 'true' : nothing}
|
|
225
235
|
aria-readonly=${this.readOnly ? 'true' : nothing}
|
|
226
236
|
@keydown=${this.handleSegmentKeydown}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { auditName as audit } from './audit-name.js';
|
|
2
|
+
/**
|
|
3
|
+
* The missing-name warning in a production build: nothing.
|
|
4
|
+
*
|
|
5
|
+
* `#audit-name` resolves here unless the bundler or runtime asks for the
|
|
6
|
+
* `development` condition, so a production build carries none of the audit's
|
|
7
|
+
* code. The signature is the audit's, so a call site reads the same in both
|
|
8
|
+
* builds. See `audit-name.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const auditName: typeof audit;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const auditName = () => { };
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Whether an element has an accessible name, by the routes a page names a
|
|
3
|
-
* control: its own `aria-label`, `aria-labelledby` or `title`, a `<label>`,
|
|
4
|
-
*
|
|
3
|
+
* control: its own `aria-label`, `aria-labelledby` or `title`, a `<label>`,
|
|
4
|
+
* and its content where its role is named by its content.
|
|
5
|
+
*
|
|
6
|
+
* **A `placeholder` counts for no route.** A browser names a control by it
|
|
7
|
+
* where nothing else does, and it is gone from the screen once a person
|
|
8
|
+
* types, so a field named by it alone loses its label as it is filled.
|
|
9
|
+
* React Aria's fields warn on the same terms.
|
|
5
10
|
*
|
|
6
11
|
* **An estimate, cheap on purpose.** It answers whether any route holds words,
|
|
7
12
|
* which is what the warning is about, and leaves the order the routes win in
|
|
8
13
|
* to the browser.
|
|
14
|
+
*
|
|
15
|
+
* **What the control shows as its value is no name**, so the words in
|
|
16
|
+
* `shown` count for no route. A Select with no label names its trigger by the
|
|
17
|
+
* part that shows the choice, and while nothing is chosen that part shows the
|
|
18
|
+
* placeholder, "Choose", which says nothing about what is being chosen.
|
|
9
19
|
*/
|
|
10
|
-
export declare const hasName: (element: HTMLElement) => boolean;
|
|
20
|
+
export declare const hasName: (element: HTMLElement, shown?: Element | null) => boolean;
|
|
11
21
|
/**
|
|
12
22
|
* Warns once, through the runtime channel, when a control has no accessible
|
|
13
23
|
* name: none on the host, and none on the control inside it.
|
|
@@ -18,11 +28,19 @@ export declare const hasName: (element: HTMLElement) => boolean;
|
|
|
18
28
|
* that task, and an element is read once in its life, so a name taken away
|
|
19
29
|
* later is not reported.
|
|
20
30
|
*
|
|
21
|
-
* **In
|
|
22
|
-
*
|
|
23
|
-
*
|
|
31
|
+
* **In a development build only.** Every field and Button calls it, so in
|
|
32
|
+
* production it would cost each of them about 450 bytes gzip for a sentence
|
|
33
|
+
* meant for the person writing the page. Core imports it as `#audit-name`,
|
|
34
|
+
* which `package.json` resolves here under the `development` condition (Vite
|
|
35
|
+
* and webpack in development, `bun --conditions development`,
|
|
36
|
+
* `node --conditions=development`) and to `audit-name-off.ts`, which does
|
|
37
|
+
* nothing, everywhere else. Like every runtime warning it reports through the
|
|
38
|
+
* one channel, and a project that wants the console quiet tells
|
|
39
|
+
* `@fusion.dev/helix-diagnostics` with `core.warnings: false`.
|
|
24
40
|
*
|
|
25
41
|
* `also` answers for a name the component gave through its internals, which no
|
|
26
|
-
* attribute shows. `fix` is the sentence that says how to name it.
|
|
42
|
+
* attribute shows. `fix` is the sentence that says how to name it. `shown` is
|
|
43
|
+
* the part where the control shows its value or its placeholder, whose words
|
|
44
|
+
* are no name.
|
|
27
45
|
*/
|
|
28
|
-
export declare const auditName: (host: HTMLElement, control: () => HTMLElement | null, fix: string, also?: () => boolean) => void;
|
|
46
|
+
export declare const auditName: (host: HTMLElement, control: () => HTMLElement | null, fix: string, also?: () => boolean, shown?: () => Element | null) => void;
|
|
@@ -1,31 +1,13 @@
|
|
|
1
|
+
import { nameText } from '../name-text/name-text.js';
|
|
1
2
|
import { reportRuntime } from '../report-runtime/report-runtime.js';
|
|
2
|
-
const
|
|
3
|
-
if (node instanceof HTMLSlotElement) {
|
|
4
|
-
return node.assignedNodes({ flatten: true }).map(nameText).join('');
|
|
5
|
-
}
|
|
6
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
7
|
-
return node.textContent ?? '';
|
|
8
|
-
}
|
|
9
|
-
if (!(node instanceof Element)) {
|
|
10
|
-
return '';
|
|
11
|
-
}
|
|
12
|
-
if ((node instanceof HTMLElement && node.hidden) || node.getAttribute('aria-hidden') === 'true') {
|
|
13
|
-
return '';
|
|
14
|
-
}
|
|
15
|
-
const said = node.getAttribute('aria-label') ?? node.getAttribute('alt');
|
|
16
|
-
if (said?.trim()) {
|
|
17
|
-
return said;
|
|
18
|
-
}
|
|
19
|
-
return [...node.childNodes].map(nameText).join('');
|
|
20
|
-
};
|
|
21
|
-
const namesWithWords = (ids, from) => {
|
|
3
|
+
const namesWithWords = (ids, from, shown) => {
|
|
22
4
|
const root = from.getRootNode();
|
|
23
5
|
return (ids ?? '')
|
|
24
6
|
.split(/\s+/)
|
|
25
7
|
.filter(Boolean)
|
|
26
8
|
.some((id) => {
|
|
27
9
|
const found = root.getElementById?.(id);
|
|
28
|
-
return found ? nameText(found).trim() !== '' : false;
|
|
10
|
+
return found ? nameText(found, shown).trim() !== '' : false;
|
|
29
11
|
});
|
|
30
12
|
};
|
|
31
13
|
const NAMED_BY_CONTENT = new Set([
|
|
@@ -40,26 +22,32 @@ const NAMED_BY_CONTENT = new Set([
|
|
|
40
22
|
'switch',
|
|
41
23
|
'tab',
|
|
42
24
|
]);
|
|
43
|
-
export const hasName = (element) => {
|
|
44
|
-
for (const attribute of ['aria-label', 'title'
|
|
25
|
+
export const hasName = (element, shown) => {
|
|
26
|
+
for (const attribute of ['aria-label', 'title']) {
|
|
45
27
|
if (element.getAttribute(attribute)?.trim()) {
|
|
46
28
|
return true;
|
|
47
29
|
}
|
|
48
30
|
}
|
|
49
|
-
if (namesWithWords(element.getAttribute('aria-labelledby'), element)) {
|
|
31
|
+
if (namesWithWords(element.getAttribute('aria-labelledby'), element, shown)) {
|
|
50
32
|
return true;
|
|
51
33
|
}
|
|
52
34
|
const referenced = element
|
|
53
35
|
.ariaLabelledByElements;
|
|
54
|
-
if ((referenced ?? []).some((one) => nameText(one).trim() !== '')) {
|
|
36
|
+
if ((referenced ?? []).some((one) => nameText(one, shown).trim() !== '')) {
|
|
55
37
|
return true;
|
|
56
38
|
}
|
|
57
39
|
const labels = element.labels;
|
|
58
40
|
if ([...(labels ?? [])].some((label) => nameText(label).trim() !== '')) {
|
|
59
41
|
return true;
|
|
60
42
|
}
|
|
61
|
-
const role = element.getAttribute('role') ?? (element
|
|
62
|
-
return NAMED_BY_CONTENT.has(role) && nameText(element).trim() !== '';
|
|
43
|
+
const role = element.getAttribute('role') ?? implicitRole(element);
|
|
44
|
+
return NAMED_BY_CONTENT.has(role) && nameText(element, shown).trim() !== '';
|
|
45
|
+
};
|
|
46
|
+
const implicitRole = (element) => {
|
|
47
|
+
if (element.localName === 'button' || element.localName === 'summary') {
|
|
48
|
+
return 'button';
|
|
49
|
+
}
|
|
50
|
+
return element.localName === 'a' && element.hasAttribute('href') ? 'link' : '';
|
|
63
51
|
};
|
|
64
52
|
const audited = new WeakSet();
|
|
65
53
|
const waiting = new Map();
|
|
@@ -70,7 +58,7 @@ const readWaiting = () => {
|
|
|
70
58
|
check();
|
|
71
59
|
}
|
|
72
60
|
};
|
|
73
|
-
export const auditName = (host, control, fix, also) => {
|
|
61
|
+
export const auditName = (host, control, fix, also, shown) => {
|
|
74
62
|
if (audited.has(host) || waiting.has(host)) {
|
|
75
63
|
return;
|
|
76
64
|
}
|
|
@@ -83,12 +71,16 @@ export const auditName = (host, control, fix, also) => {
|
|
|
83
71
|
}
|
|
84
72
|
audited.add(host);
|
|
85
73
|
const inner = control();
|
|
86
|
-
|
|
74
|
+
const value = shown?.();
|
|
75
|
+
if (also?.() || hasName(host) || (inner !== null && hasName(inner, value))) {
|
|
87
76
|
return;
|
|
88
77
|
}
|
|
78
|
+
const placeholder = [host, inner].some((one) => one?.getAttribute('placeholder')?.trim());
|
|
89
79
|
reportRuntime({
|
|
90
80
|
kind: 'missing-name',
|
|
91
|
-
message:
|
|
81
|
+
message: placeholder
|
|
82
|
+
? `<${host.localName}> is named only by its placeholder, which is gone once a person types. ${fix}`
|
|
83
|
+
: `<${host.localName}> has no accessible name, so a screen reader announces it by its role alone. ${fix}`,
|
|
92
84
|
tag: host.localName,
|
|
93
85
|
});
|
|
94
86
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { auditSource as audit } from './audit-source.js';
|
|
2
|
+
/**
|
|
3
|
+
* The missing-table warning in a production build: nothing.
|
|
4
|
+
*
|
|
5
|
+
* `#audit-source` resolves here unless the bundler or runtime asks for the
|
|
6
|
+
* `development` condition, so a production build carries none of the audit's
|
|
7
|
+
* code. The signature is the audit's, so a call site reads the same in both
|
|
8
|
+
* builds. See `audit-source.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const auditSource: typeof audit;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const auditSource = () => { };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reports a chart, a legend or a sparkline whose `for` names no table once the
|
|
3
|
+
* page has loaded, the one mistake that leaves it drawing nothing and saying
|
|
4
|
+
* nothing.
|
|
5
|
+
*
|
|
6
|
+
* **Read a task after `load`**, so a table later in the markup, or one a
|
|
7
|
+
* script writes as the page starts, counts. A table a framework renders later
|
|
8
|
+
* than that is reported, and the chart still draws it when it arrives.
|
|
9
|
+
*
|
|
10
|
+
* **In a development build only**, as `missing-name` is: every chart would pay
|
|
11
|
+
* for a sentence meant for the person writing the page. Core imports it as
|
|
12
|
+
* `#audit-source`, which `package.json` resolves here under the `development`
|
|
13
|
+
* condition and to `audit-source-off.ts`, which does nothing, everywhere else.
|
|
14
|
+
*
|
|
15
|
+
* `table` answers what `for` resolves to now: the table, or null.
|
|
16
|
+
*/
|
|
17
|
+
export declare const auditSource: (host: HTMLElement & {
|
|
18
|
+
for: string;
|
|
19
|
+
}, table: () => Element | null) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { reportRuntime } from '../report-runtime/report-runtime.js';
|
|
2
|
+
const audited = new WeakSet();
|
|
3
|
+
export const auditSource = (host, table) => {
|
|
4
|
+
if (host.for === '' || audited.has(host) || typeof document === 'undefined') {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
audited.add(host);
|
|
8
|
+
const read = () => {
|
|
9
|
+
setTimeout(() => {
|
|
10
|
+
if (host.isConnected && host.for !== '' && !table()) {
|
|
11
|
+
reportRuntime({
|
|
12
|
+
kind: 'missing-table',
|
|
13
|
+
message: `<${host.localName} for="${host.for}"> names no table, so it draws nothing. ` +
|
|
14
|
+
'Point `for` at the id of a <table>, an element with role="table" or "grid", or a ' +
|
|
15
|
+
'helix-table, in the same tree.',
|
|
16
|
+
tag: host.localName,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}, 0);
|
|
20
|
+
};
|
|
21
|
+
if (document.readyState === 'complete') {
|
|
22
|
+
read();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
window.addEventListener('load', read, { once: true });
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { valueAsDate as asDate, dateAsValue as fromDate } from '../value-as/value-as.js';
|
|
1
|
+
import { valueAsDate as asDate, dateAsValue as fromDate, utcDate } from '../value-as/value-as.js';
|
|
2
2
|
import { writtenList } from '../written-list/written-list.js';
|
|
3
3
|
export const localToday = (now = new Date()) => {
|
|
4
4
|
const month = `${now.getMonth() + 1}`.padStart(2, '0');
|
|
@@ -15,13 +15,13 @@ export const weekStartOf = (locale) => {
|
|
|
15
15
|
return 0;
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
-
export const monthOf = (date) => `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
19
|
-
export const addDays = (date, days) =>
|
|
18
|
+
export const monthOf = (date) => `${String(date.getUTCFullYear()).padStart(4, '0')}-${String(date.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
19
|
+
export const addDays = (date, days) => utcDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate() + days);
|
|
20
20
|
export const sameMonth = (left, right) => left.getUTCFullYear() === right.getUTCFullYear() && left.getUTCMonth() === right.getUTCMonth();
|
|
21
21
|
export const addMonths = (date, months) => {
|
|
22
|
-
const target =
|
|
23
|
-
const last =
|
|
24
|
-
return
|
|
22
|
+
const target = utcDate(date.getUTCFullYear(), date.getUTCMonth() + months, 1);
|
|
23
|
+
const last = utcDate(target.getUTCFullYear(), target.getUTCMonth() + 1, 0).getUTCDate();
|
|
24
|
+
return utcDate(target.getUTCFullYear(), target.getUTCMonth(), Math.min(date.getUTCDate(), last));
|
|
25
25
|
};
|
|
26
26
|
export const barredBy = (date, rules) => {
|
|
27
27
|
const iso = fromDate(date);
|
|
@@ -72,7 +72,7 @@ export const periodCells = ({ max, min, page, shown, today, view }) => {
|
|
|
72
72
|
const start = years ? yearPageOf(page) : page;
|
|
73
73
|
return Array.from({ length: 12 }, (_, index) => {
|
|
74
74
|
const at = years ? start + index : index;
|
|
75
|
-
const first =
|
|
75
|
+
const first = utcDate(years ? at : start, years ? 0 : index, 1);
|
|
76
76
|
return {
|
|
77
77
|
at,
|
|
78
78
|
chosen: years ? at === shown.getUTCFullYear() : monthOf(first) === monthOf(shown),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the three diagnostics slots share: a listener on `globalThis` under a
|
|
3
|
+
* registered symbol, called where there is one, whose failure is printed once.
|
|
4
|
+
*
|
|
5
|
+
* `reportRuntime`, `reportUsage` and `reportRender` each wrote this out, with
|
|
6
|
+
* a flag and a sentence of its own, and every element carries all three. Kept
|
|
7
|
+
* as one function, the sentence is one template and the flags one set.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Calls the function in `slot` with `args`, and answers what it returned.
|
|
11
|
+
*
|
|
12
|
+
* **A listener that throws never breaks the caller.** The error is printed
|
|
13
|
+
* once per `kind`, saying what happened instead (`after`), and the answer is
|
|
14
|
+
* `undefined`, as it is where nothing listens.
|
|
15
|
+
*/
|
|
16
|
+
export declare const callListener: (slot: symbol, kind: string, after: string, ...args: unknown[]) => unknown;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const told = new Set();
|
|
2
|
+
export const callListener = (slot, kind, after, ...args) => {
|
|
3
|
+
const listener = globalThis[slot];
|
|
4
|
+
if (typeof listener !== 'function') {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
return listener(...args);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (!told.has(kind)) {
|
|
12
|
+
told.add(kind);
|
|
13
|
+
console.error(`helix: a ${kind} listener threw, and ${after}. Later failures are not printed.`, error);
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -41,3 +41,17 @@ export interface ChordPress {
|
|
|
41
41
|
* should go to whatever did want it.
|
|
42
42
|
*/
|
|
43
43
|
export declare const matchesChord: (press: ChordPress, keys: string, apple: boolean) => boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether a key press is Control+A, or Command+A on an Apple keyboard: the
|
|
46
|
+
* press that selects every item of a multiple selection.
|
|
47
|
+
*
|
|
48
|
+
* List, Tree, Listbox, Data Table and Virtual List each wrote the test. Either
|
|
49
|
+
* modifier counts on any platform, as they did, and Alt held is another
|
|
50
|
+
* shortcut.
|
|
51
|
+
*/
|
|
52
|
+
export declare const selectsAll: (press: {
|
|
53
|
+
readonly altKey: boolean;
|
|
54
|
+
readonly ctrlKey: boolean;
|
|
55
|
+
readonly key: string;
|
|
56
|
+
readonly metaKey: boolean;
|
|
57
|
+
}) => boolean;
|
|
@@ -51,3 +51,4 @@ export const matchesChord = (press, keys, apple) => {
|
|
|
51
51
|
(ctrl ? press.ctrlKey : !press.ctrlKey) &&
|
|
52
52
|
(meta ? press.metaKey : !press.metaKey));
|
|
53
53
|
};
|
|
54
|
+
export const selectsAll = (press) => (press.ctrlKey || press.metaKey) && !press.altKey && press.key.toLowerCase() === 'a';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
@@ -46,7 +46,7 @@ export interface TooltipAt {
|
|
|
46
46
|
*
|
|
47
47
|
* The chart's `pointer` is one, and is what every chart passes.
|
|
48
48
|
*/
|
|
49
|
-
export type ReadingCursor = Pick<ChartPointerController, 'column' | 'keyed'
|
|
49
|
+
export type ReadingCursor = Pick<ChartPointerController, 'column' | 'keyed'> & Partial<Pick<ChartPointerController, 'position' | 'shows'>>;
|
|
50
50
|
/**
|
|
51
51
|
* What a pointer resting on a reading shows, as `Tooltip` pointed at a mark.
|
|
52
52
|
*
|
|
@@ -10,7 +10,9 @@ export const drawTooltip = (rows, at, tag, where, slotOf, reading) => {
|
|
|
10
10
|
const row = rows[at];
|
|
11
11
|
const live = reading === undefined || rows.length === 0
|
|
12
12
|
? nothing
|
|
13
|
-
: html `<span aria-live="polite" part="live" role="status">${reading.keyed && row
|
|
13
|
+
: html `<span aria-live="polite" part="live" role="status">${reading.keyed && row
|
|
14
|
+
? spokenReading(row, reading.column, (cell) => reading.shows?.(cell) !== false, reading.position)
|
|
15
|
+
: ''}</span>`;
|
|
14
16
|
if (!row || rows.length === 0) {
|
|
15
17
|
return live;
|
|
16
18
|
}
|
|
@@ -5,6 +5,10 @@ import type { Row } from '../read-series/read-series.js';
|
|
|
5
5
|
*
|
|
6
6
|
* The table's own text, as the bubble shows it, so the words and the picture
|
|
7
7
|
* agree, and nothing in it is English: the names are the table's headings. A
|
|
8
|
-
* blank cell says nothing and is left out
|
|
8
|
+
* blank cell says nothing and is left out, and so is a cell `shows` answers
|
|
9
|
+
* false for, which is a series a legend hid.
|
|
10
|
+
*
|
|
11
|
+
* `position` follows in brackets, such as "2 of 12", where the chart gives
|
|
12
|
+
* one: last, so the reading is heard before where it stands.
|
|
9
13
|
*/
|
|
10
|
-
export declare const spokenReading: (row: Row, column?: number) => string;
|
|
14
|
+
export declare const spokenReading: (row: Row, column?: number, shows?: (cell: number) => boolean, position?: string) => string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export const spokenReading = (row, column) => {
|
|
1
|
+
export const spokenReading = (row, column, shows = () => true, position = '') => {
|
|
2
|
+
const after = position === '' ? '' : ` (${position})`;
|
|
2
3
|
const cell = column === undefined ? undefined : row.cells[column];
|
|
3
4
|
if (cell) {
|
|
4
|
-
return `${row.label}, ${cell.name} ${cell.text}`.trim()
|
|
5
|
+
return `${`${row.label}, ${cell.name} ${cell.text}`.trim()}${after}`;
|
|
5
6
|
}
|
|
6
7
|
const said = row.cells
|
|
7
|
-
.filter((one) => one.text.trim() !== '')
|
|
8
|
+
.filter((one, index) => one.text.trim() !== '' && shows(index))
|
|
8
9
|
.map((one) => `${one.name} ${one.text}`.trim());
|
|
9
|
-
return said.length === 0 ? row.label : `${row.label}: ${said.join(', ')}`;
|
|
10
|
+
return `${said.length === 0 ? row.label : `${row.label}: ${said.join(', ')}`}${after}`;
|
|
10
11
|
};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import { UNITS as TIME_UNITS } from '../relative-time/relative-time.js';
|
|
1
2
|
const held = new Map();
|
|
2
3
|
const dated = new Map();
|
|
3
|
-
const UNITS =
|
|
4
|
-
{ ms: 60_000, unit: 'minute' },
|
|
5
|
-
{ ms: 3_600_000, unit: 'hour' },
|
|
6
|
-
{ ms: 86_400_000, unit: 'day' },
|
|
7
|
-
];
|
|
4
|
+
const UNITS = TIME_UNITS.slice(1, 4);
|
|
8
5
|
const HORIZON = 604_800_000;
|
|
9
6
|
const compact = (locale) => {
|
|
10
7
|
let found = held.get(locale);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A heading level a reader can be told: a whole number from 1 to 6, from
|
|
3
|
+
* the level asked for, or `fallback` where none was.
|
|
4
|
+
*
|
|
5
|
+
* `aria-level` takes a whole number, so a fraction is cut to the level below
|
|
6
|
+
* it, and a level past either end is held at it: `0` is 1 and `9` is 6.
|
|
7
|
+
*/
|
|
8
|
+
export declare const headingLevel: (level: number | undefined, fallback?: number) => number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words that name a host, for a part inside its shadow root that must
|
|
3
|
+
* carry the same name: the list a Select or a Time Picker opens.
|
|
4
|
+
*
|
|
5
|
+
* **Words rather than a reference**, because the part is inside the host's
|
|
6
|
+
* shadow root, and neither an IDREF on the page nor the host's labels reach
|
|
7
|
+
* it. Read in the order a browser names the host: its `aria-labelledby`, its
|
|
8
|
+
* `aria-label`, then `own`, the name the component draws itself (its `label`),
|
|
9
|
+
* then a `<label>` for it. Each element's words are read as a name reads
|
|
10
|
+
* them, so hidden text is left out and runs of white space are one space.
|
|
11
|
+
*
|
|
12
|
+
* Empty where nothing names the host, which leaves the fallback to the caller.
|
|
13
|
+
*/
|
|
14
|
+
export declare const hostWords: (host: HTMLElement & {
|
|
15
|
+
labels?: NodeList | null;
|
|
16
|
+
}, own?: string) => string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { elementById } from '../element-by-id/element-by-id.js';
|
|
2
|
+
import { nameText } from '../name-text/name-text.js';
|
|
3
|
+
const wordsOf = (named) => named
|
|
4
|
+
.map((one) => (one ? nameText(one).replace(/\s+/g, ' ').trim() : ''))
|
|
5
|
+
.filter(Boolean)
|
|
6
|
+
.join(' ');
|
|
7
|
+
export const hostWords = (host, own = '') => {
|
|
8
|
+
const ids = (host.getAttribute('aria-labelledby') ?? '').split(/\s+/).filter(Boolean);
|
|
9
|
+
return (wordsOf(ids.map((id) => elementById(host, id))) ||
|
|
10
|
+
host.getAttribute('aria-label')?.trim() ||
|
|
11
|
+
own ||
|
|
12
|
+
wordsOf([...(host.labels ?? [])]));
|
|
13
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Whether `element` is a submit button, native or Helix. */
|
|
2
|
+
export declare const submits: (element: Element) => boolean;
|
|
3
|
+
/** The form's default button, the first submit button in it, native or Helix. */
|
|
4
|
+
export declare const defaultButton: (form: HTMLFormElement) => Element | undefined;
|
|
5
|
+
/** Whether `target` is a native field of `form` that Enter submits it from. */
|
|
6
|
+
export declare const submitsOnEnter: (target: EventTarget | undefined, form: HTMLFormElement) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Submits a control's form the way pressing Enter in a native text field does.
|
|
9
|
+
*
|
|
10
|
+
* A Helix control's own input is in a shadow root and has no form owner, so
|
|
11
|
+
* the platform never runs implicit submission for it. This runs it: the form's
|
|
12
|
+
* default button is activated, the first submit button in it, a Helix one
|
|
13
|
+
* included. With none, the form is submitted when it holds no more than one
|
|
14
|
+
* field that counts against implicit submission, and nothing happens
|
|
15
|
+
* otherwise.
|
|
16
|
+
*
|
|
17
|
+
* **A module of its own rather than a method on the base**, so its code ships
|
|
18
|
+
* only with the controls that submit on Enter (Input, Number Input, Pin Input,
|
|
19
|
+
* Combobox, the picker fields and Button) and not with Switch, Checkbox or
|
|
20
|
+
* Slider. `form` is the control's `form`, and a control with none submits
|
|
21
|
+
* nothing.
|
|
22
|
+
*
|
|
23
|
+
* @reference https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission implicit submission
|
|
24
|
+
*/
|
|
25
|
+
export declare const submitImplicitly: (form: HTMLFormElement | null) => void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HelixFormElement } from '../../helix-form-element/helix-form-element.js';
|
|
2
|
+
const BLOCKING_TYPES = new Set([
|
|
3
|
+
'date',
|
|
4
|
+
'datetime-local',
|
|
5
|
+
'email',
|
|
6
|
+
'month',
|
|
7
|
+
'number',
|
|
8
|
+
'password',
|
|
9
|
+
'search',
|
|
10
|
+
'tel',
|
|
11
|
+
'text',
|
|
12
|
+
'time',
|
|
13
|
+
'url',
|
|
14
|
+
'week',
|
|
15
|
+
]);
|
|
16
|
+
const hooks = (element) => element;
|
|
17
|
+
export const submits = (element) => {
|
|
18
|
+
if (element instanceof HelixFormElement) {
|
|
19
|
+
return hooks(element).submitsForm;
|
|
20
|
+
}
|
|
21
|
+
if (element instanceof HTMLButtonElement) {
|
|
22
|
+
return element.type === 'submit';
|
|
23
|
+
}
|
|
24
|
+
return (element instanceof HTMLInputElement && (element.type === 'submit' || element.type === 'image'));
|
|
25
|
+
};
|
|
26
|
+
export const defaultButton = (form) => [...form.elements].find((element) => submits(element));
|
|
27
|
+
export const submitsOnEnter = (target, form) => target instanceof HTMLInputElement && target.form === form && BLOCKING_TYPES.has(target.type);
|
|
28
|
+
export const submitImplicitly = (form) => {
|
|
29
|
+
if (!form) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const button = defaultButton(form);
|
|
33
|
+
if (button) {
|
|
34
|
+
if (button.matches(':disabled')) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (button instanceof HelixFormElement) {
|
|
38
|
+
hooks(button).activate();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
;
|
|
42
|
+
button.click();
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const blocking = [...form.elements].filter((element) => element instanceof HelixFormElement
|
|
47
|
+
? hooks(element).blocksImplicitSubmission
|
|
48
|
+
: element instanceof HTMLInputElement && BLOCKING_TYPES.has(element.type));
|
|
49
|
+
if (blocking.length <= 1) {
|
|
50
|
+
form.requestSubmit();
|
|
51
|
+
}
|
|
52
|
+
};
|