@fusion.dev/helix-headless 0.138.0 → 0.140.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.
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { CHART_PLOT, customElement, drawTooltip, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -58,10 +58,14 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
58
58
  return;
59
59
  }
60
60
  this.warnedAboutNegatives = true;
61
- console.warn(`<${this.localName}> read a negative number and drew it as none. Its bands are parts of a ` +
62
- 'total, and a part cannot be less than nothing: a band drawn below its neighbor would say ' +
63
- 'the part took some of the one beneath it away. A series that crosses zero is a line, so ' +
64
- 'reach for the line chart in this tier instead.');
61
+ reportRuntime({
62
+ kind: 'negative-value',
63
+ message: `<${this.localName}> read a negative number and drew it as none. Its bands are parts of a ` +
64
+ 'total, and a part cannot be less than nothing: a band drawn below its neighbor would say ' +
65
+ 'the part took some of the one beneath it away. A series that crosses zero is a line, so ' +
66
+ 'reach for the line chart in this tier instead.',
67
+ tag: this.localName,
68
+ });
65
69
  }
66
70
  render() {
67
71
  const rows = this.sourceRows;
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement, hostAria } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, hostAria, reportRuntime } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  export const FOLDS_UP_TO = 12;
@@ -35,9 +35,13 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
35
35
  const limit = this.max ?? 0;
36
36
  if (limit > FOLDS_UP_TO && this.avatars.length > limit && !this.warned) {
37
37
  this.warned = true;
38
- console.warn(`<${this.localName}> has max="${this.max}", past the ${FOLDS_UP_TO} the fold is written for, ` +
39
- 'so every avatar is shown and none are counted. Lower `max`, or hand the group the ' +
40
- 'avatars you want shown.');
38
+ reportRuntime({
39
+ kind: 'max-past-fold',
40
+ message: `<${this.localName}> has max="${this.max}", past the ${FOLDS_UP_TO} the fold is written for, ` +
41
+ 'so every avatar is shown and none are counted. Lower `max`, or hand the group the ' +
42
+ 'avatars you want shown.',
43
+ tag: this.localName,
44
+ });
41
45
  }
42
46
  }
43
47
  render() {
@@ -41,6 +41,7 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
41
41
  * @csspart label - The label next to the box.
42
42
  * @csspart mark - The check mark, or the dash when the box is indeterminate.
43
43
  * @csspart mark-checked - Added to `mark` while the box is checked.
44
+ * @csspart mark-dash - Added to `mark` while the box is indeterminate, and kept until the box is checked, so a mark that animates out leaves as the dash it was.
44
45
  * @csspart mark-indeterminate - Added to `mark` while the box is indeterminate.
45
46
  *
46
47
  * @cssstate checked - While the box is checked.
@@ -61,6 +62,17 @@ export declare class HelixHeadlessCheckbox extends HelixHeadlessCheckbox_base {
61
62
  * The value submitted when the box is checked.
62
63
  */
63
64
  value: string;
65
+ /**
66
+ * Whether the mark draws the dash: from the moment the box is indeterminate
67
+ * until it is checked.
68
+ *
69
+ * **Kept past indeterminate, so a mark leaving the box leaves as what it
70
+ * was.** A stylesheet that shrinks the mark away drew the tick in the dash's
71
+ * place for the whole exit, since the shape is a mask and a mask swaps at
72
+ * once. Measured on Data Table's select-all, deselecting the one row that
73
+ * was selected: 15 frames of a shrinking tick where the dash had been.
74
+ */
75
+ private dashed;
64
76
  /** Focus is delegated to the input, so the host is never the focus target. */
65
77
  static shadowRootOptions: ShadowRootInit;
66
78
  private readonly handleChange;
@@ -16,6 +16,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
16
16
  this.clicks = new HostClickController(this, () => this.partNamed('control'));
17
17
  this.indeterminate = false;
18
18
  this.value = 'on';
19
+ this.dashed = false;
19
20
  this.handleChange = (event) => {
20
21
  const input = event.target;
21
22
  const next = input.checked;
@@ -45,6 +46,12 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
45
46
  willUpdate(changed) {
46
47
  super.willUpdate(changed);
47
48
  this.internals.ariaRequired = this.required ? 'true' : null;
49
+ if (this.indeterminate) {
50
+ this.dashed = true;
51
+ }
52
+ else if (this.checked) {
53
+ this.dashed = false;
54
+ }
48
55
  }
49
56
  updated(changed) {
50
57
  super.updated(changed);
@@ -79,7 +86,7 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
79
86
  @input=${this.stopInner}
80
87
  />
81
88
  <span
82
- part="mark${this.checked ? ' mark-checked' : ''}${this.indeterminate ? ' mark-indeterminate' : ''}"
89
+ part="mark${this.checked ? ' mark-checked' : ''}${this.indeterminate ? ' mark-indeterminate' : ''}${this.dashed ? ' mark-dash' : ''}"
83
90
  aria-hidden="true"
84
91
  ></span>
85
92
  </span>
@@ -86,6 +86,7 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
86
86
  * @csspart checkbox-label - Forwarded from the checkbox. The label is read by screen readers only.
87
87
  * @csspart checkbox-mark - The check mark, or the dash while some rows are selected.
88
88
  * @csspart checkbox-mark-checked - Added to `checkbox-mark` while its box is checked.
89
+ * @csspart checkbox-mark-dash - Added to `checkbox-mark` while it draws the dash, which it keeps until its box is checked.
89
90
  * @csspart checkbox-mark-indeterminate - Added to `checkbox-mark` while only some rows are selected.
90
91
  * @csspart empty - Shown when there are no rows, because none arrived or the filter matches none. While rows are loading, the `loading` part is shown in its place.
91
92
  * @csspart footer - The region below the grid.
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessDataTable_1;
8
- import { customElement, fillTemplate, formatNumbers, HelixElement, HelixFilterEvent, HelixNavigateEvent, HelixReorderEvent, HelixSelectEvent, HelixSortEvent, hostAria, inlineStep, isRtl, MixinCollection, markRegion, requestChange, VirtualizeController, writtenList, } from '@fusion.dev/helix-core';
8
+ import { customElement, fillTemplate, formatNumbers, HelixElement, HelixFilterEvent, HelixNavigateEvent, HelixReorderEvent, HelixSelectEvent, HelixSortEvent, hostAria, inlineStep, isRtl, MixinCollection, markRegion, reportRuntime, requestChange, VirtualizeController, writtenList, } from '@fusion.dev/helix-core';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { repeat } from 'lit/directives/repeat.js';
@@ -308,10 +308,14 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
308
308
  return;
309
309
  }
310
310
  this.warnedAboutSize = true;
311
- console.warn(`<${this.localName}> is drawing ${drawn} rows at once, which is past the ${HelixHeadlessDataTable_1.COMFORTABLE} ` +
312
- 'it stays responsive to: every row is rendered, so this costs roughly 37 microseconds each and ' +
313
- 'crosses an interaction budget around 2,700. Set `page-size` to paginate, or `mode="server"` ' +
314
- 'to hand it one page at a time with `total` describing the whole result.');
311
+ reportRuntime({
312
+ kind: 'too-many-rows',
313
+ message: `<${this.localName}> is drawing ${drawn} rows at once, which is past the ${HelixHeadlessDataTable_1.COMFORTABLE} ` +
314
+ 'it stays responsive to: every row is rendered, so this costs roughly 37 microseconds each and ' +
315
+ 'crosses an interaction budget around 2,700. Set `page-size` to paginate, or `mode="server"` ' +
316
+ 'to hand it one page at a time with `total` describing the whole result.',
317
+ tag: this.localName,
318
+ });
315
319
  }
316
320
  get view() {
317
321
  if (this.mode === 'server') {
@@ -414,7 +418,11 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
414
418
  }
415
419
  if (this.selectable && !this.warnedAboutKey && typeof window !== 'undefined') {
416
420
  this.warnedAboutKey = true;
417
- console.warn(`<${this.localName} selectable> has no \`row-key\`, so it knows each row by its place in the result, and a sort, a filter or new data selects a different row than the one that was chosen. Set \`row-key\` to the field that identifies a row.`);
421
+ reportRuntime({
422
+ kind: 'missing-row-key',
423
+ message: `<${this.localName} selectable> has no \`row-key\`, so it knows each row by its place in the result, and a sort, a filter or new data selects a different row than the one that was chosen. Set \`row-key\` to the field that identifies a row.`,
424
+ tag: this.localName,
425
+ });
418
426
  }
419
427
  return String(place);
420
428
  }
@@ -591,7 +599,8 @@ let HelixHeadlessDataTable = class HelixHeadlessDataTable extends MixinCollectio
591
599
  part="checkbox"
592
600
  exportparts="box: checkbox-box, control: checkbox-control, feedback: checkbox-feedback,
593
601
  field: checkbox-field, hint: checkbox-hint, label: checkbox-label, mark: checkbox-mark,
594
- mark-checked: checkbox-mark-checked, mark-indeterminate: checkbox-mark-indeterminate"
602
+ mark-checked: checkbox-mark-checked, mark-dash: checkbox-mark-dash,
603
+ mark-indeterminate: checkbox-mark-indeterminate"
595
604
  aria-label=${name}
596
605
  .checked=${checked}
597
606
  .indeterminate=${indeterminate}
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, reportRuntime } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { DEFAULT_FILTER_OPERATORS } from '../grammar.js';
@@ -21,7 +21,11 @@ let HelixHeadlessFilterField = class HelixHeadlessFilterField extends HelixEleme
21
21
  get defaultOperator() {
22
22
  const marked = this.operators.filter((operator) => operator.default);
23
23
  if (marked.length > 1) {
24
- console.warn(`\`${this.name || 'A filter field'}\` names ${marked.length} default operators, and a chip can only leave one unwritten. Mark one.`);
24
+ reportRuntime({
25
+ kind: 'two-default-operators',
26
+ message: `\`${this.name || 'A filter field'}\` names ${marked.length} default operators, and a chip can only leave one unwritten. Mark one.`,
27
+ tag: this.localName,
28
+ });
25
29
  }
26
30
  return marked[0] ?? null;
27
31
  }
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { valueAsNumber as asNumber, customElement, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, } from '@fusion.dev/helix-core';
7
+ import { valueAsNumber as asNumber, customElement, numberAsValue as fromNumber, HelixFormElement, KeyboardNavigationController, MixinControllerValue, MixinField, reportRuntime, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue(MixinField(HelixFormElement)) {
@@ -169,9 +169,13 @@ let HelixHeadlessRating = class HelixHeadlessRating extends MixinControllerValue
169
169
  this.sync();
170
170
  if (this.floor !== this.min && !this.warned) {
171
171
  this.warned = true;
172
- console.warn(`<${this.localName}> has min="${this.min}", which is not a score between 0 and its max of ` +
173
- `${this.max}, so the lowest score it offers is ${this.floor}. Put min inside the marks, ` +
174
- 'or move max.');
172
+ reportRuntime({
173
+ kind: 'min-out-of-range',
174
+ message: `<${this.localName}> has min="${this.min}", which is not a score between 0 and its max of ` +
175
+ `${this.max}, so the lowest score it offers is ${this.floor}. Put min inside the marks, ` +
176
+ 'or move max.',
177
+ tag: this.localName,
178
+ });
175
179
  }
176
180
  }
177
181
  willUpdate(changed) {
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { anchorElement, customElement, elementById, HelixElement, hostAria, MixinControllerOpen, PopoverController, } from '@fusion.dev/helix-core';
7
+ import { anchorElement, customElement, elementById, HelixElement, hostAria, MixinControllerOpen, PopoverController, reportRuntime, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  const GENERIC = new Set(['div', 'p', 'section', 'span']);
@@ -175,17 +175,25 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
175
175
  }
176
176
  this.audited = true;
177
177
  if (this.for === '') {
178
- console.warn(`<${this.localName}> is wrapping its trigger rather than naming one, so its text ` +
179
- "reaches no screen reader: a description in this element's shadow root cannot be " +
180
- 'referenced from a trigger in the light DOM. Give this tooltip an `id`, set `for` to ' +
181
- 'the trigger\'s id, and add `aria-describedby="<that id>"` to the trigger.');
178
+ reportRuntime({
179
+ kind: 'wrapped-trigger',
180
+ message: `<${this.localName}> is wrapping its trigger rather than naming one, so its text ` +
181
+ "reaches no screen reader: a description in this element's shadow root cannot be " +
182
+ 'referenced from a trigger in the light DOM. Give this tooltip an `id`, set `for` to ' +
183
+ 'the trigger\'s id, and add `aria-describedby="<that id>"` to the trigger.',
184
+ tag: this.localName,
185
+ });
182
186
  return;
183
187
  }
184
188
  const trigger = this.named;
185
189
  if (!trigger) {
186
- console.warn(`<${this.localName} for="${this.for}"> found no element with that id in its own tree, ` +
187
- 'so it has nothing to describe and nothing to point at. Ids resolve per tree: an ' +
188
- 'element inside another shadow root is not reachable from here.');
190
+ reportRuntime({
191
+ kind: 'missing-trigger',
192
+ message: `<${this.localName} for="${this.for}"> found no element with that id in its own tree, ` +
193
+ 'so it has nothing to describe and nothing to point at. Ids resolve per tree: an ' +
194
+ 'element inside another shadow root is not reachable from here.',
195
+ tag: this.localName,
196
+ });
189
197
  return;
190
198
  }
191
199
  const names = `${trigger.getAttribute('aria-describedby') ?? ''} ${trigger.getAttribute('aria-labelledby') ?? ''}`
@@ -194,10 +202,14 @@ let HelixHeadlessTooltip = class HelixHeadlessTooltip extends MixinControllerOpe
194
202
  if (this.id !== '' && names.includes(this.id)) {
195
203
  return;
196
204
  }
197
- console.warn(`<${this.localName} for="${this.for}"> is not read out with its trigger. The link is ` +
198
- 'written from the trigger, not from here: give this tooltip an `id` and add ' +
199
- '`aria-describedby="<that id>"` to the element it names. Written in your own markup ' +
200
- 'it survives server rendering, which is why this element will not write it for you.');
205
+ reportRuntime({
206
+ kind: 'unlinked-trigger',
207
+ message: `<${this.localName} for="${this.for}"> is not read out with its trigger. The link is ` +
208
+ 'written from the trigger, not from here: give this tooltip an `id` and add ' +
209
+ '`aria-describedby="<that id>"` to the element it names. Written in your own markup ' +
210
+ 'it survives server rendering, which is why this element will not write it for you.',
211
+ tag: this.localName,
212
+ });
201
213
  }
202
214
  connectedCallback() {
203
215
  super.connectedCallback();
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { activeCue, adoptTrack, CueController, captionsAmong, captionTracks, copiesOf, customElement, drawIcon, FullscreenController, fillTemplate, HelixElement, HelixNextEvent, HelixPreviousEvent, IdleController, MediaSessionController, onUnlessFalse, RemotePlaybackController, SKIP_SECONDS, sameCopies, showCaption, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
7
+ import { activeCue, adoptTrack, CueController, captionsAmong, captionTracks, copiesOf, customElement, drawIcon, FullscreenController, fillTemplate, HelixElement, HelixNextEvent, HelixPreviousEvent, IdleController, MediaSessionController, onUnlessFalse, RemotePlaybackController, reportRuntime, SKIP_SECONDS, sameCopies, showCaption, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { airplay, arrowClockwise, arrowCounterClockwise, closedCaptioning, cornersIn, cornersOut, dotsThreeVertical, download, gauge, pause, pictureInPicture, play, skipBack, skipForward, speakerHigh, speakerX, } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -378,7 +378,12 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
378
378
  await video.requestPictureInPicture();
379
379
  }
380
380
  catch (error) {
381
- console.warn(`<${this.localName}> could not enter picture in picture. A press has to come from the reader, and one window holds it at a time.`, error);
381
+ reportRuntime({
382
+ detail: error,
383
+ kind: 'picture-in-picture-refused',
384
+ message: `<${this.localName}> could not enter picture in picture. A press has to come from the reader, and one window holds it at a time.`,
385
+ tag: this.localName,
386
+ });
382
387
  }
383
388
  }
384
389
  willUpdate(changed) {