@fusion.dev/helix-core 0.146.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.
Files changed (84) hide show
  1. package/custom-elements.json +1299 -60
  2. package/dist/controllers/aria-forward-controller/aria-forward-controller.js +5 -3
  3. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +6 -0
  4. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +13 -0
  5. package/dist/controllers/copy-controller/copy-controller.d.ts +4 -1
  6. package/dist/controllers/copy-controller/copy-controller.js +2 -1
  7. package/dist/controllers/countdown-controller/countdown-controller.d.ts +4 -1
  8. package/dist/controllers/countdown-controller/countdown-controller.js +3 -1
  9. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +5 -0
  10. package/dist/controllers/source-controller/source-controller.js +2 -0
  11. package/dist/controllers/swipe-controller/swipe-controller.d.ts +10 -1
  12. package/dist/controllers/swipe-controller/swipe-controller.js +1 -1
  13. package/dist/controllers/virtualize-controller/virtualize-controller.js +8 -6
  14. package/dist/events/band.d.ts +13 -0
  15. package/dist/events/band.js +11 -0
  16. package/dist/events/details.d.ts +5 -0
  17. package/dist/events/events.d.ts +4 -2
  18. package/dist/events/events.js +3 -1
  19. package/dist/events/names.d.ts +3 -0
  20. package/dist/events/names.js +13 -88
  21. package/dist/helix-element/helix-element.js +9 -43
  22. package/dist/helix-element/react-bridge.d.ts +13 -0
  23. package/dist/helix-element/react-bridge.js +1 -1
  24. package/dist/index.d.ts +18 -9
  25. package/dist/index.js +18 -9
  26. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +7 -0
  27. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +1 -0
  28. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +25 -4
  29. package/dist/mixins/mixin-picker-feedback/mixin-picker-feedback.d.ts +42 -0
  30. package/dist/mixins/mixin-picker-feedback/mixin-picker-feedback.js +46 -0
  31. package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +2 -11
  32. package/dist/mixins/mixin-picker-field/mixin-picker-field.js +1 -6
  33. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +1 -1
  34. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +10 -6
  35. package/dist/utils/audit-name/audit-name.d.ts +7 -2
  36. package/dist/utils/audit-name/audit-name.js +12 -3
  37. package/dist/utils/audit-source/audit-source-off.d.ts +10 -0
  38. package/dist/utils/audit-source/audit-source-off.js +1 -0
  39. package/dist/utils/audit-source/audit-source.d.ts +19 -0
  40. package/dist/utils/audit-source/audit-source.js +27 -0
  41. package/dist/utils/call-listener/call-listener.d.ts +16 -0
  42. package/dist/utils/call-listener/call-listener.js +17 -0
  43. package/dist/utils/chord/chord.d.ts +14 -0
  44. package/dist/utils/chord/chord.js +1 -0
  45. package/dist/utils/clamp/clamp.d.ts +7 -0
  46. package/dist/utils/clamp/clamp.js +1 -0
  47. package/dist/utils/css-color/css-color.js +1 -1
  48. package/dist/utils/elapsed-time/elapsed-time.d.ts +1 -1
  49. package/dist/utils/elapsed-time/elapsed-time.js +2 -5
  50. package/dist/utils/host-words/host-words.d.ts +16 -0
  51. package/dist/utils/host-words/host-words.js +13 -0
  52. package/dist/utils/load-more/load-more.d.ts +25 -0
  53. package/dist/utils/load-more/load-more.js +1 -0
  54. package/dist/utils/locale-of/locale-of.d.ts +18 -0
  55. package/dist/utils/locale-of/locale-of.js +26 -0
  56. package/dist/utils/nearest/nearest.d.ts +9 -0
  57. package/dist/utils/nearest/nearest.js +9 -0
  58. package/dist/utils/number-format/number-format.d.ts +9 -0
  59. package/dist/utils/number-format/number-format.js +4 -0
  60. package/dist/utils/own-rule/own-rule.d.ts +16 -0
  61. package/dist/utils/own-rule/own-rule.js +7 -0
  62. package/dist/utils/platform/platform.d.ts +8 -0
  63. package/dist/utils/platform/platform.js +1 -0
  64. package/dist/utils/read-series/read-series.d.ts +14 -0
  65. package/dist/utils/read-series/read-series.js +15 -0
  66. package/dist/utils/relative-time/relative-time.d.ts +8 -0
  67. package/dist/utils/relative-time/relative-time.js +5 -2
  68. package/dist/utils/report-render/report-render.js +2 -14
  69. package/dist/utils/report-runtime/report-runtime.d.ts +5 -1
  70. package/dist/utils/report-runtime/report-runtime.js +2 -14
  71. package/dist/utils/report-usage/report-usage.js +2 -14
  72. package/dist/utils/scroll-item-into-view/scroll-item-into-view.d.ts +33 -0
  73. package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +13 -3
  74. package/dist/utils/slotted-glyph/slotted-glyph.d.ts +33 -0
  75. package/dist/utils/slotted-glyph/slotted-glyph.js +27 -0
  76. package/dist/utils/typeahead/typeahead.d.ts +8 -0
  77. package/dist/utils/typeahead/typeahead.js +1 -0
  78. package/dist/utils/verdict-flags/verdict-flags.d.ts +13 -0
  79. package/dist/utils/verdict-flags/verdict-flags.js +6 -0
  80. package/dist/utils/written-list/written-list.d.ts +31 -0
  81. package/dist/utils/written-list/written-list.js +16 -0
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/package.json +15 -4
package/dist/index.js CHANGED
@@ -30,10 +30,10 @@ export { SwipeController, } from './controllers/swipe-controller/swipe-controlle
30
30
  export { TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
31
31
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
32
32
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
33
- export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, } from './events/events.js';
33
+ export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, } from './events/events.js';
34
34
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
35
35
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
36
- export { MixinChartSource, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
36
+ export { MixinChartSource, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, POSITION_LABEL, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
37
37
  export { MixinCollection, } from './mixins/mixin-collection/mixin-collection.js';
38
38
  export { MixinControllerActive, } from './mixins/mixin-controller-active/mixin-controller-active.js';
39
39
  export { MixinControllerChecked, } from './mixins/mixin-controller-checked/mixin-controller-checked.js';
@@ -42,6 +42,7 @@ export { MixinControllerValue, } from './mixins/mixin-controller-value/mixin-con
42
42
  export { MixinDismissible, } from './mixins/mixin-dismissible/mixin-dismissible.js';
43
43
  export { MixinField } from './mixins/mixin-field/mixin-field.js';
44
44
  export { MixinPickerEditing, } from './mixins/mixin-picker-editing/mixin-picker-editing.js';
45
+ export { MixinPickerFeedback, } from './mixins/mixin-picker-feedback/mixin-picker-feedback.js';
45
46
  export { MixinPickerField, } from './mixins/mixin-picker-field/mixin-picker-field.js';
46
47
  export { MixinSegmentedField, } from './mixins/mixin-segmented-field/mixin-segmented-field.js';
47
48
  export { MixinSelectableItem, } from './mixins/mixin-selectable-item/mixin-selectable-item.js';
@@ -51,7 +52,8 @@ export { anchorElement } from './utils/anchor-element/anchor-element.js';
51
52
  export { arrowReach, sideOf } from './utils/arrow/arrow.js';
52
53
  export { addDays, addMonths, barredBy, localToday, monthOf, monthOut, periodCells, periodKey, periodMove, sameMonth, weekStartOf, YEARS_SHOWN, yearOut, yearPageOf, } from './utils/calendar/calendar.js';
53
54
  export { capturePointer } from './utils/capture-pointer/capture-pointer.js';
54
- export { ariaChord, chordParts, matchesChord, printableChord, } from './utils/chord/chord.js';
55
+ export { ariaChord, chordParts, matchesChord, printableChord, selectsAll, } from './utils/chord/chord.js';
56
+ export { clamp } from './utils/clamp/clamp.js';
55
57
  export { clockLocale, dayPeriod, isTwelveHour, nearestTime, offeredTimes, timeBarred, timeFault, wheelParts, wholeHour, } from './utils/clock/clock.js';
56
58
  export { closestInstance } from './utils/closest-instance/closest-instance.js';
57
59
  export { colorName, hueName } from './utils/color-name/color-name.js';
@@ -72,22 +74,28 @@ export { formatNumbers } from './utils/format-numbers/format-numbers.js';
72
74
  export { headingIn, headingsIn } from './utils/heading-in/heading-in.js';
73
75
  export { headingLevel } from './utils/heading-level/heading-level.js';
74
76
  export { hostAria } from './utils/host-aria/host-aria.js';
77
+ export { hostWords } from './utils/host-words/host-words.js';
75
78
  export { defaultButton, submitImplicitly, submits, submitsOnEnter, } from './utils/implicit-submit/implicit-submit.js';
76
79
  export { forgetDisabled, inheritDisabled, } from './utils/inherit-disabled/inherit-disabled.js';
77
80
  export { inlineStep } from './utils/inline-step/inline-step.js';
78
81
  export { isRtl } from './utils/is-rtl/is-rtl.js';
79
82
  export { keyClaimed } from './utils/key-claimed/key-claimed.js';
80
83
  export { linePath } from './utils/line-path/line-path.js';
84
+ export { endInSight } from './utils/load-more/load-more.js';
85
+ export { localeOf } from './utils/locale-of/locale-of.js';
81
86
  export { markRegion } from './utils/mark-region/mark-region.js';
82
87
  export { copiesOf, sameCopies, } from './utils/media-copy/media-copy.js';
83
- export { formatNumber, parseNumber } from './utils/number-format/number-format.js';
88
+ export { nameText } from './utils/name-text/name-text.js';
89
+ export { nearest } from './utils/nearest/nearest.js';
90
+ export { formatNumber, parseNumber, readingText } from './utils/number-format/number-format.js';
84
91
  export { onUnlessFalse } from './utils/on-unless-false/on-unless-false.js';
92
+ export { applyOwnRule } from './utils/own-rule/own-rule.js';
85
93
  export { plainClick } from './utils/plain-click/plain-click.js';
86
- export { appleTouch, onApple } from './utils/platform/platform.js';
94
+ export { appleTouch, onAndroid, onApple } from './utils/platform/platform.js';
87
95
  export { pressType } from './utils/press-type/press-type.js';
88
96
  export { rangeAdd } from './utils/range-add/range-add.js';
89
97
  export { readColor, rgbOf } from './utils/read-color/read-color.js';
90
- export { byRow, readRows, readSeries, seriesKey, unbroken, } from './utils/read-series/read-series.js';
98
+ export { byRow, extremesOf, readRows, readSeries, seriesKey, unbroken, } from './utils/read-series/read-series.js';
91
99
  export { linkDescription, reflectElements, unlinkDescription, } from './utils/reflect-elements/reflect-elements.js';
92
100
  export { relativeTime, } from './utils/relative-time/relative-time.js';
93
101
  export { measuring, RENDER_SLOT, reportRender } from './utils/report-render/report-render.js';
@@ -102,9 +110,10 @@ export { valueAt } from './utils/row-data/row-data.js';
102
110
  export { arranged, cycle, filter, format, ordered, parse, sort, window_, } from './utils/row-order/row-order.js';
103
111
  export { extent, linear } from './utils/scale/scale.js';
104
112
  export { ticks } from './utils/scale/ticks.js';
105
- export { scrollItemIntoView, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
113
+ export { scrollItemIntoView, scrollingAncestor, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
106
114
  export { lockScroll, scrollHolders, unlockScroll } from './utils/scroll-lock/scroll-lock.js';
107
115
  export { slotFilled } from './utils/slot-filled/slot-filled.js';
116
+ export { SlottedGlyph } from './utils/slotted-glyph/slotted-glyph.js';
108
117
  export { slottedWords } from './utils/slotted-words/slotted-words.js';
109
118
  export { splitValues } from './utils/split-values/split-values.js';
110
119
  export { edgeOffset, FLING, parseSnapPoints, settleSwipe, snapTravel, swipeAxis, swipeEdge, swipeStep, towardEdge, } from './utils/swipe/swipe.js';
@@ -112,10 +121,10 @@ export { tellDisclosure } from './utils/tell-disclosure/tell-disclosure.js';
112
121
  export { subscribe } from './utils/ticker/ticker.js';
113
122
  export { TONE_MARKS, toneMark, } from './utils/tone-marks/tone-marks.js';
114
123
  export { treeArrow } from './utils/tree-arrow/tree-arrow.js';
115
- export { Typeahead, typeaheadBy, typesCharacter, } from './utils/typeahead/typeahead.js';
124
+ export { Typeahead, typeaheadBy, typeaheadByWords, typesCharacter, } from './utils/typeahead/typeahead.js';
116
125
  export { dateAsValue, isClockValue, isDateValue, isTimeValue, minutesAsValue, numberAsValue, secondsAsValue, timeAsValue, valueAsDate, valueAsMinutes, valueAsNumber, valueAsSeconds, valueAsTime, } from './utils/value-as/value-as.js';
117
126
  export { watchTheme } from './utils/watch-theme/watch-theme.js';
118
127
  export { wordsConverter } from './utils/words-converter/words-converter.js';
119
128
  export { writeClock, writeTime } from './utils/write-time/write-time.js';
120
- export { commaList, pressedValue, prunedValue, toggleEvery, toggleValue, writtenList, writtenNumbers, } from './utils/written-list/written-list.js';
129
+ export { anchorAt, commaList, everyState, inRowOrder, pressedValue, prunedValue, toggleEvery, toggleValue, writtenList, writtenNumbers, } from './utils/written-list/written-list.js';
121
130
  export { VERSION } from './version.js';
@@ -187,6 +187,13 @@ export declare class MixinChartSourceInterface {
187
187
  */
188
188
  pointer: ChartPointerController;
189
189
  }
190
+ /**
191
+ * The words a chart says a reading's place in by default, such as "2 of 12":
192
+ * every chart's `position-label`, and Sparkline's, which is no chart source.
193
+ * The property keeps the words written out, which is what its reference page
194
+ * shows as the default; a spec holds the two together.
195
+ */
196
+ export declare const POSITION_LABEL = "{position} of {count}";
190
197
  /**
191
198
  * The half of a chart that is about finding its numbers rather than drawing.
192
199
  *
@@ -14,6 +14,7 @@ export const seriesIndex = (depth) => (depth % PALETTE) + 1;
14
14
  export const PLOT_WIDTH = 100;
15
15
  export const PLOT_HEIGHT = 100;
16
16
  export const seriesNames = (list) => writtenList(list).map(seriesKey);
17
+ export const POSITION_LABEL = '{position} of {count}';
17
18
  export const MixinChartSource = (Base) => {
18
19
  class ChartSourceElement extends Base {
19
20
  constructor() {
@@ -14,6 +14,8 @@ export const MixinDismissible = (Base) => {
14
14
  super(...arguments);
15
15
  this.dismissible = false;
16
16
  this.dismissLabel = 'Dismiss';
17
+ this.handOff = undefined;
18
+ this.forgetFocus = undefined;
17
19
  }
18
20
  dismissEvent() {
19
21
  const own = [...this.childNodes]
@@ -63,7 +65,8 @@ export const MixinDismissible = (Base) => {
63
65
  return [...after, ...before];
64
66
  }
65
67
  keepFocus(neighbors, beside) {
66
- const settle = () => {
68
+ this.forgetFocus?.();
69
+ const settle = (late) => {
67
70
  const active = document.activeElement;
68
71
  if (this.isConnected || (active !== null && active !== document.body)) {
69
72
  return;
@@ -77,12 +80,30 @@ export const MixinDismissible = (Base) => {
77
80
  }
78
81
  }
79
82
  }
80
- if (beside?.isConnected) {
83
+ if (late && beside?.isConnected) {
81
84
  beside.focus();
82
85
  }
83
86
  };
84
- settle();
85
- requestAnimationFrame(settle);
87
+ settle(false);
88
+ requestAnimationFrame(() => settle(true));
89
+ if (this.isConnected) {
90
+ const forget = () => {
91
+ this.handOff = undefined;
92
+ this.forgetFocus = undefined;
93
+ document.removeEventListener('focusin', forget, true);
94
+ };
95
+ this.handOff = settle;
96
+ this.forgetFocus = forget;
97
+ document.addEventListener('focusin', forget, true);
98
+ }
99
+ }
100
+ disconnectedCallback() {
101
+ super.disconnectedCallback();
102
+ const settle = this.handOff;
103
+ if (settle) {
104
+ this.forgetFocus?.();
105
+ requestAnimationFrame(() => settle(true));
106
+ }
86
107
  }
87
108
  }
88
109
  __decorate([
@@ -0,0 +1,42 @@
1
+ import { nothing, type TemplateResult } from 'lit';
2
+ import type { HelixFormElement } from '../../helix-form-element/helix-form-element.js';
3
+ type Constructor<T> = new (...args: any[]) => T;
4
+ export declare class MixinPickerFeedbackInterface {
5
+ /**
6
+ * A message under the picker, such as what is wrong with the answer.
7
+ *
8
+ * While set, the picker is invalid and does not submit, as a field's
9
+ * `feedback` is, it is marked invalid to a reader and to CSS, and the
10
+ * controls a reader answers with are described by it.
11
+ */
12
+ feedback: string;
13
+ /**
14
+ * Whether the answer fails validation, marked on the controls a reader
15
+ * answers with and as `:state(invalid)`.
16
+ *
17
+ * A flag a field that wraps the picker sets, since that field owns the
18
+ * label, the hint and the message; on its own, `feedback` both marks the
19
+ * picker and says why.
20
+ */
21
+ invalid: boolean;
22
+ /** Whether a reader is told the answer is wrong: `invalid`, or `feedback`. */
23
+ protected get marked(): boolean;
24
+ /** The `feedback` line, or nothing while there is none. */
25
+ protected renderPickerFeedback(): TemplateResult | typeof nothing;
26
+ }
27
+ /**
28
+ * `feedback` and `invalid` for a picker that stands on its own as a form
29
+ * control: Color Picker, Date Picker and Time Picker.
30
+ *
31
+ * **One mark for both.** `invalid` is the flag a wrapping field sets, and
32
+ * `feedback` is the message a picker on its own shows; either one marks the
33
+ * picker invalid on its internals (`aria-invalid` on the host) and as
34
+ * `:state(invalid)`, and each picker marks the controls it draws by `marked`.
35
+ * Measured on Color Picker before this: `feedback` set `customError` in its
36
+ * validity while every control reported `invalid: false`.
37
+ *
38
+ * The message is the validity's too, as a field's `feedback` is, through
39
+ * `HelixFormElement`'s `validityFeedback`, so a form with one does not submit.
40
+ */
41
+ export declare const MixinPickerFeedback: <T extends Constructor<HelixFormElement>>(Base: T) => Constructor<MixinPickerFeedbackInterface> & T;
42
+ export {};
@@ -0,0 +1,46 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { html, nothing } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { hostAria } from '../../utils/host-aria/host-aria.js';
10
+ export const MixinPickerFeedback = (Base) => {
11
+ class PickerFeedbackElement extends Base {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.feedback = '';
15
+ this.invalid = false;
16
+ }
17
+ get marked() {
18
+ return this.invalid || this.feedback !== '';
19
+ }
20
+ get validityFeedback() {
21
+ return this.feedback;
22
+ }
23
+ willUpdate(changed) {
24
+ super.willUpdate(changed);
25
+ this.internals.ariaInvalid = this.marked ? 'true' : null;
26
+ this.toggleState('invalid', this.marked);
27
+ }
28
+ updated(changed) {
29
+ super.updated(changed);
30
+ const line = this.partNamed('feedback');
31
+ hostAria(this.internals, { describedBy: line ? [line] : [] });
32
+ }
33
+ renderPickerFeedback() {
34
+ return this.feedback === ''
35
+ ? nothing
36
+ : html `<p part="feedback" id="feedback">${this.feedback}</p>`;
37
+ }
38
+ }
39
+ __decorate([
40
+ property()
41
+ ], PickerFeedbackElement.prototype, "feedback", void 0);
42
+ __decorate([
43
+ property({ reflect: true, type: Boolean })
44
+ ], PickerFeedbackElement.prototype, "invalid", void 0);
45
+ return PickerFeedbackElement;
46
+ };
@@ -1,8 +1,10 @@
1
1
  import { PopoverController } from '../../controllers/popover-controller/popover-controller.js';
2
2
  import type { HelixFormElement } from '../../helix-form-element/helix-form-element.js';
3
+ import { type PickerVerdict } from '../../utils/verdict-flags/verdict-flags.js';
3
4
  import type { MixinControllerOpenInterface } from '../mixin-controller-open/mixin-controller-open.js';
4
5
  import type { MixinControllerValueInterface } from '../mixin-controller-value/mixin-controller-value.js';
5
6
  import type { MixinFieldInterface } from '../mixin-field/mixin-field.js';
7
+ export type { PickerVerdict } from '../../utils/verdict-flags/verdict-flags.js';
6
8
  type Constructor<T> = new (...args: any[]) => T;
7
9
  /**
8
10
  * What this is applied over: a form field that opens, holds a value and has a
@@ -99,16 +101,6 @@ export declare class MixinPickerFieldInterface {
99
101
  */
100
102
  protected validate(): PickerVerdict;
101
103
  }
102
- /**
103
- * What a picker field finds wrong: a message, reported as `badInput`, or the
104
- * validity flag to set and its message. Empty means nothing is wrong.
105
- */
106
- export type PickerVerdict = string | readonly [flag: keyof ValidityStateFlags, message: string];
107
- /**
108
- * A verdict as the flags and the message `setValidity` takes, or `null` where
109
- * there is nothing wrong.
110
- */
111
- export declare const verdictFlags: (verdict: PickerVerdict) => [flags: ValidityStateFlags, message: string] | null;
112
104
  /**
113
105
  * Grafts a floating picker onto a text field, and the protocol between them.
114
106
  *
@@ -166,4 +158,3 @@ export declare const verdictFlags: (verdict: PickerVerdict) => [flags: ValidityS
166
158
  * `sync`, which is not a protocol.
167
159
  */
168
160
  export declare const MixinPickerField: <T extends Constructor<PickerBase>>(Base: T) => Constructor<MixinPickerFieldInterface> & T;
169
- export {};
@@ -9,12 +9,7 @@ import { PopoverController } from '../../controllers/popover-controller/popover-
9
9
  import { submitImplicitly } from '../../utils/implicit-submit/implicit-submit.js';
10
10
  import { reportValues } from '../../utils/report-values/report-values.js';
11
11
  import { tabStops } from '../../utils/tab-stops/tab-stops.js';
12
- export const verdictFlags = (verdict) => {
13
- if (typeof verdict === 'string') {
14
- return verdict === '' ? null : [{ badInput: true }, verdict];
15
- }
16
- return [{ [verdict[0]]: true }, verdict[1]];
17
- };
12
+ import { verdictFlags } from '../../utils/verdict-flags/verdict-flags.js';
18
13
  export const MixinPickerField = (Base) => {
19
14
  class PickerFieldElement extends Base {
20
15
  constructor() {
@@ -4,7 +4,7 @@ import { type SegmentedKind, type SegmentOptions } from '../../utils/date-segmen
4
4
  import type { MixinControllerOpenInterface } from '../mixin-controller-open/mixin-controller-open.js';
5
5
  import type { MixinControllerValueInterface } from '../mixin-controller-value/mixin-controller-value.js';
6
6
  import type { MixinFieldInterface } from '../mixin-field/mixin-field.js';
7
- import { type MixinPickerFieldInterface } from '../mixin-picker-field/mixin-picker-field.js';
7
+ import type { MixinPickerFieldInterface } from '../mixin-picker-field/mixin-picker-field.js';
8
8
  type Constructor<T> = new (...args: any[]) => T;
9
9
  /** What this is applied over: a picker field, which `MixinPickerField` makes. */
10
10
  type SegmentedBase = HelixFormElement & MixinControllerOpenInterface & MixinControllerValueInterface & MixinFieldInterface & MixinPickerFieldInterface;
@@ -8,7 +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 { verdictFlags, } from '../mixin-picker-field/mixin-picker-field.js';
11
+ import { appleTouch } from '../../utils/platform/platform.js';
12
+ import { verdictFlags } from '../../utils/verdict-flags/verdict-flags.js';
12
13
  export const MixinSegmentedField = (Base) => {
13
14
  class SegmentedFieldElement extends Base {
14
15
  constructor() {
@@ -18,6 +19,7 @@ export const MixinSegmentedField = (Base) => {
18
19
  this.parts = {};
19
20
  this.typed = '';
20
21
  this.own = false;
22
+ this.touchApple = appleTouch(typeof navigator === 'undefined' ? undefined : navigator);
21
23
  this.entered = undefined;
22
24
  this.handleSegmentKeydown = (event) => {
23
25
  const element = event.currentTarget;
@@ -213,20 +215,22 @@ export const MixinSegmentedField = (Base) => {
213
215
  const empty = shown === undefined;
214
216
  const id = `${this.controlId}-${kind}`;
215
217
  const editable = !this.isDisabled && !this.readOnly;
218
+ const spin = !this.touchApple;
216
219
  return html `<span
217
220
  part="segment${empty ? ' segment-placeholder' : ''}"
218
221
  id=${id}
219
222
  data-segment=${kind}
220
- role="spinbutton"
223
+ role=${spin ? 'spinbutton' : 'textbox'}
221
224
  tabindex=${this.isDisabled ? '-1' : '0'}
222
225
  inputmode=${kind === 'dayPeriod' ? nothing : 'numeric'}
223
226
  enterkeyhint=${editable ? 'next' : nothing}
224
227
  aria-label=${this.labeled ? `${segmentName(kind, locale)},` : segmentName(kind, locale)}
225
228
  aria-labelledby=${this.labeled ? `${id} ${this.labelId}` : nothing}
226
- aria-valuemin=${min}
227
- aria-valuemax=${max}
228
- aria-valuenow=${shown ?? nothing}
229
- aria-valuetext=${empty ? this.emptySegmentLabel : segmentValueText(kind, this.parts, twelve, locale)}
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}
230
234
  aria-disabled=${this.isDisabled ? 'true' : nothing}
231
235
  aria-readonly=${this.readOnly ? 'true' : nothing}
232
236
  @keydown=${this.handleSegmentKeydown}
@@ -1,7 +1,12 @@
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>`, a
4
- * `placeholder`, and its content where its role is named by its content.
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
@@ -23,7 +23,7 @@ const NAMED_BY_CONTENT = new Set([
23
23
  'tab',
24
24
  ]);
25
25
  export const hasName = (element, shown) => {
26
- for (const attribute of ['aria-label', 'title', 'placeholder']) {
26
+ for (const attribute of ['aria-label', 'title']) {
27
27
  if (element.getAttribute(attribute)?.trim()) {
28
28
  return true;
29
29
  }
@@ -40,9 +40,15 @@ export const hasName = (element, shown) => {
40
40
  if ([...(labels ?? [])].some((label) => nameText(label).trim() !== '')) {
41
41
  return true;
42
42
  }
43
- const role = element.getAttribute('role') ?? (element.localName === 'button' ? 'button' : '');
43
+ const role = element.getAttribute('role') ?? implicitRole(element);
44
44
  return NAMED_BY_CONTENT.has(role) && nameText(element, shown).trim() !== '';
45
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' : '';
51
+ };
46
52
  const audited = new WeakSet();
47
53
  const waiting = new Map();
48
54
  const readWaiting = () => {
@@ -69,9 +75,12 @@ export const auditName = (host, control, fix, also, shown) => {
69
75
  if (also?.() || hasName(host) || (inner !== null && hasName(inner, value))) {
70
76
  return;
71
77
  }
78
+ const placeholder = [host, inner].some((one) => one?.getAttribute('placeholder')?.trim());
72
79
  reportRuntime({
73
80
  kind: 'missing-name',
74
- message: `<${host.localName}> has no accessible name, so a screen reader announces it by its role alone. ${fix}`,
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}`,
75
84
  tag: host.localName,
76
85
  });
77
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
+ };
@@ -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,7 @@
1
+ /**
2
+ * `value` held between `low` and `high`.
3
+ *
4
+ * Written four times before: Lightbox's zoom, Scroll Area's scrollbar, Color
5
+ * Picker's channels and core's color reading.
6
+ */
7
+ export declare const clamp: (value: number, low: number, high: number) => number;
@@ -0,0 +1 @@
1
+ export const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
@@ -1,4 +1,4 @@
1
- const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
1
+ import { clamp } from '../clamp/clamp.js';
2
2
  const toAlpha = (token) => {
3
3
  if (token === undefined) {
4
4
  return 100;
@@ -1,4 +1,4 @@
1
- import type { Relative } from '../relative-time/relative-time.js';
1
+ import { type Relative } from '../relative-time/relative-time.js';
2
2
  /**
3
3
  * A formatter for one locale, kept.
4
4
  *
@@ -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,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
+ };