@fusion.dev/helix 0.145.0 → 0.146.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 (153) hide show
  1. package/custom-elements.json +7841 -3357
  2. package/dist/accordion/item/accordion-item.css.js +12 -8
  3. package/dist/accordion/item/accordion-item.d.ts +8 -0
  4. package/dist/aura/root/aura-root.css.js +3 -0
  5. package/dist/aura/root/aura-root.d.ts +21 -9
  6. package/dist/aura/root/aura-root.js +23 -12
  7. package/dist/badge/root/badge-root.css.js +86 -16
  8. package/dist/badge/root/badge-root.d.ts +18 -0
  9. package/dist/badge/root/badge-root.js +6 -0
  10. package/dist/blockquote/root/blockquote-root.css.js +2 -2
  11. package/dist/blockquote/root/blockquote-root.d.ts +2 -0
  12. package/dist/breadcrumbs/root/breadcrumbs-root.css.js +23 -0
  13. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +1 -0
  14. package/dist/button/root/button-root.css.js +4 -3
  15. package/dist/button/root/button-root.d.ts +2 -0
  16. package/dist/callout/root/callout-root.css.js +4 -3
  17. package/dist/callout/root/callout-root.d.ts +6 -2
  18. package/dist/card/root/card-root.d.ts +6 -0
  19. package/dist/chat/message/chat-message.css.js +0 -62
  20. package/dist/chat/message/chat-message.js +2 -1
  21. package/dist/chat/root/chat-root.css.js +7 -0
  22. package/dist/chat/root/chat-root.d.ts +2 -0
  23. package/dist/chat/root/chat-root.js +2 -1
  24. package/dist/chat/typing.css.d.ts +1 -0
  25. package/dist/chat/typing.css.js +62 -0
  26. package/dist/checkbox/root/checkbox-root.css.js +15 -0
  27. package/dist/checkbox/root/checkbox-root.d.ts +2 -1
  28. package/dist/code/root/code-root.css.js +2 -2
  29. package/dist/code/root/code-root.d.ts +2 -0
  30. package/dist/code-block/root/code-block-root.css.js +24 -0
  31. package/dist/code-block/root/code-block-root.d.ts +2 -1
  32. package/dist/color-input/root/color-input-root.css.js +10 -0
  33. package/dist/color-picker/root/color-picker-root.css.js +8 -0
  34. package/dist/color-picker/root/color-picker-root.d.ts +1 -0
  35. package/dist/copy-button/root/copy-button-root.css.js +15 -6
  36. package/dist/copy-button/root/copy-button-root.d.ts +7 -0
  37. package/dist/data-table/root/data-table-root.css.js +14 -7
  38. package/dist/data-table/root/data-table-root.d.ts +5 -0
  39. package/dist/date-input/root/date-input-root.css.js +10 -0
  40. package/dist/donut-chart/root/donut-chart-root.css.js +19 -0
  41. package/dist/donut-chart/root/donut-chart-root.d.ts +3 -0
  42. package/dist/drawer/root/drawer-root.css.js +47 -0
  43. package/dist/drawer/root/drawer-root.d.ts +5 -0
  44. package/dist/drilldown/item/drilldown-item.css.js +6 -6
  45. package/dist/drilldown/item/drilldown-item.d.ts +6 -0
  46. package/dist/drilldown/root/drilldown-root.css.js +25 -12
  47. package/dist/drilldown/root/drilldown-root.d.ts +10 -0
  48. package/dist/element-events.generated.js +15 -5
  49. package/dist/file-input/root/file-input-root.css.js +12 -11
  50. package/dist/file-input/root/file-input-root.d.ts +11 -0
  51. package/dist/filter-input/root/filter-input-root.css.js +6 -5
  52. package/dist/filter-input/root/filter-input-root.d.ts +5 -0
  53. package/dist/flex/root/flex-root.css.js +9 -0
  54. package/dist/flex/root/flex-root.d.ts +17 -6
  55. package/dist/graph/root/graph-root.css.js +11 -8
  56. package/dist/graph/root/graph-root.d.ts +8 -0
  57. package/dist/grid/root/grid-root.d.ts +4 -1
  58. package/dist/heatmap/root/heatmap-root.css.js +2 -1
  59. package/dist/icon-button/root/icon-button-root.css.js +68 -13
  60. package/dist/icon-button/root/icon-button-root.d.ts +4 -0
  61. package/dist/kbd/root/kbd-root.css.js +27 -0
  62. package/dist/kbd/root/kbd-root.d.ts +2 -0
  63. package/dist/lightbox/item/lightbox-item.css.js +4 -0
  64. package/dist/lightbox/root/lightbox-root.css.js +41 -0
  65. package/dist/line-chart/root/line-chart-root.css.js +16 -0
  66. package/dist/line-chart/root/line-chart-root.d.ts +2 -0
  67. package/dist/listbox/root/listbox-root.css.js +9 -0
  68. package/dist/listbox/root/listbox-root.d.ts +1 -0
  69. package/dist/loader/root/loader-root.css.js +3 -3
  70. package/dist/loader/root/loader-root.d.ts +2 -0
  71. package/dist/megamenu/root/megamenu-root.css.js +16 -13
  72. package/dist/megamenu/root/megamenu-root.d.ts +11 -0
  73. package/dist/menu/item/menu-item.css.js +25 -4
  74. package/dist/menu/item/menu-item.d.ts +1 -0
  75. package/dist/meter/root/meter-root.css.js +6 -3
  76. package/dist/meter/root/meter-root.d.ts +1 -0
  77. package/dist/navigation/root/navigation-root.d.ts +7 -0
  78. package/dist/notifications/root/notifications-root.css.js +6 -0
  79. package/dist/number-input/root/number-input-root.css.js +12 -9
  80. package/dist/number-input/root/number-input-root.d.ts +8 -0
  81. package/dist/object-inspector/root/object-inspector-root.css.js +2 -6
  82. package/dist/option/root/option-root.css.js +1 -1
  83. package/dist/orb/root/orb-root.d.ts +14 -6
  84. package/dist/orb/root/orb-root.js +16 -27
  85. package/dist/pin-input/root/pin-input-root.css.js +4 -4
  86. package/dist/pin-input/root/pin-input-root.d.ts +4 -0
  87. package/dist/progress/root/progress-root.css.js +4 -2
  88. package/dist/progress/root/progress-root.d.ts +1 -0
  89. package/dist/qr-code/root/qr-code-root.css.js +11 -0
  90. package/dist/quote/root/quote-root.css.js +3 -0
  91. package/dist/quote/root/quote-root.d.ts +2 -0
  92. package/dist/radio/root/radio-root.css.js +16 -0
  93. package/dist/radio/root/radio-root.d.ts +2 -1
  94. package/dist/sandbox/frame.d.ts +3 -2
  95. package/dist/sandbox/frame.js +19 -5
  96. package/dist/sandbox/index.d.ts +1 -0
  97. package/dist/sandbox/source/sandbox-source.css.js +70 -0
  98. package/dist/sandbox/source/sandbox-source.d.ts +34 -0
  99. package/dist/sandbox/source/sandbox-source.js +15 -0
  100. package/dist/sandbox/terminal/sandbox-terminal.d.ts +10 -2
  101. package/dist/sandbox/terminal/sandbox-terminal.js +18 -5
  102. package/dist/sandbox/tokens.d.ts +39 -0
  103. package/dist/sandbox/tokens.js +9 -0
  104. package/dist/scatter-chart/root/scatter-chart-root.css.js +9 -0
  105. package/dist/scatter-chart/root/scatter-chart-root.d.ts +2 -0
  106. package/dist/skeleton/root/skeleton-root.css.js +11 -11
  107. package/dist/skeleton/root/skeleton-root.d.ts +1 -0
  108. package/dist/skip-link/root/skip-link-root.css.js +12 -0
  109. package/dist/skip-link/root/skip-link-root.d.ts +3 -3
  110. package/dist/slider/root/slider-root.css.js +26 -0
  111. package/dist/sparkline/root/sparkline-root.css.js +49 -0
  112. package/dist/sparkline/root/sparkline-root.d.ts +6 -0
  113. package/dist/sparkline/root/sparkline-root.js +2 -1
  114. package/dist/speed-dial/item/speed-dial-item.css.js +7 -6
  115. package/dist/speed-dial/item/speed-dial-item.d.ts +3 -0
  116. package/dist/speed-dial/root/speed-dial-root.css.js +10 -5
  117. package/dist/speed-dial/root/speed-dial-root.d.ts +7 -1
  118. package/dist/speed-dial/root/speed-dial-root.js +0 -1
  119. package/dist/stat/root/stat-root.css.js +4 -0
  120. package/dist/steps/root/steps-root.css.js +28 -0
  121. package/dist/steps/root/steps-root.d.ts +5 -0
  122. package/dist/tabs/root/tabs-root.css.js +2 -2
  123. package/dist/tabs/root/tabs-root.d.ts +4 -0
  124. package/dist/tabs/tab/tab.css.js +16 -9
  125. package/dist/tabs/tab/tab.d.ts +7 -0
  126. package/dist/tag/group/tag-group.css.js +6 -0
  127. package/dist/text/clamp.d.ts +5 -2
  128. package/dist/text/clamp.js +3 -1
  129. package/dist/text/root/text-root.css.js +0 -6
  130. package/dist/text/root/text-root.d.ts +2 -1
  131. package/dist/textarea/root/textarea-root.css.js +27 -0
  132. package/dist/time-input/root/time-input-root.css.js +12 -0
  133. package/dist/time-picker/root/time-picker-root.css.js +1 -1
  134. package/dist/toast/item/toast-item.css.js +10 -0
  135. package/dist/toast/root/toast-root.d.ts +1 -0
  136. package/dist/toast/root/toast-root.js +1 -0
  137. package/dist/toggle/group/toggle-group.css.js +10 -7
  138. package/dist/toggle/group/toggle-group.d.ts +6 -1
  139. package/dist/toggle/root/toggle-root.d.ts +6 -0
  140. package/dist/toolbar/root/toolbar-root.css.js +1 -0
  141. package/dist/toolbar/root/toolbar-root.d.ts +2 -0
  142. package/dist/tooltip/root/tooltip-root.css.js +5 -5
  143. package/dist/tooltip/root/tooltip-root.d.ts +4 -0
  144. package/dist/tree/item/tree-item.css.js +19 -9
  145. package/dist/tree/item/tree-item.d.ts +9 -0
  146. package/dist/trend/root/trend-root.css.js +3 -5
  147. package/dist/trend/root/trend-root.d.ts +2 -2
  148. package/dist/virtual-list/root/virtual-list-root.css.js +14 -0
  149. package/dist/virtual-list/root/virtual-list-root.d.ts +7 -1
  150. package/lint.json +1 -1
  151. package/package.json +9 -9
  152. package/types/jsx.d.ts +4289 -631
  153. package/types/vue.d.ts +1296 -127
@@ -23,7 +23,7 @@ background-color: transparent;
23
23
 
24
24
  [part="control"] {
25
25
  font-variant-numeric: tabular-nums;
26
- padding-inline: var(--helix-space-xs);
26
+ padding-inline: var(--number-input-padding-inline, var(--helix-space-xs));
27
27
  text-align: center;
28
28
  }
29
29
 
@@ -46,12 +46,13 @@ padding-inline-end: var(--helix-space-xs);
46
46
  all: unset;
47
47
  align-items: center;
48
48
  align-self: stretch;
49
+ background-color: var(--number-input-stepper-background-color, transparent);
49
50
  box-sizing: border-box;
50
- color: var(--helix-color-foreground-muted);
51
+ color: var(--number-input-stepper-color, var(--helix-color-foreground-muted));
51
52
  cursor: pointer;
52
53
  display: flex;
53
54
  flex: none;
54
- inline-size: var(--helix-size-control);
55
+ inline-size: var(--number-input-stepper-size, var(--helix-size-control));
55
56
  justify-content: center;
56
57
  transition:
57
58
  background-color var(--helix-duration-fast) var(--helix-easing-standard),
@@ -59,17 +60,19 @@ color var(--helix-duration-fast) var(--helix-easing-standard);
59
60
  }
60
61
 
61
62
  [part="decrement"] {
62
- border-inline-end: var(--helix-border-thin) solid var(--helix-color-border);
63
+ border-inline-end: var(--helix-border-thin) solid
64
+ var(--number-input-stepper-border-color, var(--helix-color-border));
63
65
  }
64
66
 
65
67
  [part="increment"] {
66
- border-inline-start: var(--helix-border-thin) solid var(--helix-color-border);
68
+ border-inline-start: var(--helix-border-thin) solid
69
+ var(--number-input-stepper-border-color, var(--helix-color-border));
67
70
  }
68
71
 
69
72
  [part="decrement"]:hover:not(:disabled),
70
73
  [part="increment"]:hover:not(:disabled) {
71
- background-color: var(--helix-color-surface);
72
- color: var(--helix-color-foreground);
74
+ background-color: var(--number-input-stepper-background-color-hover, var(--helix-color-surface));
75
+ color: var(--number-input-stepper-color-hover, var(--helix-color-foreground));
73
76
  }
74
77
 
75
78
  [part="decrement"]:disabled,
@@ -80,8 +83,8 @@ opacity: 0.4;
80
83
 
81
84
  [part="decrement"] svg,
82
85
  [part="increment"] svg {
83
- block-size: var(--helix-size-icon-tight);
84
- inline-size: var(--helix-size-icon-tight);
86
+ block-size: var(--number-input-stepper-icon-size, var(--helix-size-icon-tight));
87
+ inline-size: var(--number-input-stepper-icon-size, var(--helix-size-icon-tight));
85
88
  }
86
89
 
87
90
  :host([scrub]) [part="label"] {
@@ -34,8 +34,16 @@ import type { CSSResultGroup } from 'lit';
34
34
  *
35
35
  * @cssproperty --number-input-background-color - The fill behind the number.
36
36
  * @cssproperty --number-input-border-color - Edge of the field at rest. Hover, focus and an error still draw their own.
37
+ * @cssproperty --number-input-padding-inline - Space at each side of the number, inside the steppers.
37
38
  * @cssproperty --number-input-radius - Corner radius of the field.
38
39
  * @cssproperty --number-input-size - Height of the field.
40
+ * @cssproperty --number-input-stepper-background-color - Fill behind each stepper button at rest.
41
+ * @cssproperty --number-input-stepper-background-color-hover - Fill behind a stepper button under the pointer.
42
+ * @cssproperty --number-input-stepper-border-color - The rule between each stepper button and the number.
43
+ * @cssproperty --number-input-stepper-color - Icon color of the stepper buttons at rest.
44
+ * @cssproperty --number-input-stepper-color-hover - Icon color of a stepper button under the pointer.
45
+ * @cssproperty --number-input-stepper-icon-size - Width and height of the stepper icons.
46
+ * @cssproperty --number-input-stepper-size - Width of each stepper button.
39
47
  */
40
48
  export declare class HelixNumberInput extends HelixHeadlessNumberInput {
41
49
  static styles: CSSResultGroup;
@@ -77,10 +77,6 @@ overflow-y: auto;
77
77
  overscroll-behavior: contain;
78
78
  }
79
79
 
80
- [part="tree"]:focus-visible {
81
- outline: none;
82
- }
83
-
84
80
  [part~="row"] {
85
81
  cursor: default;
86
82
  overflow: hidden;
@@ -105,11 +101,11 @@ var(--helix-space-sm)
105
101
  );
106
102
  }
107
103
 
108
- [part="tree"]:focus [part~="row-active"] {
104
+ [part="tree"]:focus-within [part~="row-active"] {
109
105
  background-color: var(--helix-color-surface-sunken);
110
106
  }
111
107
 
112
- [part="tree"]:focus-visible [part~="row-active"] {
108
+ [part~="row"]:focus-visible {
113
109
  outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
114
110
  outline-offset: calc(var(--helix-size-focus-offset) * -1);
115
111
  }
@@ -67,7 +67,7 @@ mask-repeat: no-repeat;
67
67
  mask-size: contain;
68
68
  }
69
69
 
70
- :host([data-active]) [part~="item"],
70
+ :host(:state(active)) [part~="item"],
71
71
  :host(:not([disabled])) [part~="item"]:hover {
72
72
 
73
73
  background-color: var(
@@ -31,7 +31,8 @@ import { type OrbState } from '../shapes.js';
31
31
  * `prefers-reduced-motion`, it draws one still frame.
32
32
  *
33
33
  * The orb is styled only and has behavior, which the tier split does not
34
- * otherwise allow. Its role, name and busy state follow `state`, and the rest
34
+ * otherwise allow. Its role, name and busy state follow `state`, unless
35
+ * `announce` makes it a status region, and the rest
35
36
  * is drawing dots to a canvas, so there is no structure to restyle. For a
36
37
  * drawing of your own, the headless `Loader` holds the same busy progress bar
37
38
  * and its name.
@@ -47,7 +48,7 @@ import { type OrbState } from '../shapes.js';
47
48
  * @documentation https://fusion.dev/helix/components/orb
48
49
  *
49
50
  * @csspart canvas - The canvas the dots are drawn on.
50
- * @csspart live - The live region that says the new state when `announce` is set. It is rendered from the first change of state and clipped out of view.
51
+ * @csspart live - The word for the state, which the orb says as it changes when `announce` is set. Rendered only then, and clipped out of view.
51
52
  *
52
53
  * @cssproperty --orb-color - The color of the nearest dots. Defaults to `currentColor`.
53
54
  * @cssproperty --orb-ground - The color the farthest dots fade toward. Defaults to the page's background.
@@ -64,8 +65,13 @@ import { type OrbState } from '../shapes.js';
64
65
  export declare class HelixOrb extends HelixElement {
65
66
  static styles: CSSResultGroup;
66
67
  /**
67
- * Says each new state through a polite live region, such as "Working" and
68
- * then "Done".
68
+ * Says each new state politely, such as "Working" and then "Done".
69
+ *
70
+ * **The orb becomes the live region**: its role is `status` rather than
71
+ * `progressbar` or `img`, and it holds the state's word. A region inside a
72
+ * progress bar or an image may never be heard, because assistive technology
73
+ * treats what those two hold as presentation. The name is still `label` or
74
+ * the state's word.
69
75
  *
70
76
  * Off by default, because the name and the busy state already change with
71
77
  * the state, and an orb beside a stream of messages would otherwise talk
@@ -118,8 +124,6 @@ export declare class HelixOrb extends HelixElement {
118
124
  * markup and plain HTML can carry the words too.
119
125
  */
120
126
  stateLabels: Partial<Record<OrbState, string>>;
121
- /** The region `announce` speaks in. */
122
- private readonly status;
123
127
  private canvas;
124
128
  private context;
125
129
  /**
@@ -161,6 +165,10 @@ export declare class HelixOrb extends HelixElement {
161
165
  */
162
166
  private phase;
163
167
  protected willUpdate(changed: PropertyValues): void;
168
+ /** The state drawn: `state`, or `thinking` for one it does not draw. */
169
+ private get shown();
170
+ /** The word for the state drawn, in the page's words where it gave them. */
171
+ private get word();
164
172
  protected render(): TemplateResult;
165
173
  protected firstUpdated(changed: PropertyValues): void;
166
174
  connectedCallback(): void;
@@ -4,8 +4,8 @@ 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, drawStatus, HelixElement, hostAria, PaintGateController, readColor, StatusController, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
7
+ import { customElement, HelixElement, hostAria, PaintGateController, readColor, wordsConverter, } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { styles as liveStyles } from '../../shared/live/live.css.js';
11
11
  import { blendStaged, evenSphere, fibonacci, meshFor, ORB_STATES, orbPoint, paramsFor, project, smoother, stateDistance, TRAIL, } from '../shapes.js';
@@ -23,19 +23,6 @@ const LABELS = {
23
23
  waiting: 'Waiting',
24
24
  working: 'Working',
25
25
  };
26
- const labelsConverter = {
27
- fromAttribute: (value) => {
28
- try {
29
- const parsed = JSON.parse(value ?? '');
30
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
31
- ? parsed
32
- : {};
33
- }
34
- catch {
35
- return {};
36
- }
37
- },
38
- };
39
26
  const GRAY = [128, 128, 128];
40
27
  let HelixOrb = class HelixOrb extends HelixElement {
41
28
  constructor() {
@@ -44,7 +31,6 @@ let HelixOrb = class HelixOrb extends HelixElement {
44
31
  this.label = '';
45
32
  this.state = 'thinking';
46
33
  this.stateLabels = {};
47
- this.status = new StatusController(this);
48
34
  this.context = null;
49
35
  this.gate = new PaintGateController(this, {
50
36
  canRun: () => !!this.context && !this.lost,
@@ -86,14 +72,12 @@ let HelixOrb = class HelixOrb extends HelixElement {
86
72
  static { this.styles = [liveStyles, styles]; }
87
73
  willUpdate(changed) {
88
74
  super.willUpdate(changed);
89
- const state = ORB_STATES.includes(this.state)
90
- ? this.state
91
- : 'thinking';
75
+ const state = this.shown;
92
76
  hostAria(this.internals, {
93
- label: this.label || this.stateLabels[state] || LABELS[state],
94
- role: state === 'done' ? 'img' : 'progressbar',
77
+ label: this.label || this.word,
78
+ role: this.announce ? 'status' : state === 'done' ? 'img' : 'progressbar',
95
79
  });
96
- this.internals.ariaBusy = state === 'done' ? 'false' : 'true';
80
+ this.internals.ariaBusy = this.announce ? null : state === 'done' ? 'false' : 'true';
97
81
  this.toggleState('connecting', state === 'connecting');
98
82
  this.toggleState('done', state === 'done');
99
83
  this.toggleState('fetching', state === 'fetching');
@@ -103,13 +87,18 @@ let HelixOrb = class HelixOrb extends HelixElement {
103
87
  this.toggleState('working', state === 'working');
104
88
  if (changed.has('state') && changed.get('state') !== undefined) {
105
89
  this.turn();
106
- if (this.announce) {
107
- this.status.say(this.label || this.stateLabels[state] || LABELS[state]);
108
- }
109
90
  }
110
91
  }
92
+ get shown() {
93
+ return ORB_STATES.includes(this.state)
94
+ ? this.state
95
+ : 'thinking';
96
+ }
97
+ get word() {
98
+ return this.stateLabels?.[this.shown] || LABELS[this.shown];
99
+ }
111
100
  render() {
112
- return html `<canvas part="canvas" aria-hidden="true"></canvas>${drawStatus(this.status)}`;
101
+ return html `<canvas part="canvas" aria-hidden="true"></canvas>${this.announce ? html `<span part="live">${this.word}</span>` : nothing}`;
113
102
  }
114
103
  firstUpdated(changed) {
115
104
  super.firstUpdated(changed);
@@ -275,7 +264,7 @@ __decorate([
275
264
  property()
276
265
  ], HelixOrb.prototype, "state", void 0);
277
266
  __decorate([
278
- property({ attribute: 'state-labels', converter: labelsConverter })
267
+ property({ attribute: 'state-labels', converter: wordsConverter })
279
268
  ], HelixOrb.prototype, "stateLabels", void 0);
280
269
  HelixOrb = __decorate([
281
270
  customElement('helix-orb')
@@ -19,9 +19,9 @@ border: var(--helix-border-thin) solid
19
19
  var(--pin-input-cell-border-color, var(--helix-color-border));
20
20
  border-radius: var(--pin-input-cell-radius, var(--helix-radii-sm));
21
21
  box-sizing: border-box;
22
- color: var(--helix-color-foreground);
22
+ color: var(--pin-input-cell-color, var(--helix-color-foreground));
23
23
  font-family: var(--helix-font-sans);
24
- font-size: var(--helix-text-lg);
24
+ font-size: var(--pin-input-cell-font-size, var(--helix-text-lg));
25
25
 
26
26
  font-variant-numeric: tabular-nums;
27
27
  font-weight: var(--helix-weight-medium);
@@ -35,7 +35,7 @@ border-color: var(--helix-color-foreground-muted);
35
35
  }
36
36
 
37
37
  [part="cell"]:focus-visible {
38
- border-color: var(--helix-color-primary);
38
+ border-color: var(--pin-input-cell-border-color-focus, var(--helix-color-primary));
39
39
  outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
40
40
  outline-offset: 0;
41
41
  }
@@ -59,7 +59,7 @@ font-size: var(--helix-text-xl);
59
59
 
60
60
  [part="separator"] {
61
61
  align-self: center;
62
- color: var(--helix-color-foreground-muted);
62
+ color: var(--pin-input-separator-color, var(--helix-color-foreground-muted));
63
63
  font-size: var(--helix-text-lg);
64
64
  }
65
65
 
@@ -25,9 +25,13 @@ import type { CSSResultGroup } from 'lit';
25
25
  *
26
26
  * @cssproperty --pin-input-cell-background-color - Fill behind each box.
27
27
  * @cssproperty --pin-input-cell-border-color - Edge of each box at rest. Hover, focus and an error still draw their own.
28
+ * @cssproperty --pin-input-cell-border-color-focus - Edge of the box holding focus.
29
+ * @cssproperty --pin-input-cell-color - The color of a typed character.
30
+ * @cssproperty --pin-input-cell-font-size - The size of a typed character.
28
31
  * @cssproperty --pin-input-cell-radius - Corner radius of each box.
29
32
  * @cssproperty --pin-input-cell-size - Width and height of each box.
30
33
  * @cssproperty --pin-input-gap - The space between one box and the next.
34
+ * @cssproperty --pin-input-separator-color - The color of the text `format` draws between boxes.
31
35
  */
32
36
  export declare class HelixPinInput extends HelixHeadlessPinInput {
33
37
  static styles: CSSResultGroup;
@@ -55,7 +55,8 @@ border-radius: inherit;
55
55
  inline-size: 100%;
56
56
  scale: var(--progress-fraction, 0) 1;
57
57
  transform-origin: left center;
58
- transition: scale var(--helix-duration-normal) var(--helix-easing-decelerate);
58
+ transition: scale var(--progress-transition-duration, var(--helix-duration-normal))
59
+ var(--helix-easing-decelerate);
59
60
  }
60
61
 
61
62
  :host(:dir(rtl)) [part="indicator"] {
@@ -150,7 +151,8 @@ scale: none;
150
151
  stroke: var(--progress-color, var(--helix-color-primary));
151
152
  stroke-dasharray: calc(var(--progress-fraction) * 100) 100;
152
153
  stroke-linecap: round;
153
- transition: stroke-dasharray var(--helix-duration-normal) var(--helix-easing-decelerate);
154
+ transition: stroke-dasharray var(--progress-transition-duration, var(--helix-duration-normal))
155
+ var(--helix-easing-decelerate);
154
156
  }
155
157
 
156
158
  :host([shape="ring"][show-value]) {
@@ -28,6 +28,7 @@ import type { CSSResultGroup } from 'lit';
28
28
  * @cssproperty --progress-ring-width - Thickness of the ring's stroke.
29
29
  * @cssproperty --progress-size - Thickness of the bar.
30
30
  * @cssproperty --progress-track-color - Fill of the part that is not done.
31
+ * @cssproperty --progress-transition-duration - How long the fill takes to move to a new value. `0s` moves it at once.
31
32
  */
32
33
  export declare class HelixProgress extends HelixHeadlessProgress {
33
34
  /**
@@ -17,6 +17,17 @@ display: block;
17
17
  inline-size: 100%;
18
18
  }
19
19
 
20
+ [part="fallback"] {
21
+ align-items: center;
22
+ block-size: 100%;
23
+ box-sizing: border-box;
24
+ display: flex;
25
+ justify-content: center;
26
+ overflow-wrap: anywhere;
27
+ padding: var(--helix-space-md);
28
+ text-align: center;
29
+ }
30
+
20
31
  [part="finders"] {
21
32
  fill: var(--qr-code-finder-color, currentColor);
22
33
  }
@@ -8,7 +8,10 @@ font: inherit;
8
8
 
9
9
  color: var(--quote-color, var(--helix-color-foreground-muted));
10
10
  font: inherit;
11
+
12
+ font-family: var(--quote-font-family, inherit);
11
13
  font-style: var(--quote-font-style, italic);
14
+ font-weight: var(--quote-font-weight, inherit);
12
15
  }
13
16
 
14
17
  @media print {
@@ -14,7 +14,9 @@ import type { CSSResultGroup } from 'lit';
14
14
  * @documentation https://fusion.dev/helix/components/quote
15
15
  *
16
16
  * @cssproperty --quote-color - Color of the quoted text.
17
+ * @cssproperty --quote-font-family - The typeface of the quoted text, the paragraph's own by default.
17
18
  * @cssproperty --quote-font-style - The slant of the quoted text, `italic` by default. Set `normal` for a face whose italic is too loud, or `oblique` for one with no italic of its own.
19
+ * @cssproperty --quote-font-weight - The weight of the quoted text, the paragraph's own by default.
18
20
  */
19
21
  export declare class HelixQuote extends HelixHeadlessQuote {
20
22
  static styles: CSSResultGroup;
@@ -58,6 +58,22 @@ transition: scale var(--helix-duration-fast) var(--helix-easing-emphasized);
58
58
  scale: 1;
59
59
  }
60
60
 
61
+ :host(:state(has-indicator)) [part="box"]::after {
62
+ content: none;
63
+ }
64
+
65
+ slot[name="indicator"]::slotted(*) {
66
+ block-size: var(--radio-indicator-size, calc(var(--_radio-size) * 0.75));
67
+ color: var(--radio-mark-color, var(--helix-color-foreground));
68
+ inline-size: var(--radio-indicator-size, calc(var(--_radio-size) * 0.75));
69
+ scale: 0;
70
+ transition: scale var(--helix-duration-fast) var(--helix-easing-emphasized);
71
+ }
72
+
73
+ :host(:state(selected)) slot[name="indicator"]::slotted(*) {
74
+ scale: 1;
75
+ }
76
+
61
77
  [part="item"]:focus-visible [part="box"] {
62
78
  outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
63
79
  outline-offset: var(--helix-size-focus-offset);
@@ -23,7 +23,8 @@ import type { CSSResultGroup } from 'lit';
23
23
  * @cssproperty --radio-card-border-color - The border color of a card-shaped choice.
24
24
  * @cssproperty --radio-card-padding - The padding inside a card-shaped choice.
25
25
  * @cssproperty --radio-chip-size - The diameter of a chip.
26
- * @cssproperty --radio-mark-color - The color of the dot.
26
+ * @cssproperty --radio-indicator-size - The width and height of an icon slotted into `indicator`. Three quarters of the circle by default.
27
+ * @cssproperty --radio-mark-color - The color of the dot, or of an icon slotted into `indicator`.
27
28
  * @cssproperty --radio-size - The diameter of the circle.
28
29
  */
29
30
  export declare class HelixRadio extends HelixHeadlessRadio {
@@ -24,9 +24,10 @@ export interface ServePreviewOptions {
24
24
  * shrinks each top-level child to its own content and centers it, which is
25
25
  * the difference for a component whose root is a full-width block. They
26
26
  * select the root by its `data-preview-root` attribute, so they reach nothing
27
- * an example draws inside it.
27
+ * an example draws inside it. The three that fill the viewport count their
28
+ * padding inside that height, so the root never overflows the frame.
28
29
  */
29
- export declare const LAYOUT_RULES = "\n:root { --preview-measure: 48rem; }\n[data-preview-root][data-layout=\"padded\"] { padding: var(--helix-space-lg, 1.5rem); }\n[data-preview-root][data-layout=\"centered\"], [data-preview-root][data-layout=\"hug\"] {\n align-content: center; display: block; inline-size: min(100%, var(--preview-measure));\n margin-inline: auto; min-block-size: 100dvh; padding: var(--helix-space-lg, 1.5rem);\n}\n[data-preview-root][data-layout=\"centered\"] { text-align: center; }\n[data-preview-root][data-layout=\"centered\"] > * { margin-inline: auto; text-align: start; }\n[data-preview-root][data-layout=\"hug\"] > * {\n inline-size: fit-content; margin-inline: auto; max-inline-size: 100%;\n}\n[data-preview-root][data-layout=\"fullscreen\"] { min-block-size: 100dvh; padding: 0; }\n";
30
+ export declare const LAYOUT_RULES = "\n:root { --preview-measure: 48rem; }\n[data-preview-root][data-layout=\"padded\"] { padding: var(--helix-space-lg, 1.5rem); }\n[data-preview-root][data-layout=\"centered\"], [data-preview-root][data-layout=\"hug\"] {\n align-content: center; box-sizing: border-box; display: block;\n inline-size: min(100%, var(--preview-measure)); margin-inline: auto; min-block-size: 100dvh;\n padding: var(--helix-space-lg, 1.5rem);\n}\n[data-preview-root][data-layout=\"centered\"] { text-align: center; }\n[data-preview-root][data-layout=\"centered\"] > * { margin-inline: auto; text-align: start; }\n[data-preview-root][data-layout=\"hug\"] > * {\n inline-size: fit-content; margin-inline: auto; max-inline-size: 100%;\n}\n[data-preview-root][data-layout=\"fullscreen\"] { box-sizing: border-box; min-block-size: 100dvh; padding: 0; }\n";
30
31
  /** Whether `data` is a render message, as the preview sends one. */
31
32
  export declare const isRender: (data: unknown) => data is PreviewRender;
32
33
  /**
@@ -2,15 +2,16 @@ export const LAYOUT_RULES = `
2
2
  :root { --preview-measure: 48rem; }
3
3
  [data-preview-root][data-layout="padded"] { padding: var(--helix-space-lg, 1.5rem); }
4
4
  [data-preview-root][data-layout="centered"], [data-preview-root][data-layout="hug"] {
5
- align-content: center; display: block; inline-size: min(100%, var(--preview-measure));
6
- margin-inline: auto; min-block-size: 100dvh; padding: var(--helix-space-lg, 1.5rem);
5
+ align-content: center; box-sizing: border-box; display: block;
6
+ inline-size: min(100%, var(--preview-measure)); margin-inline: auto; min-block-size: 100dvh;
7
+ padding: var(--helix-space-lg, 1.5rem);
7
8
  }
8
9
  [data-preview-root][data-layout="centered"] { text-align: center; }
9
10
  [data-preview-root][data-layout="centered"] > * { margin-inline: auto; text-align: start; }
10
11
  [data-preview-root][data-layout="hug"] > * {
11
12
  inline-size: fit-content; margin-inline: auto; max-inline-size: 100%;
12
13
  }
13
- [data-preview-root][data-layout="fullscreen"] { min-block-size: 100dvh; padding: 0; }
14
+ [data-preview-root][data-layout="fullscreen"] { box-sizing: border-box; min-block-size: 100dvh; padding: 0; }
14
15
  `;
15
16
  export const isRender = (data) => typeof data === 'object' &&
16
17
  data !== null &&
@@ -113,9 +114,21 @@ export const servePreview = (options = {}) => {
113
114
  options.onRender?.(root, fresh);
114
115
  measure();
115
116
  };
116
- const press = () => post({ type: 'helix-preview-press' });
117
117
  const watcher = new ResizeObserver(measure);
118
- watcher.observe(root);
118
+ const watch = () => {
119
+ watcher.disconnect();
120
+ watcher.observe(root);
121
+ for (const child of root.children) {
122
+ const placed = getComputedStyle(child).display === 'contents' ? child.children : [child];
123
+ for (const box of placed) {
124
+ watcher.observe(box);
125
+ }
126
+ }
127
+ };
128
+ const children = new MutationObserver(watch);
129
+ children.observe(root, { childList: true });
130
+ watch();
131
+ const press = () => post({ type: 'helix-preview-press' });
119
132
  window.addEventListener('error', failed);
120
133
  window.addEventListener('message', render);
121
134
  document.addEventListener('pointerdown', press, { capture: true, passive: true });
@@ -125,6 +138,7 @@ export const servePreview = (options = {}) => {
125
138
  console[level] = originals[index];
126
139
  });
127
140
  watcher.disconnect();
141
+ children.disconnect();
128
142
  window.removeEventListener('error', failed);
129
143
  window.removeEventListener('message', render);
130
144
  document.removeEventListener('pointerdown', press, { capture: true });
@@ -18,4 +18,5 @@ export * from './root/sandbox-root.js';
18
18
  export * from './source/sandbox-source.js';
19
19
  export * from './status-bar/sandbox-status-bar.js';
20
20
  export * from './terminal/sandbox-terminal.js';
21
+ export type { SandboxHighlight, SandboxToken } from './tokens.js';
21
22
  export * from './toolbar/sandbox-toolbar.js';
@@ -23,4 +23,74 @@ tab-size: 2;
23
23
  [part="source"]:focus-visible {
24
24
  background-color: var(--helix-color-surface);
25
25
  outline: none;
26
+ }
27
+
28
+ [part="field"] {
29
+ background-color: var(--sandbox-source-background-color, var(--helix-color-background));
30
+ block-size: 100%;
31
+ display: grid;
32
+ grid-template-rows: minmax(max-content, auto);
33
+ overflow: auto;
34
+ }
35
+
36
+ [part="field"]:focus-within {
37
+ background-color: var(--helix-color-surface);
38
+ }
39
+
40
+ [part="field"] > [part="highlight"],
41
+ [part="field"] > [part="source"] {
42
+ border: none;
43
+ box-sizing: border-box;
44
+ font-family: var(--helix-font-mono);
45
+ font-size: var(--helix-text-xs);
46
+ grid-area: 1 / 1;
47
+ inline-size: 100%;
48
+ letter-spacing: normal;
49
+ line-height: var(--helix-leading-normal);
50
+ margin: 0;
51
+ min-block-size: var(--sandbox-source-min-block-size, 12rem);
52
+ overflow-wrap: break-word;
53
+ padding: var(--helix-space-sm);
54
+ tab-size: 2;
55
+ white-space: pre-wrap;
56
+ }
57
+
58
+ [part="field"] > [part="highlight"] {
59
+ color: var(--helix-color-foreground-muted);
60
+ }
61
+
62
+ [part="field"] > [part="source"],
63
+ [part="field"] > [part="source"]:focus-visible {
64
+ -webkit-text-fill-color: transparent;
65
+ align-self: stretch;
66
+ background-color: transparent;
67
+ block-size: auto;
68
+ caret-color: var(--helix-color-foreground);
69
+ color: transparent;
70
+ overflow: hidden;
71
+ }
72
+
73
+ [part="token"][data-kind="callable"] {
74
+ color: var(--sandbox-source-callable-color, var(--helix-color-syntax-callable));
75
+ }
76
+ [part="token"][data-kind="comment"] {
77
+ color: var(--sandbox-source-comment-color, var(--helix-color-syntax-comment));
78
+ }
79
+ [part="token"][data-kind="constant"] {
80
+ color: var(--sandbox-source-constant-color, var(--helix-color-syntax-constant));
81
+ }
82
+ [part="token"][data-kind="invalid"] {
83
+ color: var(--sandbox-source-invalid-color, var(--helix-color-syntax-invalid));
84
+ }
85
+ [part="token"][data-kind="keyword"] {
86
+ color: var(--sandbox-source-keyword-color, var(--helix-color-syntax-keyword));
87
+ }
88
+ [part="token"][data-kind="name"] {
89
+ color: var(--sandbox-source-name-color, var(--helix-color-syntax-name));
90
+ }
91
+ [part="token"][data-kind="string"] {
92
+ color: var(--sandbox-source-string-color, var(--helix-color-syntax-string));
93
+ }
94
+ [part="token"][data-kind="tag"] {
95
+ color: var(--sandbox-source-tag-color, var(--helix-color-syntax-tag));
26
96
  }`);
@@ -1,5 +1,6 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import type { CSSResultGroup, TemplateResult } from 'lit';
3
+ import { type SandboxHighlight } from '../tokens.js';
3
4
  /**
4
5
  * @summary An editable source pane for a sandbox preview.
5
6
  *
@@ -11,6 +12,16 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
11
12
  * field, press Escape and then Tab. Shift+Tab moves focus back as it does
12
13
  * anywhere else. The field's description says so to a screen reader.
13
14
  *
15
+ * **`highlight` colors the source** with a highlighter you bring, such as one
16
+ * built on Lezer, Prism or Shiki. It is a function from the source to runs of
17
+ * text, each with an optional kind, and the pane draws the runs under a
18
+ * transparent field, so typing, selection and undo stay the textarea's. The
19
+ * kinds are `callable`, `comment`, `constant`, `invalid`, `keyword`, `name`,
20
+ * `string` and `tag`, each colored by the `--helix-color-syntax-*` token of
21
+ * its name and a hook of its own, such as `--sandbox-source-keyword-color`.
22
+ * A run of any other kind is drawn in the field's own color. Every run with a
23
+ * kind is a `token` part, and carries its kind as `data-kind`.
24
+ *
14
25
  * @since 0.1.0
15
26
  * @status stable
16
27
  * @aliases Editor, Code pane
@@ -20,16 +31,37 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
20
31
  * @event {HelixCaretEvent} helix-caret - Fired when the cursor moves.
21
32
  * @event {HelixEditEvent} helix-edit - Fired after the source was edited, with the new source.
22
33
  *
34
+ * @csspart field - The box that scrolls the field and its colors together, under `highlight`.
35
+ * @csspart highlight - The colored copy of the source drawn under the field, under `highlight`.
23
36
  * @csspart source - The editable region.
37
+ * @csspart token - One run of the source that has a kind, under `highlight`.
24
38
  *
25
39
  * @cssproperty --sandbox-source-background-color - Fill behind the source.
40
+ * @cssproperty --sandbox-source-callable-color - Color of a `callable` run, under `highlight`. Defaults to `--helix-color-syntax-callable`.
41
+ * @cssproperty --sandbox-source-comment-color - Color of a `comment` run, under `highlight`. Defaults to `--helix-color-syntax-comment`.
42
+ * @cssproperty --sandbox-source-constant-color - Color of a `constant` run, under `highlight`. Defaults to `--helix-color-syntax-constant`.
43
+ * @cssproperty --sandbox-source-invalid-color - Color of an `invalid` run, under `highlight`. Defaults to `--helix-color-syntax-invalid`.
44
+ * @cssproperty --sandbox-source-keyword-color - Color of a `keyword` run, under `highlight`. Defaults to `--helix-color-syntax-keyword`.
26
45
  * @cssproperty --sandbox-source-min-block-size - Minimum height of the pane.
46
+ * @cssproperty --sandbox-source-name-color - Color of a `name` run, under `highlight`. Defaults to `--helix-color-syntax-name`.
47
+ * @cssproperty --sandbox-source-string-color - Color of a `string` run, under `highlight`. Defaults to `--helix-color-syntax-string`.
48
+ * @cssproperty --sandbox-source-tag-color - Color of a `tag` run, under `highlight`. Defaults to `--helix-color-syntax-tag`.
27
49
  */
28
50
  export declare class HelixSandboxSource extends HelixElement {
29
51
  /**
30
52
  * The editable region's name, read by screen readers.
31
53
  */
32
54
  fieldLabel: string;
55
+ /**
56
+ * Colors the source: a function from the source and the pane's `name` to
57
+ * runs of text, each with an optional kind, such as
58
+ * `[{ text: '<' }, { kind: 'tag', text: 'p' }, { text: '>' }]`.
59
+ *
60
+ * Absent, the pane is a plain field. Runs whose text does not add up to the
61
+ * source are drawn as the source, uncolored, so the colors never drift from
62
+ * the caret. Set it as a property.
63
+ */
64
+ highlight?: SandboxHighlight;
33
65
  /**
34
66
  * The field's description, read by screen readers while Tab indents.
35
67
  */
@@ -91,6 +123,8 @@ export declare class HelixSandboxSource extends HelixElement {
91
123
  private edit;
92
124
  private readonly handleKeydown;
93
125
  protected render(): TemplateResult;
126
+ /** The field itself and its description, which is the whole pane without `highlight`. */
127
+ private renderField;
94
128
  static styles: CSSResultGroup;
95
129
  }
96
130
  declare global {