@fusion.dev/helix-core 0.103.2 → 0.104.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 (37) hide show
  1. package/custom-elements.json +3668 -1787
  2. package/dist/controllers/arrival-controller/arrival-controller.d.ts +43 -0
  3. package/dist/controllers/arrival-controller/arrival-controller.js +35 -0
  4. package/dist/controllers/cue-controller/cue-controller.d.ts +8 -0
  5. package/dist/controllers/cue-controller/cue-controller.js +4 -1
  6. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +4 -0
  7. package/dist/controllers/remote-playback-controller/remote-playback-controller.d.ts +15 -0
  8. package/dist/controllers/remote-playback-controller/remote-playback-controller.js +19 -3
  9. package/dist/events/events.d.ts +253 -6
  10. package/dist/events/events.js +144 -0
  11. package/dist/helix-form-element/helix-form-element.d.ts +9 -0
  12. package/dist/helix-form-element/helix-form-element.js +1 -0
  13. package/dist/index.d.ts +7 -1
  14. package/dist/index.js +7 -1
  15. package/dist/mixins/mixin-collection/mixin-collection.d.ts +6 -0
  16. package/dist/mixins/mixin-collection/mixin-collection.js +5 -1
  17. package/dist/mixins/mixin-controller-open/mixin-controller-open.js +3 -3
  18. package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +1 -1
  19. package/dist/mixins/mixin-controller-value/mixin-controller-value.js +2 -1
  20. package/dist/mixins/mixin-field/mixin-field.d.ts +11 -0
  21. package/dist/mixins/mixin-field/mixin-field.js +25 -3
  22. package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +8 -0
  23. package/dist/mixins/mixin-picker-field/mixin-picker-field.js +22 -2
  24. package/dist/mixins/mixin-tokens/mixin-tokens.js +0 -1
  25. package/dist/mixins/mixin-viewport/mixin-viewport.d.ts +97 -0
  26. package/dist/mixins/mixin-viewport/mixin-viewport.js +215 -0
  27. package/dist/utils/fill-template/fill-template.d.ts +13 -0
  28. package/dist/utils/fill-template/fill-template.js +1 -0
  29. package/dist/utils/key-claimed/key-claimed.d.ts +18 -0
  30. package/dist/utils/key-claimed/key-claimed.js +14 -0
  31. package/dist/utils/report-values/report-values.d.ts +21 -0
  32. package/dist/utils/report-values/report-values.js +11 -0
  33. package/dist/utils/slotted-words/slotted-words.d.ts +11 -0
  34. package/dist/utils/slotted-words/slotted-words.js +5 -0
  35. package/dist/utils/tell-disclosure/tell-disclosure.d.ts +3 -1
  36. package/dist/utils/tell-disclosure/tell-disclosure.js +1 -1
  37. package/package.json +4 -4
@@ -111,6 +111,15 @@ export declare class HelixFormElement extends HelixElement {
111
111
  * middle of removing.
112
112
  */
113
113
  formDisabledCallback(disabled: boolean): void;
114
+ /**
115
+ * The form was reset. Nothing here, and every override calls it.
116
+ *
117
+ * **A chain rather than a leaf**, because more than one layer owes a reset
118
+ * something: the value mixin its default, and the field mixin beneath it the
119
+ * forgetting of a reader's past attempt, whose message otherwise showed over
120
+ * the emptied control the moment the form was reset.
121
+ */
122
+ formResetCallback(): void;
114
123
  protected willUpdate(changed: PropertyValues): void;
115
124
  /**
116
125
  * Announces `input` from the host, the way the control it stands for would.
@@ -20,6 +20,7 @@ export class HelixFormElement extends HelixElement {
20
20
  formDisabledCallback(disabled) {
21
21
  this.formDisabled = disabled && !this.disabled;
22
22
  }
23
+ formResetCallback() { }
23
24
  willUpdate(changed) {
24
25
  super.willUpdate(changed);
25
26
  this.internals.ariaDisabled = this.isDisabled ? 'true' : null;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { AriaForwardController, type AriaForwardOptions, } from './controllers/aria-forward-controller/aria-forward-controller.js';
2
+ export { ArrivalController, type ArrivalOptions, } from './controllers/arrival-controller/arrival-controller.js';
2
3
  export { ChartPointerController, type ChartPointerHost, } from './controllers/chart-pointer-controller/chart-pointer-controller.js';
3
4
  export { CommandController, type CommandOptions, HelixFallbackCommandEvent, supportsInvokers, } from './controllers/command-controller/command-controller.js';
4
5
  export { CopyController, type CopyOptions, } from './controllers/copy-controller/copy-controller.js';
@@ -19,7 +20,7 @@ export { SourceController, type SourceHost, } from './controllers/source-control
19
20
  export { type TokenColor, TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
20
21
  export { type Rendered, VirtualizeController, type VirtualizeOptions, type Window as VirtualizeWindow, } from './controllers/virtualize-controller/virtualize-controller.js';
21
22
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
22
- export { type CaretDetail, type ChangeDetail, type CompleteDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapPanEvent, HelixMapZoomEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type MapPanDetail, type MapZoomDetail, type NavigateDetail, type OpenDetail, type ReadDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, } from './events/events.js';
23
+ export { type CanvasView, type CaretDetail, type ChangeDetail, type CompleteDetail, type ConnectDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapPanEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type MapPanDetail, type MapZoomDetail, type MoveDetail, type NavigateDetail, type OpenDetail, type ReadDetail, type ReconnectDetail, type RelabelDetail, type RemoveDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, type ViewportPanDetail, type ViewportZoomDetail, } from './events/events.js';
23
24
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
24
25
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
25
26
  export { MixinChartSource, type MixinChartSourceInterface, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
@@ -34,6 +35,7 @@ export { MixinPickerEditing, type MixinPickerEditingInterface, } from './mixins/
34
35
  export { MixinPickerField, type MixinPickerFieldInterface, } from './mixins/mixin-picker-field/mixin-picker-field.js';
35
36
  export { MixinSelectableItem, type MixinSelectableItemInterface, } from './mixins/mixin-selectable-item/mixin-selectable-item.js';
36
37
  export { MixinTokens, type MixinTokensInterface } from './mixins/mixin-tokens/mixin-tokens.js';
38
+ export { MixinViewport, type ViewportControlsPlacement, type ViewportElement, type ViewportWay, } from './mixins/mixin-viewport/mixin-viewport.js';
37
39
  export { anchorElement } from './utils/anchor-element/anchor-element.js';
38
40
  export { capturePointer } from './utils/capture-pointer/capture-pointer.js';
39
41
  export { closestInstance } from './utils/closest-instance/closest-instance.js';
@@ -44,12 +46,14 @@ export { type DrawIconOptions, drawIcon } from './utils/draw-icon/draw-icon.js';
44
46
  export { CHART_PLOT, drawTooltip } from './utils/draw-tooltip/draw-tooltip.js';
45
47
  export { elapsedTime } from './utils/elapsed-time/elapsed-time.js';
46
48
  export { type ElementByIdOptions, elementById, searchableById, } from './utils/element-by-id/element-by-id.js';
49
+ export { fillTemplate } from './utils/fill-template/fill-template.js';
47
50
  export { deepActiveElement, deepContains, focusableElements, } from './utils/focusable-elements/focusable-elements.js';
48
51
  export { formatNumbers } from './utils/format-numbers/format-numbers.js';
49
52
  export { type HostAria, hostAria } from './utils/host-aria/host-aria.js';
50
53
  export { type DisableableItem, forgetDisabled, inheritDisabled, } from './utils/inherit-disabled/inherit-disabled.js';
51
54
  export { inlineStep } from './utils/inline-step/inline-step.js';
52
55
  export { isRtl } from './utils/is-rtl/is-rtl.js';
56
+ export { keyClaimed } from './utils/key-claimed/key-claimed.js';
53
57
  export { type Curve, linePath, type Point } from './utils/line-path/line-path.js';
54
58
  export { markRegion, type RegionOptions } from './utils/mark-region/mark-region.js';
55
59
  export { copiesOf, type MediaCopy, sameCopies, } from './utils/media-copy/media-copy.js';
@@ -57,6 +61,7 @@ export { onUnlessFalse } from './utils/on-unless-false/on-unless-false.js';
57
61
  export { type ColorBytes, readColor } from './utils/read-color/read-color.js';
58
62
  export { type Row, type RowCell, readRows, readSeries, type Series, type SeriesPoint, } from './utils/read-series/read-series.js';
59
63
  export { type Relative, relativeTime, } from './utils/relative-time/relative-time.js';
64
+ export { reportValues } from './utils/report-values/report-values.js';
60
65
  export { type ChangeRequestOptions, requestChange, } from './utils/request-change/request-change.js';
61
66
  export { type ActivatableItem, resolveActiveItem, } from './utils/resolve-active-item/resolve-active-item.js';
62
67
  export { holdCaret, restoreValue } from './utils/restore-value/restore-value.js';
@@ -64,6 +69,7 @@ export { extent, linear, type Scale } from './utils/scale/scale.js';
64
69
  export { ticks } from './utils/scale/ticks.js';
65
70
  export { type ScrollItemOptions, scrollItemIntoView, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
66
71
  export { slotFilled } from './utils/slot-filled/slot-filled.js';
72
+ export { slottedWords } from './utils/slotted-words/slotted-words.js';
67
73
  export { splitValues } from './utils/split-values/split-values.js';
68
74
  export { tellDisclosure } from './utils/tell-disclosure/tell-disclosure.js';
69
75
  export { subscribe } from './utils/ticker/ticker.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { AriaForwardController, } from './controllers/aria-forward-controller/aria-forward-controller.js';
2
+ export { ArrivalController, } from './controllers/arrival-controller/arrival-controller.js';
2
3
  export { ChartPointerController, } from './controllers/chart-pointer-controller/chart-pointer-controller.js';
3
4
  export { CommandController, HelixFallbackCommandEvent, supportsInvokers, } from './controllers/command-controller/command-controller.js';
4
5
  export { CopyController, } from './controllers/copy-controller/copy-controller.js';
@@ -19,7 +20,7 @@ export { SourceController, } from './controllers/source-controller/source-contro
19
20
  export { TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
20
21
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
21
22
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
22
- export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapPanEvent, HelixMapZoomEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
23
+ export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapPanEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
23
24
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
24
25
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
25
26
  export { MixinChartSource, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
@@ -34,6 +35,7 @@ export { MixinPickerEditing, } from './mixins/mixin-picker-editing/mixin-picker-
34
35
  export { MixinPickerField, } from './mixins/mixin-picker-field/mixin-picker-field.js';
35
36
  export { MixinSelectableItem, } from './mixins/mixin-selectable-item/mixin-selectable-item.js';
36
37
  export { MixinTokens } from './mixins/mixin-tokens/mixin-tokens.js';
38
+ export { MixinViewport, } from './mixins/mixin-viewport/mixin-viewport.js';
37
39
  export { anchorElement } from './utils/anchor-element/anchor-element.js';
38
40
  export { capturePointer } from './utils/capture-pointer/capture-pointer.js';
39
41
  export { closestInstance } from './utils/closest-instance/closest-instance.js';
@@ -44,12 +46,14 @@ export { drawIcon } from './utils/draw-icon/draw-icon.js';
44
46
  export { CHART_PLOT, drawTooltip } from './utils/draw-tooltip/draw-tooltip.js';
45
47
  export { elapsedTime } from './utils/elapsed-time/elapsed-time.js';
46
48
  export { elementById, searchableById, } from './utils/element-by-id/element-by-id.js';
49
+ export { fillTemplate } from './utils/fill-template/fill-template.js';
47
50
  export { deepActiveElement, deepContains, focusableElements, } from './utils/focusable-elements/focusable-elements.js';
48
51
  export { formatNumbers } from './utils/format-numbers/format-numbers.js';
49
52
  export { hostAria } from './utils/host-aria/host-aria.js';
50
53
  export { forgetDisabled, inheritDisabled, } from './utils/inherit-disabled/inherit-disabled.js';
51
54
  export { inlineStep } from './utils/inline-step/inline-step.js';
52
55
  export { isRtl } from './utils/is-rtl/is-rtl.js';
56
+ export { keyClaimed } from './utils/key-claimed/key-claimed.js';
53
57
  export { linePath } from './utils/line-path/line-path.js';
54
58
  export { markRegion } from './utils/mark-region/mark-region.js';
55
59
  export { copiesOf, sameCopies, } from './utils/media-copy/media-copy.js';
@@ -57,6 +61,7 @@ export { onUnlessFalse } from './utils/on-unless-false/on-unless-false.js';
57
61
  export { readColor } from './utils/read-color/read-color.js';
58
62
  export { readRows, readSeries, } from './utils/read-series/read-series.js';
59
63
  export { relativeTime, } from './utils/relative-time/relative-time.js';
64
+ export { reportValues } from './utils/report-values/report-values.js';
60
65
  export { requestChange, } from './utils/request-change/request-change.js';
61
66
  export { resolveActiveItem, } from './utils/resolve-active-item/resolve-active-item.js';
62
67
  export { holdCaret, restoreValue } from './utils/restore-value/restore-value.js';
@@ -64,6 +69,7 @@ export { extent, linear } from './utils/scale/scale.js';
64
69
  export { ticks } from './utils/scale/ticks.js';
65
70
  export { scrollItemIntoView, } from './utils/scroll-item-into-view/scroll-item-into-view.js';
66
71
  export { slotFilled } from './utils/slot-filled/slot-filled.js';
72
+ export { slottedWords } from './utils/slotted-words/slotted-words.js';
67
73
  export { splitValues } from './utils/split-values/split-values.js';
68
74
  export { tellDisclosure } from './utils/tell-disclosure/tell-disclosure.js';
69
75
  export { subscribe } from './utils/ticker/ticker.js';
@@ -17,6 +17,12 @@ export declare class CollectionElement {
17
17
  * Rendered by the component wherever its rows would be.
18
18
  */
19
19
  protected renderStatus(hasContent: boolean): TemplateResult | typeof nothing;
20
+ /**
21
+ * The same words for a screen reader, in a region that is always there.
22
+ *
23
+ * Rendered by the component outside its rows, where a status is allowed.
24
+ */
25
+ protected renderLive(hasContent: boolean): TemplateResult;
20
26
  /** Binds to a rows slot's `slotchange`, and answers `hasRows` from it. */
21
27
  protected countRows(event: Event): void;
22
28
  /** What the rows slot is given, read from the host so a first render has it. */
@@ -43,13 +43,17 @@ export const MixinCollection = (Base) => {
43
43
  }
44
44
  renderStatus(hasContent) {
45
45
  if (this.loading) {
46
- return html `<p part="loading" role="status">${this.loadingText}</p>`;
46
+ return html `<p part="loading">${this.loadingText}</p>`;
47
47
  }
48
48
  if (hasContent) {
49
49
  return nothing;
50
50
  }
51
51
  return html `<p part="empty">${this.emptyText}</p>`;
52
52
  }
53
+ renderLive(hasContent) {
54
+ const said = this.loading ? this.loadingText : hasContent ? '' : this.emptyText;
55
+ return html `<span part="live" role="status">${said}</span>`;
56
+ }
53
57
  }
54
58
  __decorate([
55
59
  property({ attribute: 'empty-text' })
@@ -57,11 +57,11 @@ export const MixinControllerOpen = (Base) => {
57
57
  return this.requestOpen(!this.open);
58
58
  }
59
59
  }
60
- __decorate([
61
- property({ type: Boolean })
62
- ], OpenElement.prototype, "open", null);
63
60
  __decorate([
64
61
  property({ attribute: 'default-open', type: Boolean })
65
62
  ], OpenElement.prototype, "defaultOpen", void 0);
63
+ __decorate([
64
+ property({ type: Boolean })
65
+ ], OpenElement.prototype, "open", null);
66
66
  return OpenElement;
67
67
  };
@@ -40,7 +40,7 @@ export declare class MixinControllerValueInterface {
40
40
  /** Returns the control to the value its markup declared. */
41
41
  formResetCallback(): void;
42
42
  /** Restores the value when the browser brings a form back. */
43
- formStateRestoreCallback(state: string | null): void;
43
+ formStateRestoreCallback(state: string | File | FormData | null): void;
44
44
  }
45
45
  /**
46
46
  * Grafts `value`, `defaultValue`, and the two callbacks that answer a form.
@@ -49,13 +49,14 @@ export const MixinControllerValue = (Base) => {
49
49
  return applied;
50
50
  }
51
51
  formResetCallback() {
52
+ super.formResetCallback();
52
53
  const previous = this.value;
53
54
  this.valueState = undefined;
54
55
  this.requestUpdate('value', previous);
55
56
  }
56
57
  formStateRestoreCallback(state) {
57
58
  const previous = this.value;
58
- this.valueState = state ?? undefined;
59
+ this.valueState = typeof state === 'string' ? state : undefined;
59
60
  this.requestUpdate('value', previous);
60
61
  }
61
62
  }
@@ -16,6 +16,10 @@ export declare class MixinFieldInterface {
16
16
  * control being the family's. See the note on the class member.
17
17
  */
18
18
  protected controlId: string;
19
+ /** The id the label carries, for an element in the template to be named by. */
20
+ protected labelId: string;
21
+ /** Whether a label is drawn at all, in either shape. */
22
+ protected get labeled(): boolean;
19
23
  /**
20
24
  * Help under the control, shown while there is nothing wrong.
21
25
  *
@@ -78,8 +82,15 @@ export declare class MixinFieldInterface {
78
82
  * point its bubble at.
79
83
  */
80
84
  protected reportRequired(empty: boolean, message: string, anchor: string): void;
85
+ /**
86
+ * Hands on the verdict of a native control this renders, with the page's own
87
+ * words where it gave some.
88
+ */
89
+ protected copyValidity(control: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement): void;
81
90
  /** The message actually shown: the consumer's, or the platform's, or none. */
82
91
  readonly resolvedFeedback: string;
92
+ /** What a reader is told when the field is required and empty, where the page says. */
93
+ valueMissingMessage: string | undefined;
83
94
  }
84
95
  /**
85
96
  * Grafts a label, help text, and a validation message onto a control.
@@ -14,6 +14,7 @@ export const MixinField = (Base) => {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  this.controlId = 'control';
17
+ this.labelId = 'label';
17
18
  this.feedback = '';
18
19
  this.hint = '';
19
20
  this.label = '';
@@ -21,6 +22,9 @@ export const MixinField = (Base) => {
21
22
  this.touched = false;
22
23
  this.shownFeedback = '';
23
24
  }
25
+ get labeled() {
26
+ return this.label !== '' || Boolean(this.getAttribute('aria-label'));
27
+ }
24
28
  get validates() {
25
29
  return this.constructor.formAssociated === true;
26
30
  }
@@ -44,7 +48,7 @@ export const MixinField = (Base) => {
44
48
  if (this.label === '' && !hidden) {
45
49
  return nothing;
46
50
  }
47
- return html `<span part="label" ?data-hidden=${hidden !== null}
51
+ return html `<span part="label" id=${this.labelId} ?data-hidden=${hidden !== null}
48
52
  ><slot name="label">${this.label || hidden}</slot></span
49
53
  >`;
50
54
  }
@@ -53,7 +57,11 @@ export const MixinField = (Base) => {
53
57
  if (this.label === '' && !hidden) {
54
58
  return nothing;
55
59
  }
56
- return html `<label part="label" for=${this.controlId} ?data-hidden=${hidden !== null}
60
+ return html `<label
61
+ part="label"
62
+ for=${this.controlId}
63
+ id=${this.labelId}
64
+ ?data-hidden=${hidden !== null}
57
65
  ><slot name="label">${this.label || hidden}</slot></label
58
66
  >`;
59
67
  }
@@ -90,19 +98,30 @@ export const MixinField = (Base) => {
90
98
  }
91
99
  reportRequired(empty, message, anchor) {
92
100
  if (this.required && empty) {
93
- this.internals.setValidity({ valueMissing: true }, message, this.partNamed(anchor) ?? undefined);
101
+ this.internals.setValidity({ valueMissing: true }, this.valueMissingMessage || message, this.partNamed(anchor) ?? undefined);
94
102
  }
95
103
  else {
96
104
  this.internals.setValidity({});
97
105
  }
98
106
  this.refreshFeedback();
99
107
  }
108
+ copyValidity(control) {
109
+ const message = control.validity.valueMissing && this.valueMissingMessage
110
+ ? this.valueMissingMessage
111
+ : control.validationMessage;
112
+ this.internals.setValidity(control.validity, message, control);
113
+ }
100
114
  willUpdate(changed) {
101
115
  super.willUpdate(changed);
102
116
  this.shownFeedback = this.resolvedFeedback;
103
117
  this.toggleState('invalid', this.shownFeedback !== '');
104
118
  this.internals.ariaInvalid = this.shownFeedback === '' ? null : 'true';
105
119
  }
120
+ formResetCallback() {
121
+ ;
122
+ Base.prototype.formResetCallback?.call(this);
123
+ this.touched = false;
124
+ }
106
125
  firstUpdated(changed) {
107
126
  super.firstUpdated(changed);
108
127
  const touch = () => {
@@ -124,6 +143,9 @@ export const MixinField = (Base) => {
124
143
  __decorate([
125
144
  property({ reflect: true, type: Boolean })
126
145
  ], FieldElement.prototype, "required", void 0);
146
+ __decorate([
147
+ property({ attribute: 'value-missing-message' })
148
+ ], FieldElement.prototype, "valueMissingMessage", void 0);
127
149
  __decorate([
128
150
  state()
129
151
  ], FieldElement.prototype, "touched", void 0);
@@ -17,6 +17,8 @@ type PickerBase = HelixFormElement & MixinControllerOpenInterface & MixinControl
17
17
  * to this interface keeps the internals private and the build working.
18
18
  */
19
19
  export declare class MixinPickerFieldInterface {
20
+ /** What a reader is told when the field holds something the picker cannot read. */
21
+ badInputMessage: string | undefined;
20
22
  /**
21
23
  * What the label points at and focus lands on.
22
24
  *
@@ -25,6 +27,12 @@ export declare class MixinPickerFieldInterface {
25
27
  * can name.
26
28
  */
27
29
  protected get anchor(): HTMLElement | null;
30
+ /**
31
+ * Each answer the field holds, where it holds several, or `undefined` where
32
+ * its value is one answer. Several are sent one entry each; see
33
+ * `reportValues`.
34
+ */
35
+ protected get answers(): readonly string[] | undefined;
28
36
  /** The text input, or `null` where there is no DOM to hold one. */
29
37
  protected get control(): HTMLInputElement | null;
30
38
  /**
@@ -1,4 +1,12 @@
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 { property } from 'lit/decorators.js';
1
8
  import { PopoverController } from '../../controllers/popover-controller/popover-controller.js';
9
+ import { reportValues } from '../../utils/report-values/report-values.js';
2
10
  export const MixinPickerField = (Base) => {
3
11
  class PickerFieldElement extends Base {
4
12
  constructor() {
@@ -36,6 +44,9 @@ export const MixinPickerField = (Base) => {
36
44
  get display() {
37
45
  return this.value;
38
46
  }
47
+ get answers() {
48
+ return undefined;
49
+ }
39
50
  validate() {
40
51
  return '';
41
52
  }
@@ -47,14 +58,20 @@ export const MixinPickerField = (Base) => {
47
58
  this.sync();
48
59
  }
49
60
  sync() {
50
- this.internals.setFormValue(this.value);
61
+ const answers = this.answers;
62
+ if (answers === undefined) {
63
+ this.internals.setFormValue(this.value);
64
+ }
65
+ else {
66
+ reportValues(this.internals, this.name, answers);
67
+ }
51
68
  const control = this.control;
52
69
  if (!control) {
53
70
  return;
54
71
  }
55
72
  const message = this.validate();
56
73
  if (message === '') {
57
- this.internals.setValidity(control.validity, control.validationMessage, control);
74
+ this.copyValidity(control);
58
75
  }
59
76
  else {
60
77
  this.internals.setValidity({ badInput: true }, message, control);
@@ -78,5 +95,8 @@ export const MixinPickerField = (Base) => {
78
95
  this.sync();
79
96
  }
80
97
  }
98
+ __decorate([
99
+ property({ attribute: 'bad-input-message' })
100
+ ], PickerFieldElement.prototype, "badInputMessage", void 0);
81
101
  return PickerFieldElement;
82
102
  };
@@ -37,7 +37,6 @@ export const MixinTokens = (Base) => {
37
37
  return html `<button
38
38
  part="rest"
39
39
  type="button"
40
- aria-expanded=${this.expanded ? 'true' : 'false'}
41
40
  aria-label=${this.expanded ? this.collapseLabel : this.expandLabel}
42
41
  @click=${this.toggleExpanded}
43
42
  >${this.expanded ? drawIcon(caretUp) : `+${over}`}</button
@@ -0,0 +1,97 @@
1
+ import { type LitElement, nothing, type TemplateResult } from 'lit';
2
+ import { FullscreenController } from '../../controllers/fullscreen-controller/fullscreen-controller.js';
3
+ import { IdleController } from '../../controllers/idle-controller/idle-controller.js';
4
+ import type { ViewportPanDetail, ViewportZoomDetail } from '../../events/events.js';
5
+ type Constructor<T> = new (...args: any[]) => T;
6
+ /** Which way a press or a key asked the surface to go. */
7
+ export type ViewportWay = 'up' | 'down' | 'start' | 'end';
8
+ /** Where the controls sit, in the corner words the rest of the library uses. */
9
+ export type ViewportControlsPlacement = 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
10
+ /**
11
+ * The API a pan-and-zoom surface has, as TypeScript sees it.
12
+ */
13
+ export declare class ViewportElement {
14
+ /** Which of the controls the surface draws, as a list of what to keep. */
15
+ controls?: string;
16
+ /** Which corner the controls sit in. */
17
+ controlsPlacement?: ViewportControlsPlacement;
18
+ /** What the control that fills the screen is called. */
19
+ fullscreenLabel: string;
20
+ /** How long the reader may be still before the chrome hides, in milliseconds. */
21
+ idleDelay: number;
22
+ /** What the control that pans down is called. */
23
+ panDownLabel: string;
24
+ /** What the control that pans left is called. */
25
+ panLeftLabel: string;
26
+ /** What the control that pans right is called. */
27
+ panRightLabel: string;
28
+ /** How far one press moves the view, as a fraction of what is on screen. */
29
+ panStep: number;
30
+ /** What the control that pans up is called. */
31
+ panUpLabel: string;
32
+ /** What the control that zooms in is called. */
33
+ zoomInLabel: string;
34
+ /** What the control that zooms out is called. */
35
+ zoomOutLabel: string;
36
+ /** Asks for the view to move, which the consumer answers. */
37
+ pan(to: ViewportWay): void;
38
+ /** Asks for the view to zoom, `1` in and `-1` out. */
39
+ zoom(by: number): void;
40
+ /** Fills the screen with the surface, or comes back. */
41
+ toggleFullscreen(): void;
42
+ /** Whether the reader has been still long enough to hide the chrome. */
43
+ protected resting: boolean;
44
+ /** Whether the surface is filling the screen. */
45
+ protected filled: boolean;
46
+ /** How long the reader has been still, which the family's own frame stirs. */
47
+ protected readonly idle: IdleController;
48
+ /** Into the screen and back, for a family that offers its own way in. */
49
+ protected readonly fullscreen: FullscreenController;
50
+ /** The key relay, bound by the family's own frame. */
51
+ protected handleKeydown: (event: KeyboardEvent) => void;
52
+ /** What the group of controls is called, where the page says. */
53
+ controlsLabel: string | undefined;
54
+ /** What the group of controls is called where the page does not say, which each family says for itself. */
55
+ protected get restingControlsLabel(): string;
56
+ /** The pan event, built and not dispatched, for a family that names its own. */
57
+ protected panEvent(detail: ViewportPanDetail): Event;
58
+ /** The zoom event, built and not dispatched, for a family that names its own. */
59
+ protected zoomEvent(detail: ViewportZoomDetail): Event;
60
+ /** The controls, or nothing where the surface keeps none of them. */
61
+ protected renderControls(): TemplateResult | typeof nothing;
62
+ /** The drawn group of controls, where there is one, for a family that measures it. */
63
+ protected get controlsElement(): Element | null;
64
+ /** Which controls the surface draws while `controls` is absent. */
65
+ protected get restingControls(): string;
66
+ }
67
+ /**
68
+ * Grafts a pan-and-zoom surface: its controls, its keys, and the way out.
69
+ *
70
+ * **Nothing here is geographic, which is why it is here.** `Map` was written
71
+ * first and its frame turned out to be a surface a reader can move, fill the
72
+ * screen with, and stop touching: a canvas of notes and a graph of nodes want
73
+ * the same four hundred lines, and the only thing that made them Map's was
74
+ * being written there. Extracted before the second consumer rather than after,
75
+ * which is the opposite of what [#383](queue.md) had to conclude about a
76
+ * controller promoted ahead of one.
77
+ *
78
+ * **Nothing here moves anything, either.** Both controls ask, through an event
79
+ * a family names, and the answer is the consumer's: the surface a reader is
80
+ * looking at belongs to them, whether it is a tile renderer, a canvas or a
81
+ * force layout. A mixin that moved the view would have to know what the view
82
+ * was.
83
+ *
84
+ * **The controls are buttons, and that is a decision against arrow keys.** They
85
+ * work too, but what a screen reader does with them is not ours to decide:
86
+ * macOS passes them through, Windows readers hold them for their own reading
87
+ * mode unless a page takes `role="application"`, which costs the reader their
88
+ * normal way of reading everything else. A button is findable and operable in
89
+ * every screen reader on every platform without that bargain.
90
+ *
91
+ * **What stays in the family is `render()`, its slots and its part
92
+ * documentation**, because a part named through an interpolation is one the
93
+ * manifest cannot see: a loop over the four pan buttons would publish `pan-`
94
+ * and a consumer's `::part(pan-up)` would match nothing.
95
+ */
96
+ export declare const MixinViewport: <T extends Constructor<LitElement>>(Base: T) => Constructor<ViewportElement> & T;
97
+ export {};