@fusion.dev/helix-headless 0.58.0 → 0.59.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 (36) hide show
  1. package/custom-elements.json +24 -203
  2. package/dist/button/root/button-root.d.ts +1 -7
  3. package/dist/button/root/button-root.js +2 -3
  4. package/dist/carousel/root/carousel-root.js +2 -2
  5. package/dist/color-picker/root/color-picker-root.d.ts +7 -1
  6. package/dist/color-picker/root/color-picker-root.js +3 -3
  7. package/dist/data-table/root/data-table-root.js +2 -2
  8. package/dist/date-input/root/date-input-root.js +6 -7
  9. package/dist/date-picker/root/date-picker-root.js +8 -17
  10. package/dist/heatmap/index.d.ts +1 -1
  11. package/dist/heatmap/index.js +1 -1
  12. package/dist/heatmap/root/buckets.d.ts +0 -4
  13. package/dist/heatmap/root/buckets.js +0 -5
  14. package/dist/heatmap/root/heatmap-root.d.ts +5 -3
  15. package/dist/heatmap/root/heatmap-root.js +5 -6
  16. package/dist/legend/root/legend-root.d.ts +2 -2
  17. package/dist/outline/root/outline-root.d.ts +8 -1
  18. package/dist/outline/root/outline-root.js +2 -3
  19. package/dist/scatter-chart/root/scatter-chart-root.js +2 -2
  20. package/dist/skip-link/root/skip-link-root.d.ts +11 -1
  21. package/dist/skip-link/root/skip-link-root.js +2 -4
  22. package/dist/sparkline/root/sparkline-root.js +2 -5
  23. package/dist/splitter/root/splitter-root.js +3 -3
  24. package/dist/table/column/table-column.js +2 -2
  25. package/dist/table/root/table-root.js +2 -24
  26. package/dist/textarea/root/textarea-root.js +2 -2
  27. package/dist/time-input/root/time-input-root.js +10 -11
  28. package/dist/time-picker/root/time-picker-root.d.ts +0 -17
  29. package/dist/time-picker/root/time-picker-root.js +9 -32
  30. package/dist/tooltip/root/tooltip-root.d.ts +3 -10
  31. package/dist/tooltip/root/tooltip-root.js +3 -13
  32. package/dist/tour/stop/tour-stop.js +2 -3
  33. package/dist/video/root/video-root.js +2 -4
  34. package/package.json +5 -5
  35. package/types/jsx.d.ts +64 -68
  36. package/types/vue.d.ts +16 -20
@@ -21,15 +21,6 @@ const KEYS = new Set([
21
21
  'PageUp',
22
22
  ' ',
23
23
  ]);
24
- const toUtc = (iso) => {
25
- const parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
26
- if (!parts) {
27
- return null;
28
- }
29
- const date = new Date(Date.UTC(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3])));
30
- return Number.isNaN(date.getTime()) ? null : date;
31
- };
32
- const toIso = (date) => date.toISOString().slice(0, 10);
33
24
  const localToday = () => {
34
25
  const now = new Date();
35
26
  const month = `${now.getMonth() + 1}`.padStart(2, '0');
@@ -104,7 +95,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
104
95
  this.step = 1;
105
96
  }
106
97
  get month() {
107
- return this.monthState ?? (this.defaultMonth || toIso(this.derivedMonth).slice(0, 7));
98
+ return this.monthState ?? (this.defaultMonth || fromDate(this.derivedMonth).slice(0, 7));
108
99
  }
109
100
  set month(next) {
110
101
  const previous = this.month;
@@ -137,12 +128,12 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
137
128
  return this.derivedMonth;
138
129
  }
139
130
  get derivedMonth() {
140
- const from = toUtc(this.selected[0] ?? '') ?? new Date();
131
+ const from = asDate(this.selected[0] ?? '') ?? new Date();
141
132
  return new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), 1));
142
133
  }
143
134
  get anchor() {
144
135
  const shown = this.shown;
145
- for (const candidate of [toUtc(this.focused), toUtc(this.selected[0] ?? '')]) {
136
+ for (const candidate of [asDate(this.focused), asDate(this.selected[0] ?? '')]) {
146
137
  if (candidate && sameMonth(candidate, shown)) {
147
138
  return candidate;
148
139
  }
@@ -155,7 +146,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
155
146
  return Array.from({ length: 6 }, (_, week) => Array.from({ length: 7 }, (_, day) => addDays(start, week * 7 + day)));
156
147
  }
157
148
  barred(date) {
158
- const iso = toIso(date);
149
+ const iso = fromDate(date);
159
150
  if (this.min !== '' && iso < this.min) {
160
151
  return true;
161
152
  }
@@ -195,7 +186,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
195
186
  });
196
187
  }
197
188
  moveTo(date) {
198
- this.focused = toIso(date);
189
+ this.focused = fromDate(date);
199
190
  this.show(date);
200
191
  this.updateComplete.then(() => {
201
192
  this.renderRoot.querySelector('[part~="cell"][tabindex="0"]')?.focus();
@@ -208,7 +199,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
208
199
  if (this.barred(date)) {
209
200
  return;
210
201
  }
211
- const iso = toIso(date);
202
+ const iso = fromDate(date);
212
203
  const held = this.selected;
213
204
  let next;
214
205
  if (this.mode === 'range') {
@@ -309,7 +300,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
309
300
  renderMonth(month, index) {
310
301
  const { full, heading, weekday } = this.formats;
311
302
  const weeks = this.weeksOf(month);
312
- const anchor = toIso(this.anchor);
303
+ const anchor = fromDate(this.anchor);
313
304
  const today = localToday();
314
305
  const range = this.range;
315
306
  const first = index === 0;
@@ -349,7 +340,7 @@ let HelixHeadlessDatePicker = class HelixHeadlessDatePicker extends MixinControl
349
340
  </div>
350
341
  ${weeks.map((week) => html `<div part="week" role="row">
351
342
  ${week.map((date) => {
352
- const iso = toIso(date);
343
+ const iso = fromDate(date);
353
344
  const disabled = this.barred(date);
354
345
  const selected = this.isSelected(iso);
355
346
  const outside = !sameMonth(date, month);
@@ -1,2 +1,2 @@
1
- export { fits, numbers, type Reading, toneOf, words } from './root/buckets.js';
1
+ export { fits, type Reading, toneOf } from './root/buckets.js';
2
2
  export { HelixHeadlessHeatmap } from './root/heatmap-root.js';
@@ -1,2 +1,2 @@
1
- export { fits, numbers, toneOf, words } from './root/buckets.js';
1
+ export { fits, toneOf } from './root/buckets.js';
2
2
  export { HelixHeadlessHeatmap } from './root/heatmap-root.js';
@@ -29,10 +29,6 @@ export interface Reading {
29
29
  * painting it as the lowest bucket claims a reading that does not exist.
30
30
  */
31
31
  export declare const toneOf: (reading: Reading, thresholds: readonly number[], tones: readonly string[]) => string | undefined;
32
- /** A consumer's list of numbers, in the order written, with the rest dropped. */
33
- export declare const numbers: (source: string) => number[];
34
- /** A consumer's list of names, in the order written. */
35
- export declare const words: (source: string) => string[];
36
32
  /**
37
33
  * How many readings fit across a given width, and which ones they are.
38
34
  *
@@ -1,4 +1,3 @@
1
- import { writtenList } from '@fusion.dev/helix-core';
2
1
  export const toneOf = (reading, thresholds, tones) => {
3
2
  if (reading.tone) {
4
3
  return reading.tone;
@@ -14,10 +13,6 @@ export const toneOf = (reading, thresholds, tones) => {
14
13
  }
15
14
  return chosen;
16
15
  };
17
- export const numbers = (source) => writtenList(source)
18
- .map(Number)
19
- .filter((one) => Number.isFinite(one));
20
- export const words = (source) => writtenList(source);
21
16
  export const fits = (total, width, minimum, gap, rows = 1) => {
22
17
  if (!Number.isFinite(width) || width <= 0 || minimum <= 0) {
23
18
  return total;
@@ -211,9 +211,11 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
211
211
  * `MixinCollection` names for a slotted collection, arriving through the one
212
212
  * channel that mixin cannot see.
213
213
  *
214
- * The same shape as `childrenKnown`, asked of a document rather than of a
215
- * tree. An id that resolves to nothing in a browser is still known, and still
216
- * empty: a `for` naming no table should say so rather than sit blank.
214
+ * The same shape as `childrenKnown`, asked of a tree that can answer about
215
+ * ids rather than of one that holds children. An id that resolves to nothing
216
+ * in a browser is still known, and still empty: a `for` naming no table
217
+ * should say so rather than sit blank, which is why this asks
218
+ * `searchableById` and not `elementById`.
217
219
  */
218
220
  private get sourceKnown();
219
221
  /**
@@ -4,11 +4,11 @@ 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, HelixElement, HelixResizeEvent, hostAria, MixinChartSource, markRegion, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, HelixElement, HelixResizeEvent, hostAria, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
11
- import { fits, numbers, toneOf, words } from './buckets.js';
11
+ import { fits, toneOf } from './buckets.js';
12
12
  let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
13
13
  constructor() {
14
14
  super(...arguments);
@@ -45,8 +45,7 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
45
45
  if (this.hydrating) {
46
46
  return false;
47
47
  }
48
- const root = this.getRootNode();
49
- return typeof root.getElementById === 'function';
48
+ return searchableById(this);
50
49
  }
51
50
  get rowCount() {
52
51
  return Math.max(1, Math.floor(this.rows) || 1);
@@ -124,8 +123,8 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
124
123
  this.announced = showing;
125
124
  }
126
125
  render() {
127
- const bounds = numbers(this.thresholds);
128
- const palette = words(this.tones);
126
+ const bounds = writtenNumbers(this.thresholds);
127
+ const palette = writtenList(this.tones);
129
128
  const cells = this.drawn;
130
129
  return html `<div
131
130
  part="strip"
@@ -53,8 +53,8 @@ export declare class HelixHeadlessLegend extends HelixElement {
53
53
  /**
54
54
  * The id of the table to take the names from.
55
55
  *
56
- * Resolved through `getRootNode()`, so a legend inside another component's
57
- * shadow root names a table in that root rather than in the document.
56
+ * Names a table in the same tree, so a legend inside another component's
57
+ * shadow root reads a table in that root rather than one in the document.
58
58
  *
59
59
  * **In React this arrives as a property and not as an attribute**, the way
60
60
  * `Tooltip.for` does, so it does not reflect and nothing styles on it.
@@ -84,7 +84,14 @@ export declare class HelixHeadlessOutline extends HelixElement {
84
84
  private observer;
85
85
  /** The links, in document order. Empty on the server, which has no tree. */
86
86
  private get links();
87
- /** What each link points at, paired with the link, skipping anything missing. */
87
+ /**
88
+ * What each link points at, paired with the link, skipping anything missing.
89
+ *
90
+ * The fragment is decoded first because these are `href`s and not ids. The
91
+ * lookup is the tree's alone: this intercepts no press, so it has no
92
+ * navigation to agree with, and a section it cannot see is one it is not
93
+ * describing.
94
+ */
88
95
  private get sections();
89
96
  /**
90
97
  * Works out which section is being read, and marks its link.
@@ -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, HelixActivateEvent, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
7
+ import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
@@ -82,10 +82,9 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
82
82
  element.getAttribute('href')?.startsWith('#') === true);
83
83
  }
84
84
  get sections() {
85
- const root = this.getRootNode();
86
85
  return this.links.flatMap((link) => {
87
86
  const id = decodeURIComponent(link.getAttribute('href')?.slice(1) ?? '');
88
- const target = id ? root.getElementById?.(id) : null;
87
+ const target = elementById(this, id);
89
88
  return target && target.getClientRects().length > 0 ? [{ link, target }] : [];
90
89
  });
91
90
  }
@@ -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, extent, formatNumbers, HelixElement, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, ticks, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, isRtl, linear, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, 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';
@@ -190,7 +190,7 @@ let HelixHeadlessScatterChart = class HelixHeadlessScatterChart extends MixinCha
190
190
  return drawTooltip(this.sourceRows, -1, this.tag('tooltip'));
191
191
  }
192
192
  return drawTooltip(this.sourceRows, at, this.tag('tooltip'), {
193
- dir: getComputedStyle(this).direction === 'rtl' ? 'rtl' : 'ltr',
193
+ dir: isRtl(this) ? 'rtl' : 'ltr',
194
194
  fixed: true,
195
195
  placement: 'top',
196
196
  x: `${point.x}%`,
@@ -60,7 +60,17 @@ export declare class HelixHeadlessSkipLink extends HelixElement {
60
60
  * appearing beside it.
61
61
  */
62
62
  label: string;
63
- /** The element `href` names, looked up in the tree this link sits in. */
63
+ /**
64
+ * The element `href` names, looked up the way the browser would.
65
+ *
66
+ * `orDocument`, which no other caller of `elementById` passes and which that
67
+ * option is written for: a press handled here has to land where following the
68
+ * anchor would, and the platform resolves a fragment against the document
69
+ * whatever tree the anchor is in. A skip link inside an app shell's shadow
70
+ * root pointing at the page's `#main` is the arrangement that needs it.
71
+ *
72
+ * The fragment is decoded first because this is an `href` and not an id.
73
+ */
64
74
  private get target();
65
75
  /**
66
76
  * Focuses the target, lending it a `tabindex` only if it needs one.
@@ -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, elementById, HelixElement } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessSkipLink = class HelixHeadlessSkipLink extends HelixElement {
@@ -38,9 +38,7 @@ let HelixHeadlessSkipLink = class HelixHeadlessSkipLink extends HelixElement {
38
38
  if (!this.href.startsWith('#') || this.href.length < 2) {
39
39
  return null;
40
40
  }
41
- const id = decodeURIComponent(this.href.slice(1));
42
- const root = this.getRootNode();
43
- return (root.getElementById?.(id) ?? document.getElementById(id));
41
+ return elementById(this, decodeURIComponent(this.href.slice(1)), { orDocument: true });
44
42
  }
45
43
  render() {
46
44
  return html `<a part="link" href=${this.href} @click=${this.skip}><slot>${this.label}</slot></a>`;
@@ -4,13 +4,10 @@ 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, extent, HelixElement, linear, linePath, PLOT_WIDTH, SourceController, writtenList, } from '@fusion.dev/helix-core';
7
+ import { customElement, extent, HelixElement, linear, linePath, PLOT_WIDTH, SourceController, writtenNumbers, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  const STRIP_HEIGHT = 32;
11
- const readValues = (input) => writtenList(input)
12
- .map(Number)
13
- .filter((one) => Number.isFinite(one));
14
11
  let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
15
12
  constructor() {
16
13
  super(...arguments);
@@ -28,7 +25,7 @@ let HelixHeadlessSparkline = class HelixHeadlessSparkline extends HelixElement {
28
25
  const series = this.source.series;
29
26
  return series.length > 0
30
27
  ? (series[0]?.points.map((point) => point.value) ?? [])
31
- : readValues(this.values);
28
+ : writtenNumbers(this.values);
32
29
  }
33
30
  render() {
34
31
  const points = this.points();
@@ -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 HelixHeadlessSplitter_1;
8
- import { capturePointer, closestInstance, customElement, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixElement, inlineStep, requestChange, } from '@fusion.dev/helix-core';
8
+ import { capturePointer, closestInstance, customElement, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixElement, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
9
9
  import { html } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  const STACK_BELOW = 512;
@@ -51,7 +51,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
51
51
  }
52
52
  const along = this.vertical ? event.clientY : event.clientX;
53
53
  const raw = ((along - origin.start) / origin.length) * 100;
54
- const rtl = !this.vertical && getComputedStyle(this).direction === 'rtl';
54
+ const rtl = !this.vertical && isRtl(this);
55
55
  this.move(rtl ? 100 - raw : raw);
56
56
  };
57
57
  this.handlePointerUp = (event) => {
@@ -129,7 +129,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
129
129
  if (!origin) {
130
130
  return;
131
131
  }
132
- const rtl = getComputedStyle(this).direction === 'rtl';
132
+ const rtl = isRtl(this);
133
133
  const along = (box, at, inline) => {
134
134
  if (box.length === 0) {
135
135
  return undefined;
@@ -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 HelixHeadlessTableColumn_1;
8
- import { customElement, HelixBeforeResizeEvent, HelixElement, hostAria, inlineStep, requestChange, } from '@fusion.dev/helix-core';
8
+ import { customElement, HelixBeforeResizeEvent, HelixElement, hostAria, inlineStep, isRtl, requestChange, } from '@fusion.dev/helix-core';
9
9
  import { dotsThreeVertical } from '@fusion.dev/helix-icons';
10
10
  import { html, nothing } from 'lit';
11
11
  import { property, state } from 'lit/decorators.js';
@@ -160,7 +160,7 @@ let HelixHeadlessTableColumn = class HelixHeadlessTableColumn extends HelixEleme
160
160
  });
161
161
  }
162
162
  get forward() {
163
- return getComputedStyle(this).direction === 'rtl' ? -1 : 1;
163
+ return isRtl(this) ? -1 : 1;
164
164
  }
165
165
  render() {
166
166
  const name = this.name;
@@ -4,34 +4,12 @@ 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, MixinCollection } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, MixinCollection, splitValues } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { edgeOf } from '../column/table-column.js';
11
11
  const tracksOf = (list, wanted) => {
12
- const words = [];
13
- let depth = 0;
14
- let word = '';
15
- for (const char of list.trim()) {
16
- if (char === '(' || char === '[') {
17
- depth += 1;
18
- }
19
- else if (char === ')' || char === ']') {
20
- depth -= 1;
21
- }
22
- if (depth === 0 && /\s/.test(char)) {
23
- if (word) {
24
- words.push(word);
25
- }
26
- word = '';
27
- }
28
- else {
29
- word += char;
30
- }
31
- }
32
- if (word) {
33
- words.push(word);
34
- }
12
+ const words = splitValues(list, ' ');
35
13
  const valid = (track) => typeof CSS === 'undefined' || CSS.supports('grid-template-columns', track);
36
14
  const tracks = [];
37
15
  let names = '';
@@ -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 { AriaForwardController, customElement, HelixFormElement, holdCaret, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, customElement, HelixFormElement, holdCaret, isRtl, MixinControllerValue, MixinField, restoreValue, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerValue(MixinField(HelixFormElement)) {
@@ -60,7 +60,7 @@ let HelixHeadlessTextarea = class HelixHeadlessTextarea extends MixinControllerV
60
60
  }
61
61
  const data = new FormData();
62
62
  data.append(this.name, this.value);
63
- data.append(this.dirName, getComputedStyle(control).direction === 'rtl' ? 'rtl' : 'ltr');
63
+ data.append(this.dirName, isRtl(control) ? 'rtl' : 'ltr');
64
64
  return data;
65
65
  }
66
66
  get selectionDirection() {
@@ -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 { AriaForwardController, valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinTokens, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, isTimeValue, MixinControllerOpen, MixinControllerValue, MixinField, MixinPickerEditing, MixinPickerField, MixinTokens, valueAsMinutes, writeTime, } from '@fusion.dev/helix-core';
8
8
  import { clock } from '@fusion.dev/helix-icons';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -13,7 +13,6 @@ import { html } from 'lit/static-html.js';
13
13
  import '../../icon/index.js';
14
14
  import '../../tag/index.js';
15
15
  import '../../time-picker/index.js';
16
- import { isTime, toMinutes, writeTime } from '../../time-picker/root/time-picker-root.js';
17
16
  let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEditing(MixinPickerField(MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))))) {
18
17
  constructor() {
19
18
  super(...arguments);
@@ -34,7 +33,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEdi
34
33
  return;
35
34
  }
36
35
  const held = this.held;
37
- if (event.key === 'Enter' && isTime(this.draft)) {
36
+ if (event.key === 'Enter' && isTimeValue(this.draft)) {
38
37
  event.preventDefault();
39
38
  if (!held.includes(this.draft)) {
40
39
  this.requestValue([...held, this.draft].sort().join(','));
@@ -61,7 +60,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEdi
61
60
  .split(',')
62
61
  .map((one) => one.trim())
63
62
  .filter(Boolean);
64
- const whole = held.length > 0 && held.every(isTime);
63
+ const whole = held.length > 0 && held.every(isTimeValue);
65
64
  this.requestValue(whole ? held.join(',') : typed);
66
65
  this.restore();
67
66
  };
@@ -83,7 +82,7 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEdi
83
82
  .filter(Boolean);
84
83
  }
85
84
  written(at) {
86
- return this.timeStyle === 'iso' || !isTime(at) ? at : writeTime(at, this.locale);
85
+ return this.timeStyle === 'iso' || !isTimeValue(at) ? at : writeTime(at, this.locale);
87
86
  }
88
87
  get display() {
89
88
  if (this.mode === 'multiple') {
@@ -105,21 +104,21 @@ let HelixHeadlessTimeInput = class HelixHeadlessTimeInput extends MixinPickerEdi
105
104
  }
106
105
  get picked() {
107
106
  const held = this.held;
108
- return held.length > 0 && held.every(isTime) ? held.join(',') : '';
107
+ return held.length > 0 && held.every(isTimeValue) ? held.join(',') : '';
109
108
  }
110
109
  validate() {
111
110
  const held = this.held;
112
- const floor = toMinutes(this.min) ?? 0;
113
- const ceiling = toMinutes(this.max) ?? 1439;
114
- if (held.length > 0 && !held.every(isTime)) {
111
+ const floor = valueAsMinutes(this.min) ?? 0;
112
+ const ceiling = valueAsMinutes(this.max) ?? 1439;
113
+ if (held.length > 0 && !held.every(isTimeValue)) {
115
114
  return this.mode === undefined
116
115
  ? 'Enter a time as HH:mm.'
117
116
  : 'Enter times as HH:mm, separated by commas.';
118
117
  }
119
- if (held.some((one) => (toMinutes(one) ?? 0) < floor)) {
118
+ if (held.some((one) => (valueAsMinutes(one) ?? 0) < floor)) {
120
119
  return `Choose a time at or after ${writeTime(this.min, this.locale)}.`;
121
120
  }
122
- if (held.some((one) => (toMinutes(one) ?? 0) > ceiling)) {
121
+ if (held.some((one) => (valueAsMinutes(one) ?? 0) > ceiling)) {
123
122
  return `Choose a time at or before ${writeTime(this.max, this.locale)}.`;
124
123
  }
125
124
  if (this.mode === 'range' && held.length > 2) {
@@ -2,23 +2,6 @@ import { HelixFormElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
3
  import '../../listbox/index.js';
4
4
  import '../../option/index.js';
5
- /** Whether a string is a time the rest of this component can reason about. */
6
- export declare const isTime: (value: string) => boolean;
7
- /** Minutes since midnight, or `null` where it is not a time. */
8
- export declare const toMinutes: (value: string) => number | null;
9
- /** Minutes since midnight back to `HH:mm`, which is the value shape throughout. */
10
- export declare const fromMinutes: (minutes: number) => string;
11
- /**
12
- * A time written the way the reader's locale writes one.
13
- *
14
- * `Intl` decides between a twelve and a twenty-four hour clock, because that is
15
- * a property of the locale rather than of this component: `en-US` gets
16
- * `2:30 PM` and `en-GB` gets `14:30` without either being configured. There is
17
- * no flag to force one, deliberately. A product that needs to override its
18
- * reader's own convention has a locale problem rather than a component one, and
19
- * the value is 24 hour regardless.
20
- */
21
- export declare const writeTime: (value: string, locale?: string) => string;
22
5
  declare const HelixHeadlessTimePicker_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & typeof HelixFormElement;
23
6
  /**
24
7
  * @summary Picks a time, from a list of them.
@@ -4,35 +4,12 @@ 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 { valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, MixinControllerValue, } from '@fusion.dev/helix-core';
7
+ import { valueAsTime as asTime, customElement, timeAsValue as fromTime, HelixFormElement, MixinControllerValue, minutesAsValue, valueAsMinutes, writeTime, } from '@fusion.dev/helix-core';
8
8
  import { nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { html } from 'lit/static-html.js';
11
11
  import '../../listbox/index.js';
12
12
  import '../../option/index.js';
13
- export const isTime = (value) => /^([01]\d|2[0-3]):[0-5]\d$/.test(value);
14
- export const toMinutes = (value) => {
15
- if (!isTime(value)) {
16
- return null;
17
- }
18
- const [hours = '0', minutes = '0'] = value.split(':');
19
- return Number(hours) * 60 + Number(minutes);
20
- };
21
- export const fromMinutes = (minutes) => {
22
- const whole = ((Math.round(minutes) % 1440) + 1440) % 1440;
23
- return `${String(Math.floor(whole / 60)).padStart(2, '0')}:${String(whole % 60).padStart(2, '0')}`;
24
- };
25
- export const writeTime = (value, locale) => {
26
- const minutes = toMinutes(value);
27
- if (minutes === null) {
28
- return value;
29
- }
30
- const at = new Date(Date.UTC(2000, 0, 1, Math.floor(minutes / 60), minutes % 60));
31
- return new Intl.DateTimeFormat(locale || undefined, {
32
- timeStyle: 'short',
33
- timeZone: 'UTC',
34
- }).format(at);
35
- };
36
13
  const isTwelveHour = (locale) => new Intl.DateTimeFormat(locale || undefined, { timeStyle: 'short' }).resolvedOptions().hour12 ===
37
14
  true;
38
15
  let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControllerValue(HelixFormElement) {
@@ -92,7 +69,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
92
69
  const half = hour % 12;
93
70
  next = (half + (to === 'PM' ? 12 : 0)) * 60 + minute;
94
71
  }
95
- const whole = fromMinutes(next);
72
+ const whole = minutesAsValue(next);
96
73
  if (this.barred(whole)) {
97
74
  return;
98
75
  }
@@ -102,8 +79,8 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
102
79
  this.listRevealed = false;
103
80
  }
104
81
  get times() {
105
- const from = toMinutes(this.min) ?? 0;
106
- const to = toMinutes(this.max) ?? 1439;
82
+ const from = valueAsMinutes(this.min) ?? 0;
83
+ const to = valueAsMinutes(this.max) ?? 1439;
107
84
  const step = Math.max(1, Math.round(this.step));
108
85
  const found = [];
109
86
  for (let at = from; at <= to; at += step) {
@@ -121,11 +98,11 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
121
98
  wholeHourBarred(shown, half) {
122
99
  const whole = half === undefined ? shown : (shown % 12) + (half === 'PM' ? 12 : 0);
123
100
  const within = this.times.filter((at) => Math.floor(at / 60) === whole);
124
- return within.length > 0 && within.every((at) => this.barred(fromMinutes(at)));
101
+ return within.length > 0 && within.every((at) => this.barred(minutesAsValue(at)));
125
102
  }
126
103
  halfBarred(half) {
127
104
  const within = this.times.filter((at) => (half === 'PM') === at >= 720);
128
- return within.length > 0 && within.every((at) => this.barred(fromMinutes(at)));
105
+ return within.length > 0 && within.every((at) => this.barred(minutesAsValue(at)));
129
106
  }
130
107
  get held() {
131
108
  return this.value
@@ -141,7 +118,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
141
118
  return from !== undefined && to !== undefined && at > from && at < to;
142
119
  }
143
120
  get parts() {
144
- const at = toMinutes(this.value) ?? toMinutes(this.min) ?? 0;
121
+ const at = valueAsMinutes(this.value) ?? valueAsMinutes(this.min) ?? 0;
145
122
  const hour = Math.floor(at / 60);
146
123
  return { hour, meridiem: hour < 12 ? 'AM' : 'PM', minute: at % 60 };
147
124
  }
@@ -262,7 +239,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
262
239
  : this.hours;
263
240
  return html `<div part="columns" data-unset=${this.value === '' ? '' : nothing}>
264
241
  ${this.renderColumn('hour', 'Hour', hours.map(String), (one) => (twelve ? one : one.padStart(2, '0')), shownHour, (one) => this.wholeHourBarred(Number(one), twelve ? meridiem : undefined))}
265
- ${this.renderColumn('minute', 'Minute', this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.barred(fromMinutes(hour * 60 + Number(one))))}
242
+ ${this.renderColumn('minute', 'Minute', this.minutes.map(String), (one) => one.padStart(2, '0'), String(minute), (one) => this.barred(minutesAsValue(hour * 60 + Number(one))))}
266
243
  ${twelve
267
244
  ? this.renderColumn('meridiem', 'Before or after noon', ['AM', 'PM'], (one) => one, meridiem, (one) => this.halfBarred(one))
268
245
  : nothing}
@@ -283,7 +260,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinControl
283
260
  @input=${this.handleListInput}
284
261
  >
285
262
  ${this.times.map((minutes) => {
286
- const at = fromMinutes(minutes);
263
+ const at = minutesAsValue(minutes);
287
264
  const between = this.between(at);
288
265
  return html `<${this.tag('option')}
289
266
  part="option${between ? ' option-between' : ''}"
@@ -100,8 +100,8 @@ export declare class HelixHeadlessTooltip extends HelixHeadlessTooltip_base {
100
100
  * the element it names, and points its bubble there. Leave it unset and the
101
101
  * default slot is the trigger, which is the older form.
102
102
  *
103
- * Resolved through `getRootNode()`, so a tooltip inside another component's
104
- * shadow root names an element in that root rather than in the document.
103
+ * Names an element in the same tree, so a tooltip inside another component's
104
+ * shadow root points at one in that root rather than one in the document.
105
105
  *
106
106
  * **In React this arrives as a property and not as an attribute**, which is
107
107
  * worth knowing before someone reads the DOM and thinks it did not take.
@@ -199,14 +199,7 @@ export declare class HelixHeadlessTooltip extends HelixHeadlessTooltip_base {
199
199
  * until a chart did.
200
200
  */
201
201
  reposition(): void;
202
- /**
203
- * The element `for` names, or null.
204
- *
205
- * `getRootNode()` rather than `document`, which is what makes this work inside
206
- * another component: an id is unique to a tree, and a tooltip in a shadow root
207
- * names an element in that root. Client only, since the server has no tree to
208
- * search.
209
- */
202
+ /** The element `for` names, or null. */
210
203
  private get named();
211
204
  private readonly bubbleLayer;
212
205
  /**