@fusion.dev/helix-headless 0.31.0 → 0.33.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 (62) hide show
  1. package/custom-elements.json +880 -177
  2. package/dist/area-chart/root/area-chart-root.d.ts +8 -0
  3. package/dist/area-chart/root/area-chart-root.js +1 -0
  4. package/dist/bar-chart/root/bar-chart-root.d.ts +8 -0
  5. package/dist/bar-chart/root/bar-chart-root.js +2 -1
  6. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +1 -1
  7. package/dist/carousel/root/carousel-root.d.ts +12 -0
  8. package/dist/carousel/root/carousel-root.js +17 -11
  9. package/dist/chat/composer/chat-composer.d.ts +4 -0
  10. package/dist/chat/root/chat-root.d.ts +17 -0
  11. package/dist/chat/root/chat-root.js +12 -1
  12. package/dist/code-block/root/code-block-root.d.ts +8 -0
  13. package/dist/code-block/root/code-block-root.js +4 -1
  14. package/dist/color-picker/root/color-picker-root.js +2 -1
  15. package/dist/copy-button/root/copy-button-root.d.ts +8 -0
  16. package/dist/copy-button/root/copy-button-root.js +4 -1
  17. package/dist/data-table/root/data-table-root.d.ts +22 -3
  18. package/dist/data-table/root/data-table-root.js +25 -16
  19. package/dist/date-input/root/date-input-root.d.ts +34 -1
  20. package/dist/date-input/root/date-input-root.js +27 -1
  21. package/dist/date-picker/root/date-picker-root.js +6 -8
  22. package/dist/donut-chart/root/donut-chart-root.d.ts +21 -5
  23. package/dist/donut-chart/root/donut-chart-root.js +18 -6
  24. package/dist/drilldown/item/drilldown-item.d.ts +36 -1
  25. package/dist/drilldown/item/drilldown-item.js +30 -3
  26. package/dist/drilldown/root/drilldown-root.js +5 -4
  27. package/dist/heatmap/root/heatmap-root.d.ts +11 -2
  28. package/dist/icon/root/icon-root.d.ts +8 -0
  29. package/dist/icon/root/icon-root.js +5 -0
  30. package/dist/line-chart/root/line-chart-root.d.ts +9 -1
  31. package/dist/line-chart/root/line-chart-root.js +1 -0
  32. package/dist/menu/item/menu-item.js +1 -1
  33. package/dist/menu/root/menu-root.js +5 -4
  34. package/dist/object-inspector/root/object-inspector-root.d.ts +8 -0
  35. package/dist/object-inspector/root/object-inspector-root.js +20 -15
  36. package/dist/pagination/root/pagination-root.js +1 -1
  37. package/dist/pin-input/root/pin-input-root.d.ts +7 -1
  38. package/dist/pin-input/root/pin-input-root.js +5 -7
  39. package/dist/scatter-chart/root/scatter-chart-root.d.ts +20 -0
  40. package/dist/scatter-chart/root/scatter-chart-root.js +15 -9
  41. package/dist/scroll-area/root/scroll-area-root.js +3 -2
  42. package/dist/sparkline/root/sparkline-root.js +1 -0
  43. package/dist/splitter/root/splitter-root.d.ts +11 -0
  44. package/dist/splitter/root/splitter-root.js +16 -16
  45. package/dist/table/cell/table-cell.d.ts +2 -0
  46. package/dist/table/column/table-column.d.ts +50 -5
  47. package/dist/table/column/table-column.js +24 -5
  48. package/dist/table/root/table-root.d.ts +76 -11
  49. package/dist/table/root/table-root.js +90 -3
  50. package/dist/table/row/table-row.d.ts +14 -0
  51. package/dist/table/row/table-row.js +16 -0
  52. package/dist/toolbar/root/toolbar-root.js +6 -5
  53. package/dist/tooltip/root/tooltip-root.d.ts +20 -0
  54. package/dist/tooltip/root/tooltip-root.js +21 -3
  55. package/dist/tour/root/tour-root.js +1 -1
  56. package/dist/tour/stop/tour-stop.d.ts +15 -0
  57. package/dist/tour/stop/tour-stop.js +16 -2
  58. package/dist/tree/root/tree-root.js +4 -3
  59. package/dist/video/root/video-root.js +7 -0
  60. package/package.json +5 -5
  61. package/types/jsx.d.ts +1556 -582
  62. package/types/vue.d.ts +65 -22
@@ -33,9 +33,17 @@ declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args:
33
33
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
34
34
  * @csspart tooltip-anchor - The `Tooltip` element itself.
35
35
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
36
+ * @csspart tooltip-arrow-bottom - The point while the bubble is below the reading, so on its top edge, beside `tooltip-arrow`.
37
+ * @csspart tooltip-arrow-left - The point while the bubble is left of the reading, so on its right edge, beside `tooltip-arrow`.
38
+ * @csspart tooltip-arrow-right - The point while the bubble is right of the reading, so on its left edge, beside `tooltip-arrow`.
39
+ * @csspart tooltip-arrow-top - The point while the bubble is above the reading, so on its bottom edge, beside `tooltip-arrow`.
40
+ * @csspart tooltip-bottom - The bubble while it is below the reading, beside `tooltip`.
36
41
  * @csspart tooltip-cell - One column's name and value inside the tooltip.
37
42
  * @csspart tooltip-label - The slice's own name inside the tooltip.
43
+ * @csspart tooltip-left - The bubble while it is left of the reading, beside `tooltip`.
38
44
  * @csspart tooltip-name - A column's name inside the tooltip.
45
+ * @csspart tooltip-right - The bubble while it is right of the reading, beside `tooltip`.
46
+ * @csspart tooltip-top - The bubble while it is above the reading, beside `tooltip`.
39
47
  * @csspart tooltip-value - A column's value inside the tooltip.
40
48
  * @csspart track - The ring the slices are drawn on.
41
49
  *
@@ -45,7 +53,7 @@ export declare class HelixHeadlessDonutChart extends HelixHeadlessDonutChart_bas
45
53
  /**
46
54
  * The pointer is not mapped across this one.
47
55
  *
48
- * Every other chart lays its readings out left to right, so the nearest is
56
+ * Every other chart lays its readings out along the line, so the nearest is
49
57
  * arithmetic on x. A ring's slices are real elements at every angle, and x
50
58
  * alone would answer with whichever slice shared a horizontal position with
51
59
  * the pointer: at three o'clock and at nine o'clock it would give the same
@@ -104,6 +112,14 @@ export declare class HelixHeadlessDonutChart extends HelixHeadlessDonutChart_bas
104
112
  sweep?: 'half';
105
113
  /** The fraction of the circle the shares are spread over. */
106
114
  private get span();
115
+ /**
116
+ * Where the first slice starts, as an angle from three o'clock.
117
+ *
118
+ * Twelve for a whole ring and nine for a half, the turns the stylesheet gives
119
+ * the circles. Anything drawn or placed by arithmetic has to start from the
120
+ * same place, or it describes a slice the ring is not showing there.
121
+ */
122
+ private get origin();
107
123
  /** How thick the ring is drawn, in the drawing's own units. */
108
124
  private get thickness();
109
125
  /** The ring's radius, which follows its thickness so the outer edge holds. */
@@ -161,10 +177,10 @@ export declare class HelixHeadlessDonutChart extends HelixHeadlessDonutChart_bas
161
177
  * turn, so a growing slice would pop partway through the transition it was
162
178
  * meant to smooth. Both animations stay on the dash and this path follows it.
163
179
  *
164
- * The turn to twelve o'clock is arithmetic here rather than the rotation the
165
- * stylesheet gives the circles. That rule uses `transform-box: fill-box`, so
166
- * it spins each element about its own bounding box, which is the ring's center
167
- * for a circle and is not for a sector.
180
+ * The turn to twelve o'clock, or to nine for a half, is `origin` here rather
181
+ * than the rotation the stylesheet gives the circles. That rule uses
182
+ * `transform-box: fill-box`, so it spins each element about its own bounding
183
+ * box, which is the ring's center for a circle and is not for a sector.
168
184
  */
169
185
  protected renderEdge(share: {
170
186
  fraction: number;
@@ -26,6 +26,9 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
26
26
  get span() {
27
27
  return this.sweep === 'half' ? 0.5 : 1;
28
28
  }
29
+ get origin() {
30
+ return this.sweep === 'half' ? Math.PI : -Math.PI / 2;
31
+ }
29
32
  get thickness() {
30
33
  return THICKNESS[this.hole ?? 'medium'] ?? THICKNESS.medium ?? 0;
31
34
  }
@@ -61,7 +64,11 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
61
64
  part="plot"
62
65
  style="--donut-chart-stroke: ${this.thickness}"
63
66
  >
64
- <svg viewBox=${this.frame} xmlns="http://www.w3.org/2000/svg">
67
+ <svg
68
+ data-mirror=${this.sweep === 'half' ? '' : nothing}
69
+ viewBox=${this.frame}
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ >
65
72
  <circle
66
73
  cx=${CENTER}
67
74
  cy=${CENTER}
@@ -104,8 +111,8 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
104
111
  }
105
112
  renderEdge(share, index) {
106
113
  const turn = 2 * Math.PI * this.span;
107
- const from = share.start * turn - Math.PI / 2;
108
- const to = (share.start + share.fraction) * turn - Math.PI / 2;
114
+ const from = share.start * turn + this.origin;
115
+ const to = (share.start + share.fraction) * turn + this.origin;
109
116
  const outer = EDGE;
110
117
  const inner = EDGE - this.thickness;
111
118
  const at = (radius, angle) => `${(CENTER + Math.cos(angle) * radius).toFixed(3)} ${(CENTER + Math.sin(angle) * radius).toFixed(3)}`;
@@ -139,13 +146,18 @@ let HelixHeadlessDonutChart = class HelixHeadlessDonutChart extends MixinChartSo
139
146
  if (!share) {
140
147
  return drawTooltip(this.sourceRows, at, this.tag('tooltip'));
141
148
  }
142
- const angle = (share.start + share.fraction / 2) * 2 * Math.PI - Math.PI / 2;
149
+ const angle = (share.start + share.fraction / 2) * 2 * Math.PI * this.span + this.origin;
143
150
  const edge = EDGE;
144
151
  const x = CENTER + Math.cos(angle) * edge;
145
- const y = CENTER + Math.sin(angle) * edge;
152
+ const top = this.sweep === 'half' ? CENTER - EDGE : 0;
153
+ const tall = this.sweep === 'half' ? EDGE + this.thickness / 2 : BOX;
154
+ const y = ((CENTER + Math.sin(angle) * edge - top) / tall) * 100;
155
+ const turned = this.sweep === 'half' && this.pointer.mirrored;
156
+ const across = Math.cos(angle) > 0 !== turned;
146
157
  return drawTooltip(this.sourceRows, at, this.tag('tooltip'), {
158
+ fixed: this.sweep !== 'half',
147
159
  placement: Math.abs(Math.cos(angle)) > Math.abs(Math.sin(angle))
148
- ? Math.cos(angle) > 0
160
+ ? across
149
161
  ? 'right'
150
162
  : 'left'
151
163
  : Math.sin(angle) > 0
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { nothing, type TemplateResult } from 'lit';
2
+ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary One row of a drill-down, which may hold a page of its own.
5
5
  *
@@ -18,6 +18,11 @@ import { nothing, type TemplateResult } from 'lit';
18
18
  * Choosing is announced, never applied. The row dispatches and the stack
19
19
  * decides whether that means drilling in or choosing a command.
20
20
  *
21
+ * **A row that opens a page says so**, with a caret after its words and
22
+ * `aria-haspopup`, the way `Menu` marks a row with a submenu: a row that goes
23
+ * somewhere and a row that does something otherwise look the same until one is
24
+ * pressed. Not on the server, which cannot see the children and so cannot know.
25
+ *
21
26
  * @since 0.1.0
22
27
  * @status experimental
23
28
  * @aliases Panel item, Drill row
@@ -32,6 +37,7 @@ import { nothing, type TemplateResult } from 'lit';
32
37
  *
33
38
  * @csspart item - The row itself.
34
39
  * @csspart label - The words.
40
+ * @csspart mark - The caret at the end of a row that opens a page, after the `end` slot. A caret points, so a sheet mirrors it in a right-to-left document.
35
41
  */
36
42
  export declare class HelixHeadlessDrilldownItem extends HelixElement {
37
43
  /**
@@ -74,6 +80,35 @@ export declare class HelixHeadlessDrilldownItem extends HelixElement {
74
80
  * Identifies the row to whatever is listening.
75
81
  */
76
82
  value: string;
83
+ /**
84
+ * Whether anything is slotted into `page`.
85
+ *
86
+ * State rather than a getter over `children`, for the reason `Menu.Item`
87
+ * backs `hasSubmenu` the same way: a getter is read during `render()` with
88
+ * nothing to re-read it, so a page a framework fills after the row's first
89
+ * render would open with no caret on the row that leads to it.
90
+ */
91
+ private page;
92
+ /**
93
+ * Reads the children before each render, where they can be known.
94
+ *
95
+ * Not on the server, where there are none to read, nor while hydrating what
96
+ * one sent, whose row carried no caret. `finishHydration` asks for the read
97
+ * that skipped.
98
+ */
99
+ protected willUpdate(changed: PropertyValues): void;
100
+ protected firstUpdated(changed: PropertyValues): void;
101
+ /**
102
+ * Re-reads whether a page is slotted, from whichever `page` slot is current.
103
+ *
104
+ * Bound in the template, on both of them. The row and the page it opens each
105
+ * render a `slot[name="page"]`, and moving between them makes Lit build a new
106
+ * slot and throw the old one away, which fires on its way out with nothing
107
+ * assigned: a listener on the discarded one would clear the caret on every row
108
+ * that has been drilled into and back out of. `Menu.Item.readSubmenu` carries
109
+ * the same note.
110
+ */
111
+ private readonly readPage;
77
112
  /** Moves focus onto the row itself. */
78
113
  focusItem(): void;
79
114
  /** Announces that this row was chosen. */
@@ -5,8 +5,10 @@ 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
  import { customElement, HelixElement, HelixSelectEvent } from '@fusion.dev/helix-core';
8
+ import { caretRight } from '@fusion.dev/helix-icons';
8
9
  import { html, nothing } from 'lit';
9
- import { property } from 'lit/decorators.js';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
10
12
  let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixElement {
11
13
  constructor() {
12
14
  super(...arguments);
@@ -16,6 +18,21 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
16
18
  this.onPage = true;
17
19
  this.showingPage = false;
18
20
  this.value = '';
21
+ this.page = false;
22
+ this.readPage = () => {
23
+ const slot = this.renderRoot.querySelector('slot[name="page"]');
24
+ this.page = (slot?.assignedElements().length ?? 0) > 0;
25
+ };
26
+ }
27
+ willUpdate(changed) {
28
+ super.willUpdate(changed);
29
+ if (this.childrenKnown) {
30
+ this.page = this.knownChildren.some((child) => child.slot === 'page');
31
+ }
32
+ }
33
+ firstUpdated(changed) {
34
+ super.firstUpdated(changed);
35
+ this.finishHydration();
19
36
  }
20
37
  focusItem() {
21
38
  this.partNamed('item')?.focus();
@@ -28,7 +45,7 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
28
45
  }
29
46
  render() {
30
47
  if (this.showingPage) {
31
- return html `<slot name="page"></slot>`;
48
+ return html `<slot name="page" @slotchange=${this.readPage}></slot>`;
32
49
  }
33
50
  if (!this.onPage) {
34
51
  return nothing;
@@ -38,13 +55,20 @@ let HelixHeadlessDrilldownItem = class HelixHeadlessDrilldownItem extends HelixE
38
55
  type="button"
39
56
  role="menuitem"
40
57
  aria-disabled=${this.disabled ? 'true' : nothing}
58
+ aria-haspopup=${this.page ? 'menu' : nothing}
41
59
  tabindex=${this.focusable ? '0' : '-1'}
42
60
  @click=${() => this.select()}
43
61
  >
44
62
  <slot name="start"></slot>
45
63
  <span part="label"><slot></slot></span>
46
64
  <slot name="end"></slot>
47
- </button>`;
65
+ ${this.page
66
+ ? html `<svg part="mark" data-mirror fill="currentColor" viewBox=${caretRight.viewBox} aria-hidden="true">
67
+ ${unsafeSVG(caretRight.content)}
68
+ </svg>`
69
+ : nothing}
70
+ </button>
71
+ <div hidden><slot name="page" @slotchange=${this.readPage}></slot></div>`;
48
72
  }
49
73
  };
50
74
  __decorate([
@@ -62,6 +86,9 @@ __decorate([
62
86
  __decorate([
63
87
  property()
64
88
  ], HelixHeadlessDrilldownItem.prototype, "value", void 0);
89
+ __decorate([
90
+ state()
91
+ ], HelixHeadlessDrilldownItem.prototype, "page", void 0);
65
92
  HelixHeadlessDrilldownItem = __decorate([
66
93
  customElement('helix-headless-drilldown-item')
67
94
  ], HelixHeadlessDrilldownItem);
@@ -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, DrilldownController, HelixElement, hostAria, MixinCollection, } from '@fusion.dev/helix-core';
7
+ import { customElement, DrilldownController, HelixElement, hostAria, inlineStep, MixinCollection, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -53,14 +53,15 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
53
53
  (event.key === 'Home' ? items[0] : items[items.length - 1])?.focusItem();
54
54
  return;
55
55
  }
56
- if (event.key === 'ArrowLeft' || event.key === 'Escape') {
56
+ const along = inlineStep(this, event.key);
57
+ if (along === -1 || event.key === 'Escape') {
57
58
  if (this.back()) {
58
59
  event.preventDefault();
59
60
  event.stopPropagation();
60
61
  }
61
62
  return;
62
63
  }
63
- if (event.key === 'ArrowRight' && current !== -1) {
64
+ if (along === 1 && current !== -1) {
64
65
  const item = items[current];
65
66
  if (item && this.drilldown.hasPage(item)) {
66
67
  event.preventDefault();
@@ -144,7 +145,7 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
144
145
  ? ''
145
146
  : html `<div part="header">
146
147
  <button part="back" type="button" aria-label=${this.backLabel} @click=${() => this.back()}>
147
- <svg part="back-mark" fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
148
+ <svg part="back-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
148
149
  ${unsafeSVG(caretLeft.content)}
149
150
  </svg>
150
151
  </button>
@@ -50,9 +50,17 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
50
50
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
51
51
  * @csspart tooltip-anchor - The `Tooltip` element itself.
52
52
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
53
+ * @csspart tooltip-arrow-bottom - The point while the bubble is below the reading, so on its top edge, beside `tooltip-arrow`.
54
+ * @csspart tooltip-arrow-left - The point while the bubble is left of the reading, so on its right edge, beside `tooltip-arrow`.
55
+ * @csspart tooltip-arrow-right - The point while the bubble is right of the reading, so on its left edge, beside `tooltip-arrow`.
56
+ * @csspart tooltip-arrow-top - The point while the bubble is above the reading, so on its bottom edge, beside `tooltip-arrow`.
57
+ * @csspart tooltip-bottom - The bubble while it is below the reading, beside `tooltip`.
53
58
  * @csspart tooltip-cell - One column's name and value inside the tooltip.
54
59
  * @csspart tooltip-label - The reading's own name inside the tooltip.
60
+ * @csspart tooltip-left - The bubble while it is left of the reading, beside `tooltip`.
55
61
  * @csspart tooltip-name - A column's name inside the tooltip.
62
+ * @csspart tooltip-right - The bubble while it is right of the reading, beside `tooltip`.
63
+ * @csspart tooltip-top - The bubble while it is above the reading, beside `tooltip`.
56
64
  * @csspart tooltip-value - A column's value inside the tooltip.
57
65
  *
58
66
  * @cssproperty --heatmap-cell-height - How tall a cell is. A row of a tall thin cell is what a status page draws.
@@ -110,8 +118,9 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
110
118
  *
111
119
  * `1` is a status strip and `7` is a calendar week, which are the two readings
112
120
  * this covers. Cells fill down the column and then across, which is what a
113
- * calendar heatmap does: a week is a column, so the strip reads left to right
114
- * in weeks and top to bottom in days.
121
+ * calendar heatmap does: a week is a column, so the strip reads along the
122
+ * line in weeks, from the right on a right-to-left page, and top to bottom in
123
+ * days.
115
124
  */
116
125
  rows: number;
117
126
  /**
@@ -40,6 +40,14 @@ export declare class HelixHeadlessIcon extends HelixElement {
40
40
  * Accessible name, for an icon that is not decorative.
41
41
  */
42
42
  label: string;
43
+ /**
44
+ * Turns the glyph round on a right-to-left page, for an arrow meaning forward or back.
45
+ *
46
+ * Asked for rather than inferred from the name. The names are physical, and
47
+ * `arrow-left` drawn pointing right on an Arabic page would be the icon
48
+ * disagreeing with its own name for every reader who meant the left.
49
+ */
50
+ mirror: boolean;
43
51
  /**
44
52
  * Name of the icon in the Helix set.
45
53
  */
@@ -13,6 +13,7 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
13
13
  constructor() {
14
14
  super(...arguments);
15
15
  this.label = '';
16
+ this.mirror = false;
16
17
  this.name = '';
17
18
  }
18
19
  render() {
@@ -23,6 +24,7 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
23
24
  const labeled = this.label.length > 0;
24
25
  return html `<svg
25
26
  part="svg"
27
+ ?data-mirror=${this.mirror}
26
28
  fill="currentColor"
27
29
  viewBox=${icon.viewBox}
28
30
  role=${labeled ? 'img' : nothing}
@@ -36,6 +38,9 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
36
38
  __decorate([
37
39
  property()
38
40
  ], HelixHeadlessIcon.prototype, "label", void 0);
41
+ __decorate([
42
+ property({ type: Boolean })
43
+ ], HelixHeadlessIcon.prototype, "mirror", void 0);
39
44
  __decorate([
40
45
  property()
41
46
  ], HelixHeadlessIcon.prototype, "name", void 0);
@@ -56,9 +56,17 @@ declare const HelixHeadlessLineChart_base: typeof HelixElement & (new (...args:
56
56
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
57
57
  * @csspart tooltip-anchor - The `Tooltip` element itself.
58
58
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
59
+ * @csspart tooltip-arrow-bottom - The point while the bubble is below the reading, so on its top edge, beside `tooltip-arrow`.
60
+ * @csspart tooltip-arrow-left - The point while the bubble is left of the reading, so on its right edge, beside `tooltip-arrow`.
61
+ * @csspart tooltip-arrow-right - The point while the bubble is right of the reading, so on its left edge, beside `tooltip-arrow`.
62
+ * @csspart tooltip-arrow-top - The point while the bubble is above the reading, so on its bottom edge, beside `tooltip-arrow`.
63
+ * @csspart tooltip-bottom - The bubble while it is below the reading, beside `tooltip`.
59
64
  * @csspart tooltip-cell - One column's name and value inside the tooltip.
60
65
  * @csspart tooltip-label - The reading's own name inside the tooltip.
66
+ * @csspart tooltip-left - The bubble while it is left of the reading, beside `tooltip`.
61
67
  * @csspart tooltip-name - A column's name inside the tooltip.
68
+ * @csspart tooltip-right - The bubble while it is right of the reading, beside `tooltip`.
69
+ * @csspart tooltip-top - The bubble while it is above the reading, beside `tooltip`.
62
70
  * @csspart tooltip-value - A column's value inside the tooltip.
63
71
  */
64
72
  export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base {
@@ -127,7 +135,7 @@ export declare class HelixHeadlessLineChart extends HelixHeadlessLineChart_base
127
135
  * property with a default writes its attribute onto a host the consumer
128
136
  * rendered, which React reports as a hydration mismatch.
129
137
  *
130
- * **One per series, in a column at the right edge**, because every series here
138
+ * **One per series, in a column at the far edge**, because every series here
131
139
  * shares an x-axis and therefore ends at the same reading. That column is the
132
140
  * useful thing: it is where a reader looks for "and what is it now", which is
133
141
  * the question a sparkline's single mark answers too.
@@ -69,6 +69,7 @@ let HelixHeadlessLineChart = class HelixHeadlessLineChart extends MixinChartSour
69
69
  style=${holes ? `--_line-chart-holes: ${holes}` : nothing}
70
70
  >
71
71
  <svg part="svg"
72
+ data-mirror
72
73
  preserveAspectRatio="none"
73
74
  viewBox="0 0 ${WIDTH} ${HEIGHT}"
74
75
  xmlns="http://www.w3.org/2000/svg"
@@ -106,7 +106,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
106
106
  ${submenu
107
107
  ? html `<svg
108
108
  part="mark"
109
- data-submenu
109
+ data-mirror
110
110
  fill="currentColor"
111
111
  viewBox=${caretRight.viewBox}
112
112
  aria-hidden="true"
@@ -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, DrilldownController, HelixElement, KeyboardNavigationController, MixinCollection, MixinControllerOpen, PopoverController, scrollItemIntoView, tellDisclosure, } from '@fusion.dev/helix-core';
7
+ import { anchorElement, customElement, DrilldownController, HelixElement, inlineStep, KeyboardNavigationController, MixinCollection, MixinControllerOpen, PopoverController, scrollItemIntoView, tellDisclosure, } from '@fusion.dev/helix-core';
8
8
  import { caretLeft } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
@@ -63,14 +63,15 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
63
63
  this.handleKeydown = (event) => {
64
64
  const items = this.items;
65
65
  const at = items.indexOf(document.activeElement);
66
+ const along = inlineStep(this, event.key);
66
67
  if (event.key === 'Escape') {
67
68
  event.stopPropagation();
68
69
  this.leave();
69
70
  }
70
- else if (event.key === 'ArrowRight' && items[at]?.hasSubmenu) {
71
+ else if (along === 1 && items[at]?.hasSubmenu) {
71
72
  this.enter(items[at]);
72
73
  }
73
- else if (event.key === 'ArrowLeft') {
74
+ else if (along === -1) {
74
75
  this.leave();
75
76
  }
76
77
  else if (event.key === 'Tab') {
@@ -195,7 +196,7 @@ let HelixHeadlessMenu = class HelixHeadlessMenu extends MixinCollection(MixinCon
195
196
  aria-label=${this.backLabel}
196
197
  @click=${() => this.leave()}
197
198
  >
198
- <svg part="back-mark" fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
199
+ <svg part="back-mark" data-mirror fill="currentColor" viewBox=${caretLeft.viewBox} aria-hidden="true">
199
200
  ${unsafeSVG(caretLeft.content)}
200
201
  </svg>
201
202
  </button>
@@ -69,6 +69,14 @@ interface Row {
69
69
  * @csspart copy-mark - The clipboard glyph, and the confirmation that replaces it.
70
70
  * @csspart copy-tooltip - The bubble, forwarded from the tooltip inside.
71
71
  * @csspart copy-tooltip-arrow - The point, resting against the trigger.
72
+ * @csspart copy-tooltip-arrow-bottom - The point while the bubble is below the copy button, so on its top edge, beside `copy-tooltip-arrow`.
73
+ * @csspart copy-tooltip-arrow-left - The point while the bubble is left of the copy button, so on its right edge, beside `copy-tooltip-arrow`.
74
+ * @csspart copy-tooltip-arrow-right - The point while the bubble is right of the copy button, so on its left edge, beside `copy-tooltip-arrow`.
75
+ * @csspart copy-tooltip-arrow-top - The point while the bubble is above the copy button, so on its bottom edge, beside `copy-tooltip-arrow`.
76
+ * @csspart copy-tooltip-bottom - The bubble while it is below the copy button, beside `copy-tooltip`.
77
+ * @csspart copy-tooltip-left - The bubble while it is left of the copy button, beside `copy-tooltip`.
78
+ * @csspart copy-tooltip-right - The bubble while it is right of the copy button, beside `copy-tooltip`.
79
+ * @csspart copy-tooltip-top - The bubble while it is above the copy button, beside `copy-tooltip`.
72
80
  * @csspart count - How many entries a branch holds.
73
81
  * @csspart frame - The tree and the copy button beside it.
74
82
  * @csspart header - The bar carrying the label, where there is 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 { CopyController, customElement, HelixElement, scrollItemIntoView, VirtualizeController, } from '@fusion.dev/helix-core';
7
+ import { CopyController, customElement, HelixElement, inlineStep, scrollItemIntoView, VirtualizeController, } from '@fusion.dev/helix-core';
8
8
  import { caretDown, caretRight } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -64,15 +64,16 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
64
64
  this.move(rows.length - 1);
65
65
  break;
66
66
  case 'ArrowRight':
67
- if (row.branch && !row.open) {
68
- this.toggle(row, true);
67
+ case 'ArrowLeft':
68
+ if (inlineStep(this, event.key) === 1) {
69
+ if (row.branch && !row.open) {
70
+ this.toggle(row, true);
71
+ }
72
+ else if (row.open) {
73
+ this.move(this.active + 1);
74
+ }
69
75
  }
70
76
  else if (row.open) {
71
- this.move(this.active + 1);
72
- }
73
- break;
74
- case 'ArrowLeft':
75
- if (row.open) {
76
77
  this.toggle(row, false);
77
78
  }
78
79
  else {
@@ -238,21 +239,21 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
238
239
  const array = Array.isArray(value);
239
240
  const noun = array ? (count === 1 ? 'item' : 'items') : count === 1 ? 'key' : 'keys';
240
241
  return html `<span part="summary">${array ? '[…]' : '{…}'}</span>
241
- <span part="count">${`${count} ${noun}`}</span>`;
242
+ <span part="count" dir="auto">${`${count} ${noun}`}</span>`;
242
243
  }
243
244
  if (typeof value === 'string') {
244
- return html `<span part="string">${JSON.stringify(value)}</span>`;
245
+ return html `<span part="string" dir="ltr">${JSON.stringify(value)}</span>`;
245
246
  }
246
247
  if (typeof value === 'number') {
247
- return html `<span part="number">${String(value)}</span>`;
248
+ return html `<span part="number" dir="ltr">${String(value)}</span>`;
248
249
  }
249
250
  if (typeof value === 'boolean') {
250
- return html `<span part="boolean">${String(value)}</span>`;
251
+ return html `<span part="boolean" dir="ltr">${String(value)}</span>`;
251
252
  }
252
253
  if (value === null) {
253
- return html `<span part="null">null</span>`;
254
+ return html `<span part="null" dir="ltr">null</span>`;
254
255
  }
255
- return html `<span part="string">${String(value)}</span>`;
256
+ return html `<span part="string" dir="ltr">${String(value)}</span>`;
256
257
  }
257
258
  renderRow(row, index) {
258
259
  const active = index === this.active;
@@ -272,6 +273,7 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
272
273
  ${row.branch
273
274
  ? html `<svg
274
275
  part="toggle"
276
+ ?data-mirror=${!row.open}
275
277
  fill="currentColor"
276
278
  viewBox=${(row.open ? caretDown : caretRight).viewBox}
277
279
  aria-hidden="true"
@@ -285,7 +287,10 @@ let HelixHeadlessObjectInspector = class HelixHeadlessObjectInspector extends He
285
287
  return staticHtml `<${this.tag('copy-button')}
286
288
  part="copy"
287
289
  exportparts="button: copy-button, mark: copy-mark, tooltip: copy-tooltip,
288
- tooltip-arrow: copy-tooltip-arrow"
290
+ tooltip-arrow: copy-tooltip-arrow, tooltip-arrow-bottom: copy-tooltip-arrow-bottom,
291
+ tooltip-arrow-left: copy-tooltip-arrow-left, tooltip-arrow-right: copy-tooltip-arrow-right,
292
+ tooltip-arrow-top: copy-tooltip-arrow-top, tooltip-bottom: copy-tooltip-bottom,
293
+ tooltip-left: copy-tooltip-left, tooltip-right: copy-tooltip-right, tooltip-top: copy-tooltip-top"
289
294
  label=${row?.key === undefined ? 'Copy value' : `Copy ${row.key}`}
290
295
  .value=${this.copyable(row)}
291
296
  ></${this.tag('copy-button')}>`;
@@ -180,7 +180,7 @@ let HelixHeadlessPagination = class HelixHeadlessPagination extends HelixElement
180
180
  return {
181
181
  address: spent ? nothing : fill(this.href, to, this.count),
182
182
  click: (event) => this.go(to, event),
183
- glyph: html `<svg part="mark" fill="currentColor" viewBox=${icon.viewBox} aria-hidden="true">
183
+ glyph: html `<svg part="mark" data-mirror fill="currentColor" viewBox=${icon.viewBox} aria-hidden="true">
184
184
  ${unsafeSVG(icon.content)}
185
185
  </svg>`,
186
186
  name,
@@ -27,6 +27,12 @@ declare const HelixHeadlessPinInput_base: (new (...args: any[]) => import("@fusi
27
27
  * the field as a whole, and repeating it on six boxes offers a browser six
28
28
  * places to put one code.
29
29
  *
30
+ * **Left to right on every page.** A code is read the way a phone number is,
31
+ * one character after another from the left, in Arabic and Hebrew as in
32
+ * English, so the row carries `dir="ltr"`: the first box is the leftmost, a
33
+ * paste fills rightward and the right arrow moves on. The label, the hint and
34
+ * the feedback are words and keep the page's direction.
35
+ *
30
36
  * @since 0.1.0
31
37
  * @status stable
32
38
  * @category forms
@@ -40,7 +46,7 @@ declare const HelixHeadlessPinInput_base: (new (...args: any[]) => import("@fusi
40
46
  *
41
47
  * @csspart cell - One box.
42
48
  * @csspart feedback - What is wrong with it.
43
- * @csspart field - The row of boxes.
49
+ * @csspart field - The row of boxes, left to right on every page.
44
50
  * @csspart hint - Help under the boxes, while nothing is wrong.
45
51
  * @csspart label - The question, over the boxes.
46
52
  *
@@ -56,6 +56,7 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
56
56
  if (at === -1 || this.isDisabled || this.readOnly) {
57
57
  return;
58
58
  }
59
+ const to = at + (event.key === 'ArrowRight' ? 1 : event.key === 'ArrowLeft' ? -1 : 0);
59
60
  if (event.key === 'Backspace' && cell.value === '' && at > 0) {
60
61
  const characters = this.characters;
61
62
  characters[at - 1] = '';
@@ -65,13 +66,9 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
65
66
  this.cells[at - 1]?.focus();
66
67
  });
67
68
  }
68
- else if (event.key === 'ArrowLeft' && at > 0) {
69
- this.cells[at - 1]?.focus();
70
- this.cells[at - 1]?.select();
71
- }
72
- else if (event.key === 'ArrowRight' && at < this.count - 1) {
73
- this.cells[at + 1]?.focus();
74
- this.cells[at + 1]?.select();
69
+ else if (to !== at && to >= 0 && to < this.count) {
70
+ this.cells[to]?.focus();
71
+ this.cells[to]?.select();
75
72
  }
76
73
  else {
77
74
  return;
@@ -203,6 +200,7 @@ let HelixHeadlessPinInput = class HelixHeadlessPinInput extends MixinControllerV
203
200
  const characters = this.characters;
204
201
  return html `${this.renderGroupLabel()}
205
202
  <div
203
+ dir="ltr"
206
204
  part="field"
207
205
  role="group"
208
206
  aria-describedby=${this.describedBy ?? nothing}