@fusion.dev/helix-headless 0.1.0 → 0.3.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 (127) hide show
  1. package/custom-elements.json +234 -234
  2. package/dist/accordion/item/accordion-item.js +0 -108
  3. package/dist/accordion/root/accordion-root.js +0 -33
  4. package/dist/announcement/root/announcement-root.js +1 -76
  5. package/dist/app-header/root/app-header-root.js +0 -84
  6. package/dist/avatar/group/avatar-group.js +0 -77
  7. package/dist/avatar/root/avatar-root.js +0 -72
  8. package/dist/badge/root/badge-root.js +0 -67
  9. package/dist/bar-chart/root/bar-chart-root.js +3 -263
  10. package/dist/blockquote/root/blockquote-root.js +0 -46
  11. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -72
  12. package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -52
  13. package/dist/button/root/button-root.js +0 -215
  14. package/dist/button-group/root/button-group-root.js +0 -30
  15. package/dist/callout/root/callout-root.js +0 -52
  16. package/dist/card/root/card-root.js +0 -41
  17. package/dist/carousel/root/carousel-root.d.ts +2 -2
  18. package/dist/carousel/root/carousel-root.js +2 -362
  19. package/dist/chat/composer/chat-composer.js +0 -131
  20. package/dist/chat/message/chat-message.js +0 -137
  21. package/dist/chat/root/chat-root.js +0 -193
  22. package/dist/checkbox/root/checkbox-root.js +0 -113
  23. package/dist/checkbox-group/root/checkbox-group-root.js +1 -157
  24. package/dist/code/root/code-root.js +0 -26
  25. package/dist/code-block/root/code-block-root.js +0 -100
  26. package/dist/color-input/root/color-input-root.js +0 -217
  27. package/dist/color-picker/color.js +0 -69
  28. package/dist/color-picker/root/color-picker-root.js +0 -272
  29. package/dist/combobox/root/combobox-root.js +0 -428
  30. package/dist/command/root/command-root.js +0 -177
  31. package/dist/copy-button/root/copy-button-root.js +0 -134
  32. package/dist/data-table/root/data-table-root.js +0 -336
  33. package/dist/data-table/root/order.js +0 -100
  34. package/dist/date-input/root/date-input-root.js +1 -398
  35. package/dist/date-picker/root/date-picker-root.js +0 -358
  36. package/dist/dialog/root/dialog-root.js +0 -189
  37. package/dist/divider/root/divider-root.js +0 -71
  38. package/dist/donut-chart/root/donut-chart-root.js +0 -214
  39. package/dist/drawer/root/drawer-root.js +0 -116
  40. package/dist/drilldown/item/drilldown-item.js +0 -82
  41. package/dist/drilldown/root/drilldown-root.js +0 -91
  42. package/dist/element-events.generated.js +0 -5
  43. package/dist/empty/root/empty-root.js +0 -35
  44. package/dist/file-input/root/file-input-root.js +0 -232
  45. package/dist/format-date/root/format-date-root.js +0 -95
  46. package/dist/heatmap/root/buckets.js +0 -57
  47. package/dist/heatmap/root/heatmap-root.js +0 -313
  48. package/dist/icon/root/icon-root.js +0 -48
  49. package/dist/icon-button/root/icon-button-root.js +0 -30
  50. package/dist/image/root/image-root.js +0 -174
  51. package/dist/index.js +0 -9
  52. package/dist/input/root/input-root.js +0 -228
  53. package/dist/kbd/root/kbd-root.js +0 -117
  54. package/dist/legend/root/legend-root.js +0 -73
  55. package/dist/line-chart/root/line-chart-root.js +0 -172
  56. package/dist/list/item/list-item.js +0 -92
  57. package/dist/list/root/list-root.js +0 -26
  58. package/dist/listbox/root/listbox-root.js +2 -429
  59. package/dist/loader/root/loader-root.js +0 -40
  60. package/dist/megamenu/root/megamenu-root.js +0 -284
  61. package/dist/menu/item/menu-item.js +1 -221
  62. package/dist/menu/root/menu-root.js +1 -171
  63. package/dist/meter/root/meter-root.js +1 -152
  64. package/dist/navigation/root/navigation-root.js +0 -56
  65. package/dist/notifications/notification/notification.js +0 -159
  66. package/dist/notifications/root/notifications-root.js +0 -148
  67. package/dist/number-input/root/number-input-root.js +1 -263
  68. package/dist/option/group/option-group.js +0 -26
  69. package/dist/option/root/option-root.js +0 -157
  70. package/dist/outline/root/outline-root.js +0 -292
  71. package/dist/pagination/root/pagination-root.js +0 -361
  72. package/dist/pagination/size/pagination-size.js +0 -104
  73. package/dist/pagination/summary/pagination-summary.js +0 -90
  74. package/dist/pin-input/root/pin-input-root.js +0 -276
  75. package/dist/popover/root/popover-root.js +0 -103
  76. package/dist/progress/root/progress-root.js +1 -133
  77. package/dist/qr-code/root/qr-code-root.js +0 -96
  78. package/dist/quote/root/quote-root.js +0 -25
  79. package/dist/radio/group/radio-group.js +0 -81
  80. package/dist/radio/root/radio-root.js +0 -85
  81. package/dist/rating/root/rating-root.js +0 -220
  82. package/dist/scroll-area/root/scroll-area-root.js +0 -100
  83. package/dist/select/root/select-root.js +2 -218
  84. package/dist/skeleton/root/skeleton-root.js +0 -20
  85. package/dist/slider/root/slider-root.js +1 -267
  86. package/dist/sparkline/root/sparkline-root.js +3 -164
  87. package/dist/speed-dial/item/speed-dial-item.js +0 -52
  88. package/dist/speed-dial/root/speed-dial-root.js +1 -116
  89. package/dist/splitter/root/splitter-root.js +0 -489
  90. package/dist/stat/group/stat-group.js +0 -18
  91. package/dist/stat/root/stat-root.js +0 -23
  92. package/dist/steps/root/steps-root.js +0 -62
  93. package/dist/steps/step/step.js +0 -131
  94. package/dist/switch/root/switch-root.js +0 -80
  95. package/dist/table/cell/table-cell.js +0 -23
  96. package/dist/table/column/table-column.js +0 -158
  97. package/dist/table/root/table-root.js +0 -82
  98. package/dist/table/row/table-row.js +0 -21
  99. package/dist/tabs/panel/tab-panel.js +0 -49
  100. package/dist/tabs/root/tabs-root.js +0 -98
  101. package/dist/tabs/tab/tab.js +0 -59
  102. package/dist/tag/root/tag-root.js +0 -31
  103. package/dist/textarea/root/textarea-root.js +0 -162
  104. package/dist/time-input/root/time-input-root.js +0 -283
  105. package/dist/time-picker/root/time-picker-root.js +0 -326
  106. package/dist/timeline/item/timeline-item.js +0 -66
  107. package/dist/timeline/root/timeline-root.js +0 -41
  108. package/dist/toast/item/toast-item.js +0 -165
  109. package/dist/toast/root/toast-root.js +0 -51
  110. package/dist/toggle/group/toggle-group.js +0 -150
  111. package/dist/toggle/root/toggle-root.js +0 -78
  112. package/dist/toolbar/root/toolbar-root.js +0 -116
  113. package/dist/tooltip/root/tooltip-root.js +0 -427
  114. package/dist/tour/root/tour-root.js +0 -130
  115. package/dist/tour/stop/tour-stop.js +1 -127
  116. package/dist/tree/item/tree-item.js +0 -198
  117. package/dist/tree/root/tree-root.js +0 -97
  118. package/dist/trend/root/trend-root.js +0 -53
  119. package/dist/video/root/video-root.d.ts +8 -12
  120. package/dist/video/root/video-root.js +5 -756
  121. package/dist/video-playlist/item/video-playlist-item.js +1 -90
  122. package/dist/video-playlist/menu/video-playlist-menu.js +1 -132
  123. package/dist/video-playlist/root/video-playlist-root.js +0 -113
  124. package/dist/virtual-list/root/virtual-list-root.js +0 -105
  125. package/jsx.d.ts +1408 -2576
  126. package/package.json +6 -6
  127. package/vue.d.ts +1 -1
@@ -8,7 +8,6 @@ import { closestInstance, customElement, HelixElement, MixinCollection, MixinCon
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessTreeItem } from '../item/tree-item.js';
11
- /** Keys the tree handles, per the APG tree pattern. */
12
11
  const KEYS = new Set([
13
12
  'ArrowDown',
14
13
  'ArrowUp',
@@ -19,59 +18,15 @@ const KEYS = new Set([
19
18
  'Enter',
20
19
  ' ',
21
20
  ]);
22
- /**
23
- * @summary A tree of items, navigable entirely from the keyboard.
24
- *
25
- * @description
26
- * Focus is a single tab stop, moved with the arrow keys, which is what the tree
27
- * pattern requires: a sidebar of forty items should not be forty presses of Tab
28
- * to get past. The tree owns which item is the tab stop and moves it as focus
29
- * moves, so the browser's own focus order is never fought with.
30
- *
31
- * Items are found with `instanceof`, never by tag name, so the headless tier
32
- * does not learn about the styled one.
33
- *
34
- * @since 0.1.0
35
- * @status experimental
36
- * @category navigation
37
- * @aliases FileTree, Navigation tree
38
- * @documentation https://helix.fusion.dev/components/tree
39
- *
40
- * @slot - The tree's items.
41
- *
42
- * @event {HelixActivateEvent} helix-activate - Fired once the current one has moved, so the element already holds it.
43
- * @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the current node moves, and cancelable. The only way to refuse one.
44
- * @event {HelixSelectEvent} helix-select - Fired when an item is chosen. Dispatched by the item and listened for here, because the tree is what a consumer holds a reference to. Cancelable: refusing it takes the decision and nothing moves.
45
- *
46
- * @csspart empty - Shown when there are no rows to show.
47
- * @csspart loading - Shown while the rows are still on their way.
48
- * @csspart tree - The tree container.
49
- */
50
21
  let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinControllerActive(HelixElement)) {
51
22
  constructor() {
52
23
  super(...arguments);
53
- /**
54
- * Accessible name for the tree.
55
- */
56
24
  this.label = 'Tree';
57
- /* Through the mixin, so a consumer can refuse the change. Writing the value
58
- straight from the event makes `helix-before-activate` an announcement
59
- nobody can answer. */
60
25
  this.handleSelection = (event) => {
61
26
  this.requestActive(event.detail.value);
62
27
  this.syncItems();
63
28
  };
64
29
  this.handleKeydown = (event) => {
65
- /* The innermost item the press came from, which `find` cannot give.
66
- *
67
- * `items` is every tree item in document order and a nested one is inside
68
- * all of its ancestors, so `entry.contains(target)` matches the outermost
69
- * ancestor first and every key pressed on a nested node moves the root of
70
- * its branch instead. The shallow cases all look right.
71
- *
72
- * `closestInstance` walks up by identity and crosses shadow roots on the
73
- * way, which is the util that exists for exactly this. `closest('*')` is a
74
- * no-op: it returns the element it was called on. */
75
30
  const current = closestInstance(event.target, HelixHeadlessTreeItem);
76
31
  if (!current || !this.items.includes(current) || !KEYS.has(event.key)) {
77
32
  return;
@@ -89,8 +44,6 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
89
44
  this.focusOn(this.visibleItems.at(-1));
90
45
  }
91
46
  else if (event.key === 'ArrowRight') {
92
- // Open a closed branch, then step into it. Two presses rather than one
93
- // that does both, which is what the pattern specifies.
94
47
  if (current.isBranch && !current.open) {
95
48
  current.requestOpen(true);
96
49
  this.syncItems();
@@ -115,43 +68,18 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
115
68
  this.syncItems();
116
69
  }
117
70
  else {
118
- /* `activate`, not `select`. Announcing the choice and stopping there
119
- leaves a tree used as navigation walkable with the arrows and never
120
- enterable: the pointer has a link under it and the keyboard has
121
- nothing. */
122
71
  current.activate();
123
72
  }
124
73
  }
125
74
  event.preventDefault();
126
75
  };
127
- /**
128
- * A branch opened or closed, whoever did it.
129
- *
130
- * Listening for the item's own open events rather than calling `syncItems`
131
- * from each keyboard path: that misses the pointer, and collapsing a branch
132
- * by clicking its chevron then leaves the tree's one tab stop on a row inside
133
- * the subtree just hidden. It also covers a consumer setting `open` directly.
134
- */
135
76
  this.handleBranch = () => {
136
77
  this.syncItems();
137
78
  };
138
79
  }
139
- /**
140
- * Every item, in the order a reader moving down would meet them.
141
- *
142
- * Selected by tag, never by `'*'` and a filter: this is the array a single
143
- * arrow press rebuilds, and `'*'` makes it every descendant element in the
144
- * tree, labels and icons and whatever a consumer slotted into a row included.
145
- *
146
- * The two tag names rather than `instanceof` on everything: a styled
147
- * `helix-tree-item` extends the base, so both names are needed, and the
148
- * `instanceof` stays as the type guard because a consumer can write either
149
- * tag and neither selector proves what the element is.
150
- */
151
80
  get items() {
152
81
  return [...this.querySelectorAll('helix-tree-item, helix-headless-tree-item')].filter((node) => node instanceof HelixHeadlessTreeItem);
153
82
  }
154
- /** Items a reader can actually reach: not inside a collapsed branch. */
155
83
  get visibleItems() {
156
84
  return this.items.filter((item) => {
157
85
  let parent = item.parentElement;
@@ -164,23 +92,9 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
164
92
  return true;
165
93
  });
166
94
  }
167
- /**
168
- * Pushes tree-owned state down: which item is selected, which holds the tab
169
- * stop, and how deep each one sits.
170
- *
171
- * Depth is computed here rather than by each item counting its own ancestors,
172
- * so an item moved in the tree gets the right indent without knowing it moved.
173
- */
174
95
  syncItems() {
175
96
  const visible = this.visibleItems;
176
- /* Through the mixin, which resolves an exact match and otherwise falls back
177
- to the first item. No disabled rows are skipped, because `Tree.Item` is
178
- the only selectable row in the library with no `disabled` property. */
179
97
  const chosen = this.resolveActive(visible);
180
- /* The keyboard keeps its place: the stop moves only when the row holding it
181
- is no longer reachable. Bound to the selection instead, a branch opening
182
- or a child arriving throws the reader back to the selected row from
183
- wherever they had arrowed to. */
184
98
  const held = visible.find((item) => item.focusable);
185
99
  const stop = held ?? chosen;
186
100
  for (const item of this.items) {
@@ -211,12 +125,6 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
211
125
  entry.focusable = entry === item;
212
126
  }
213
127
  item.focusItem();
214
- /* And bring it into view, or focus lands below the fold and the tree stays
215
- where it is.
216
-
217
- Not `scrollIntoView`, which scrolls every scrollable ancestor including
218
- the page. A reader who pressed Down in a sidebar did not ask the document
219
- to move. */
220
128
  scrollItemIntoView(item, { padding: 4 });
221
129
  }
222
130
  connectedCallback() {
@@ -225,11 +133,6 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
225
133
  this.addEventListener('helix-before-open', this.handleBranch);
226
134
  this.addEventListener('helix-before-close', this.handleBranch);
227
135
  this.addEventListener('keydown', this.handleKeydown);
228
- /* Nested items arrive on a *branch's* slot, not on this element's, so the
229
- root's `slotchange` never fires for them: without this, a subtree
230
- rendered after first paint gets no `level`, no `selected` and no
231
- `focusable`, and sits at the wrong indent with no way into it. A
232
- framework filling a branch on demand is exactly that case. */
233
136
  this.watcher = new MutationObserver(() => this.syncItems());
234
137
  this.watcher.observe(this, { childList: true, subtree: true });
235
138
  }
@@ -9,73 +9,20 @@ import { arrowDown, arrowUp, minus } from '@fusion.dev/helix-icons';
9
9
  import { html } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
- /** The three directions a measurement can have moved, and how each is drawn. */
13
12
  const DIRECTIONS = {
14
13
  down: { icon: arrowDown, word: 'Down' },
15
14
  neutral: { icon: minus, word: 'Unchanged' },
16
15
  up: { icon: arrowUp, word: 'Up' },
17
16
  };
18
- /**
19
- * Reads the direction, treating an absent or unrecognised one as neutral.
20
- *
21
- * The cast is a lie the fallback catches: an attribute is a string and a
22
- * consumer can write anything into it, so an unrecognised word has to draw
23
- * something rather than fail to index and throw inside `render`.
24
- */
25
17
  const readDirection = (value) => DIRECTIONS[value] ?? DIRECTIONS.neutral;
26
- /**
27
- * @summary Which way a measurement moved, and whether that is good news.
28
- *
29
- * @description
30
- * Three parts: a direction that is up, down or neutral, the metric that moved,
31
- * and a label saying over what. The arrow carries the direction and its color
32
- * carries the judgment, and the two are separate properties because they are
33
- * separate facts.
34
- *
35
- * **`good` is absent by default, so an unjudged trend is gray.** Every
36
- * dashboard convention says up is green, and for churn, latency, error rate,
37
- * cost and bounce rate that convention is a lie told in the most legible color
38
- * on the page. State the metric's polarity once and the component does the part
39
- * that varies.
40
- *
41
- * The direction is also written as a word, because a colored arrow says
42
- * nothing to a reader who is not looking at it. `part="direction"` is what the
43
- * styled tier clips out of sight and leaves in the accessibility tree.
44
- *
45
- * Both the metric and the label are bare slots. An element whose only job is to
46
- * assign itself a slot on connect gives its own host an attribute after
47
- * upgrade, which a React consumer's server markup never had.
48
- *
49
- * @since 0.1.0
50
- * @status stable
51
- * @category charts
52
- * @aliases Delta, Change, Movement
53
- * @documentation https://helix.fusion.dev/components/trend
54
- *
55
- * @slot - The metric that moved.
56
- * @slot label - What it moved over.
57
- *
58
- * @csspart direction - The direction as a word, for readers who are not looking.
59
- * @csspart icon - The arrow.
60
- */
61
18
  let HelixHeadlessTrend = class HelixHeadlessTrend extends HelixElement {
62
19
  render() {
63
20
  const direction = readDirection(this.direction);
64
- /* Silent on a neutral move, whatever `good` says. Neither better nor worse
65
- is a reading of "unchanged" that the word already carries. */
66
21
  const judgment = this.good && this.direction && this.direction !== 'neutral'
67
22
  ? this.good === this.direction
68
23
  ? ', better'
69
24
  : ', worse'
70
25
  : '';
71
- /* One interpolation rather than two, so the phrase is one text node.
72
- Lit SSR writes a `<!--lit-part-->` marker around every binding, and two
73
- bindings put a pair of comments between "Up" and ", worse" in the served
74
- markup, which a spec then cannot assert in one string. */
75
- /* Written out rather than through `drawIcon`, because this one needs a
76
- `part` and a part behind an interpolation is one the manifest cannot see:
77
- the helper's `part=${…}` binding took `[part="icon"]` out of the
78
- stylesheet's reach. */
79
26
  return html `<svg
80
27
  aria-hidden="true"
81
28
  data-glyph
@@ -26,19 +26,15 @@ import '../../menu/index.js';
26
26
  * The keys are the ones every player uses: Space and `k` to play or pause, the
27
27
  * arrows to seek and to change the volume, `m` to mute, `f` for fullscreen.
28
28
  * They are bound on this element rather than on the document, because a video
29
- * is not a palette: keys belong to it only while a reader is on it.
29
+ * is not a palette: keys belong to it only while a reader is on it. Where
30
+ * `controls` is set they are not bound at all, since two handlers on one press
31
+ * is a video that seeks twice, and the focus stop goes with them.
30
32
  *
31
- * **And none of them where `controls` is set.** The browser's own bar owns
32
- * those keys, and two handlers on one press is a video that seeks twice or
33
- * plays and immediately pauses. The focus stop goes with them, since the native
34
- * controls bring their own.
35
- *
36
- * **`<source>` and `<track>` are copied into the video, not slotted into it.**
37
- * A media element runs its resource selection over its own child *nodes*, and a
38
- * slot is one node: the sources and the caption tracks a consumer wrote were
39
- * assigned into the flattened tree and the video never saw any of them. It had
40
- * no text tracks, no current source and a `readyState` of zero, so a video
41
- * given sources this way did not merely lack captions, it did not play.
33
+ * **`<source>` and `<track>` are copied into the video, never slotted into it.**
34
+ * A media element runs resource selection over its own child *nodes*, and a slot
35
+ * is one node, so slotted sources reach it as nothing: no text tracks, no
36
+ * current source, `readyState` zero. A video given sources that way does not
37
+ * merely lack captions, it does not play.
42
38
  *
43
39
  * They are copied rather than moved, because the consumer's light DOM is theirs
44
40
  * and a framework that re-renders it would find its children gone. The copies