@fusion.dev/helix-headless 0.43.0 → 0.45.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 (34) hide show
  1. package/custom-elements.json +60 -45
  2. package/dist/chat/composer/chat-composer.d.ts +1 -1
  3. package/dist/chat/message/chat-message.d.ts +1 -1
  4. package/dist/chat/message/chat-message.js +1 -1
  5. package/dist/chat/root/chat-root.d.ts +1 -1
  6. package/dist/element-events.generated.js +0 -1
  7. package/dist/filter-input/chip/filter-chip.d.ts +1 -1
  8. package/dist/filter-input/field/filter-field.d.ts +1 -1
  9. package/dist/filter-input/menu/filter-menu.d.ts +1 -1
  10. package/dist/filter-input/operator/filter-operator.d.ts +1 -1
  11. package/dist/filter-input/root/filter-input-root.d.ts +1 -1
  12. package/dist/notifications/group/notification-group.d.ts +17 -3
  13. package/dist/notifications/group/notification-group.js +8 -11
  14. package/dist/notifications/notification/notification.d.ts +3 -1
  15. package/dist/notifications/notification/notification.js +1 -1
  16. package/dist/notifications/root/notifications-root.d.ts +8 -3
  17. package/dist/option/group/option-group.d.ts +24 -1
  18. package/dist/option/group/option-group.js +8 -0
  19. package/dist/option/root/option-root.d.ts +1 -1
  20. package/dist/radio/group/radio-group.d.ts +1 -1
  21. package/dist/radio/group/radio-group.js +4 -8
  22. package/dist/radio/root/radio-root.d.ts +18 -1
  23. package/dist/radio/root/radio-root.js +5 -1
  24. package/dist/skip-link/root/skip-link-root.d.ts +1 -1
  25. package/dist/speed-dial/item/speed-dial-item.d.ts +1 -1
  26. package/dist/speed-dial/root/speed-dial-root.d.ts +1 -1
  27. package/dist/splitter/root/splitter-root.d.ts +26 -3
  28. package/dist/splitter/root/splitter-root.js +4 -3
  29. package/dist/toast/item/toast-item.d.ts +1 -1
  30. package/dist/toast/root/toast-root.d.ts +2 -2
  31. package/dist/tour/root/tour-root.d.ts +0 -1
  32. package/package.json +5 -5
  33. package/types/jsx.d.ts +68 -14
  34. package/types/vue.d.ts +17 -4
@@ -11148,7 +11148,7 @@
11148
11148
  "helix": {
11149
11149
  "category": "forms",
11150
11150
  "since": "0.1.0",
11151
- "status": "experimental",
11151
+ "status": "stable",
11152
11152
  "documentation": "https://fusion.dev/helix/components/chat",
11153
11153
  "aliases": [
11154
11154
  "Message box",
@@ -11882,7 +11882,7 @@
11882
11882
  "helix": {
11883
11883
  "category": "feedback",
11884
11884
  "since": "0.1.0",
11885
- "status": "experimental",
11885
+ "status": "stable",
11886
11886
  "documentation": "https://fusion.dev/helix/components/chat",
11887
11887
  "aliases": [
11888
11888
  "Message",
@@ -13286,7 +13286,7 @@
13286
13286
  "helix": {
13287
13287
  "category": "feedback",
13288
13288
  "since": "0.1.0",
13289
- "status": "experimental",
13289
+ "status": "stable",
13290
13290
  "documentation": "https://fusion.dev/helix/components/chat",
13291
13291
  "aliases": [
13292
13292
  "Conversation",
@@ -38128,7 +38128,7 @@
38128
38128
  "helix": {
38129
38129
  "category": "forms",
38130
38130
  "since": "0.23.0",
38131
- "status": "experimental",
38131
+ "status": "stable",
38132
38132
  "documentation": "https://fusion.dev/helix/components/filter-input",
38133
38133
  "aliases": [
38134
38134
  "FilterToken",
@@ -38596,7 +38596,7 @@
38596
38596
  "helix": {
38597
38597
  "category": "forms",
38598
38598
  "since": "0.23.0",
38599
- "status": "experimental",
38599
+ "status": "stable",
38600
38600
  "documentation": "https://fusion.dev/helix/components/filter-input",
38601
38601
  "aliases": [
38602
38602
  "Facet",
@@ -39325,7 +39325,7 @@
39325
39325
  "helix": {
39326
39326
  "category": "forms",
39327
39327
  "since": "0.25.0",
39328
- "status": "experimental",
39328
+ "status": "stable",
39329
39329
  "documentation": "https://fusion.dev/helix/components/filter-input",
39330
39330
  "aliases": [
39331
39331
  "FacetPicker",
@@ -39789,7 +39789,7 @@
39789
39789
  "helix": {
39790
39790
  "category": "forms",
39791
39791
  "since": "0.23.0",
39792
- "status": "experimental",
39792
+ "status": "stable",
39793
39793
  "documentation": "https://fusion.dev/helix/components/filter-input",
39794
39794
  "aliases": [
39795
39795
  "Comparator",
@@ -41812,7 +41812,7 @@
41812
41812
  "helix": {
41813
41813
  "category": "forms",
41814
41814
  "since": "0.1.0",
41815
- "status": "experimental",
41815
+ "status": "stable",
41816
41816
  "documentation": "https://fusion.dev/helix/components/filter-input",
41817
41817
  "aliases": [
41818
41818
  "SearchField",
@@ -57791,6 +57791,16 @@
57791
57791
  "description": "How many the group says it stands for, which is the count or the rows.",
57792
57792
  "readonly": true
57793
57793
  },
57794
+ {
57795
+ "kind": "field",
57796
+ "name": "counted",
57797
+ "type": {
57798
+ "text": "number | undefined"
57799
+ },
57800
+ "privacy": "private",
57801
+ "description": "The same number where anything can know it, and nothing where nothing can.\n\n**A server has no rows to count and must not say zero.** `items` reads\n`knownChildren`, which is empty on a server and while hydrating what one\nsent, so a group written around three notifications with no `count` of its\nown served `<span part=\"count\">0</span>`: a crawler, and a reader before the\npage has run, given a group that says it holds nothing over the three rows\nunderneath it. Not knowing is not zero, which is the rule `MixinCollection`\nalready states about an empty list.\n\nA declared `count` is a fact about the group rather than about its children,\nso it serves whole, which is the shape a feed trimmed by a server already\nwants: it is the number the rows were cut from.",
57802
+ "readonly": true
57803
+ },
57794
57804
  {
57795
57805
  "kind": "method",
57796
57806
  "name": "assign",
@@ -57808,16 +57818,6 @@
57808
57818
  "privacy": "private",
57809
57819
  "readonly": true
57810
57820
  },
57811
- {
57812
- "kind": "field",
57813
- "name": "folded",
57814
- "type": {
57815
- "text": "number"
57816
- },
57817
- "privacy": "private",
57818
- "description": "How many rows the collapse is hiding, for the control to say so.",
57819
- "readonly": true
57820
- },
57821
57821
  {
57822
57822
  "kind": "field",
57823
57823
  "name": "open",
@@ -58348,7 +58348,7 @@
58348
58348
  "helix": {
58349
58349
  "category": "feedback",
58350
58350
  "since": "0.24.0",
58351
- "status": "experimental",
58351
+ "status": "stable",
58352
58352
  "documentation": "https://fusion.dev/helix/components/notifications",
58353
58353
  "aliases": [
58354
58354
  "NotificationSection",
@@ -58481,6 +58481,10 @@
58481
58481
  "description": "A block belonging to the row rather than a line about it: a quoted comment, a status moving from one value to another, a preview. Unlike `caption` nothing here sets a size or an ink, because whatever fills it usually brings its own.",
58482
58482
  "name": "detail"
58483
58483
  },
58484
+ {
58485
+ "description": "Replaces the glyph inside the dismiss control, which is drawn only where the row is `dismissible`.",
58486
+ "name": "dismiss"
58487
+ },
58484
58488
  {
58485
58489
  "description": "An avatar or icon identifying who or what it came from.",
58486
58490
  "name": "media"
@@ -59058,7 +59062,7 @@
59058
59062
  "helix": {
59059
59063
  "category": "feedback",
59060
59064
  "since": "0.1.0",
59061
- "status": "experimental",
59065
+ "status": "stable",
59062
59066
  "documentation": "https://fusion.dev/helix/components/notifications",
59063
59067
  "aliases": [
59064
59068
  "Notification item",
@@ -59283,7 +59287,7 @@
59283
59287
  "text": "void"
59284
59288
  }
59285
59289
  },
59286
- "description": "Says whether anything in the feed has media, so every item can hold the\ncolumn open whether or not it has one of its own.\n\n**A fact no item can answer for itself**, which is why it is here. An item\nknows about its own media slot and nothing about its neighbors, so a feed\nwhere some rows have an avatar and some do not draws two different left\nedges for its text: measured on a four item feed, the rows without media\nstarted their text 32 pixels to the left of the rows with it. The styled\ntier turns this into a width; this tier ships no stylesheet and is\nunaffected.\n\nRead off the light DOM rather than off the items, so it does not depend on\nwhich children have upgraded by the time this runs."
59290
+ "description": "Says whether anything in the feed has media, so every item can hold the\ncolumn open whether or not it has one of its own.\n\n**A fact no item can answer for itself**, which is why it is here. An item\nknows about its own media slot and nothing about its neighbors, so a feed\nwhere some rows have an avatar and some do not draws two different left\nedges for its text: measured on a four item feed, the rows without media\nstarted their text 32 pixels to the left of the rows with it. The styled\ntier turns this into a width; this tier ships no stylesheet and is\nunaffected.\n\n**What is asked of each row is its light DOM, not the row.** A notification\npublishes no property for this, and asking one would make the answer depend\non the row having drawn itself; a `[slot=\"media\"]` child is there from the\nmoment the markup is parsed. The rows themselves come from `items`, which\ndoes need them upgraded, and that is the honest limit rather than a claim\nthis had made the other way: a row that upgrades late is counted on the next\nupdate, which its own `slotchange` asks for."
59287
59291
  },
59288
59292
  {
59289
59293
  "kind": "method",
@@ -59917,7 +59921,7 @@
59917
59921
  "helix": {
59918
59922
  "category": "feedback",
59919
59923
  "since": "0.1.0",
59920
- "status": "experimental",
59924
+ "status": "stable",
59921
59925
  "documentation": "https://fusion.dev/helix/components/notifications",
59922
59926
  "aliases": [
59923
59927
  "Notification feed",
@@ -62509,6 +62513,17 @@
62509
62513
  "description": "The heading over the run.",
62510
62514
  "attribute": "label"
62511
62515
  },
62516
+ {
62517
+ "kind": "method",
62518
+ "name": "nameFromHeading",
62519
+ "privacy": "private",
62520
+ "return": {
62521
+ "type": {
62522
+ "text": "void"
62523
+ }
62524
+ },
62525
+ "description": "Names the run after the heading it drew, whatever the heading is made of.\n\n**The string alone was not the name of the heading, it was the name of one\nway of writing it.** `label` reached `ariaLabel` and the slot reached the\nscreen, so a consumer who took the documented `label` slot for a heading\nricher than a string got a `role=\"group\"` with no accessible name at all,\nand the words they wrote were the one thing a reader could not hear: the\nheading carries `aria-hidden`, because the name was supposed to be on the\nhost already. Set both and they disagreed, the host saying the string and\nthe panel showing the slot.\n\nSo the host points at the heading, which is the one thing that is always\nwhat a reader sees. `ElementInternals` and not an IDREF, because the host\nis in the light DOM and the heading is in this shadow root, which is the\nboundary `aria-labelledby` cannot cross. `MixinField.linkHost` does the\nsame for a field's label and records the measurement.\n\nClient only. Element references need the nodes to exist, and the server has\nthe string."
62526
+ },
62512
62527
  {
62513
62528
  "kind": "field",
62514
62529
  "name": "tier",
@@ -62852,7 +62867,7 @@
62852
62867
  "summary": "A named run of options inside a listbox.",
62853
62868
  "helix": {
62854
62869
  "since": "0.1.0",
62855
- "status": "experimental",
62870
+ "status": "stable",
62856
62871
  "documentation": "https://fusion.dev/helix/components/option",
62857
62872
  "aliases": [
62858
62873
  "OptGroup",
@@ -63528,7 +63543,7 @@
63528
63543
  "helix": {
63529
63544
  "category": "forms",
63530
63545
  "since": "0.1.0",
63531
- "status": "experimental",
63546
+ "status": "stable",
63532
63547
  "documentation": "https://fusion.dev/helix/components/option",
63533
63548
  "aliases": [
63534
63549
  "Item",
@@ -71386,7 +71401,7 @@
71386
71401
  "summary": "A set of choices where exactly one is the answer.",
71387
71402
  "helix": {
71388
71403
  "since": "0.1.0",
71389
- "status": "experimental",
71404
+ "status": "stable",
71390
71405
  "documentation": "https://fusion.dev/helix/components/radio",
71391
71406
  "aliases": [
71392
71407
  "RadioButtons",
@@ -71500,6 +71515,15 @@
71500
71515
  },
71501
71516
  "description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so the declaration is the\nattribute and the state is the property.\n\n**The attribute, read when it is asked for, and never a snapshot taken in\nthe constructor.** A field initializer runs there, and an element the HTML\nparser created has none of its attributes yet at that moment: measured on\nthis element, on `Option` and on `Toggle`, `<helix-radio value=\"a\"\nselected>` reported `defaultSelected` as `false` both from parsed markup\nand from `createElement` followed by `setAttribute`, which is every way a\nconsumer writes one. The snapshot could only ever be true for an element\nalready in the document when `customElements.define` ran.\n\nNothing overwrites it either, which is what makes reading it late correct\nrather than merely later: `selected` does not reflect, for the reason\n`willUpdate` records, so the group's writes never reach the attribute and\nwhat it says is what the markup said.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
71502
71517
  },
71518
+ {
71519
+ "kind": "field",
71520
+ "name": "focusable",
71521
+ "type": {
71522
+ "text": "boolean"
71523
+ },
71524
+ "default": "false",
71525
+ "description": "Whether this choice holds the group's tab stop while nothing is chosen.\n\nThe stop rides on the selection, and an unanswered group has no selection,\nso without this every choice is `tabindex=\"-1\"` and the keyboard cannot\nenter the set at all.\n\n**Written by the group, with no attribute, and it reaches the control\nrather than the host.** A `tabindex` on the host is two faults at once: it\nis markup a consumer rendered and their server never sent, which React\nreports as a mismatch and does not patch up, and it puts the tab stop on\nthe element rather than on the `<button role=\"radio\">` inside it, so a\nscreen reader meets an unnamed box where a native group would have given\nthem an unchecked radio. `Toggle.Root` carries the same property for the\nsame reason."
71526
+ },
71503
71527
  {
71504
71528
  "kind": "field",
71505
71529
  "name": "selected",
@@ -71932,7 +71956,7 @@
71932
71956
  "helix": {
71933
71957
  "category": "forms",
71934
71958
  "since": "0.1.0",
71935
- "status": "experimental",
71959
+ "status": "stable",
71936
71960
  "documentation": "https://fusion.dev/helix/components/radio",
71937
71961
  "aliases": [
71938
71962
  "Option",
@@ -77270,7 +77294,7 @@
77270
77294
  "helix": {
77271
77295
  "category": "navigation",
77272
77296
  "since": "0.1.0",
77273
- "status": "experimental",
77297
+ "status": "stable",
77274
77298
  "documentation": "https://fusion.dev/helix/components/skip-link",
77275
77299
  "aliases": [
77276
77300
  "Skip navigation",
@@ -79818,7 +79842,7 @@
79818
79842
  "helix": {
79819
79843
  "category": "actions",
79820
79844
  "since": "0.1.0",
79821
- "status": "experimental",
79845
+ "status": "stable",
79822
79846
  "documentation": "https://fusion.dev/helix/components/speed-dial",
79823
79847
  "aliases": [
79824
79848
  "Speed dial action",
@@ -80555,7 +80579,7 @@
80555
80579
  "helix": {
80556
80580
  "category": "actions",
80557
80581
  "since": "0.1.0",
80558
- "status": "experimental",
80582
+ "status": "stable",
80559
80583
  "documentation": "https://fusion.dev/helix/components/speed-dial",
80560
80584
  "aliases": [
80561
80585
  "FAB",
@@ -81062,7 +81086,7 @@
81062
81086
  }
81063
81087
  }
81064
81088
  ],
81065
- "description": "Asks to move, and applies it where the position is this element's to move.\n\nNot announced per step: `helix-before-resize` is the settling, below. A drag that\nannounced every position would be asking a consumer to record a hundred\ndecisions to keep one."
81089
+ "description": "Moves the divider under a gesture that is still happening.\n\nNot announced per step: `helix-before-resize` is the settling, below. A drag that\nannounced every position would be asking a consumer to record a hundred\ndecisions to keep one.\n\n**A drag only, which is the whole of why applying before announcing is\nright here.** The divider has to follow the hand, and the one number worth\na decision is where the hand let go. Nothing discrete calls this: a key and\na double click go straight to `settle`, so their announcement comes before\ntheir change and refusing one takes the decision, and the corner's arrows\nalready did."
81066
81090
  },
81067
81091
  {
81068
81092
  "kind": "method",
@@ -81081,7 +81105,7 @@
81081
81105
  }
81082
81106
  }
81083
81107
  ],
81084
- "description": "Says the arrangement has settled, so a consumer can keep it.\n\nThrough the audited path, so a consumer who owns `position` is told what was\nasked for and decides, and one who does not has already had it applied."
81108
+ "description": "Says the arrangement has settled, so a consumer can keep it.\n\nThrough the audited path, so a consumer who owns `position` is told what was\nasked for and decides, and one who does not has already had it applied.\n\n**The second announcement is conditional on the first, because it is derived\nfrom it.** `shutAt` reads the position the gesture ended on, so a refused\n`helix-before-resize` leaves a collapse computed from a number that was\nrefused. Announced anyway, the refusal produced the outcome it was refusing:\nmeasured on a divider at 40 with a listener calling `preventDefault`, Home\nleft `position` reporting 0 and `collapsed` reporting `start`, because the\ncollapse applied and `position` reads the sliver once a pane is shut. The\nreturn of the first request is what says so, and it was being dropped: a\ncancelable event dispatched as a bare statement with nothing reading the\nanswer, which is the shape the navigation cohort named on `Outline` and left.\n\nRefusing the collapse alone still leaves the resize, which is the other way\nround and is coherent: the divider goes where the drag put it and the pane\nis not declared shut. What a consumer cannot have is a pane that collapses\nout from under a position they said no to."
81085
81109
  },
81086
81110
  {
81087
81111
  "kind": "field",
@@ -81585,7 +81609,7 @@
81585
81609
  "type": {
81586
81610
  "text": "HelixBeforeCollapseEvent"
81587
81611
  },
81588
- "description": "Fired before a pane shuts or reopens, and cancelable.",
81612
+ "description": "Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.",
81589
81613
  "name": "helix-before-collapse",
81590
81614
  "reactProp": "onhelix-before-collapse"
81591
81615
  },
@@ -81593,7 +81617,7 @@
81593
81617
  "type": {
81594
81618
  "text": "HelixBeforeResizeEvent"
81595
81619
  },
81596
- "description": "Fired before the divider settles, and cancelable.",
81620
+ "description": "Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.",
81597
81621
  "name": "helix-before-resize",
81598
81622
  "reactProp": "onhelix-before-resize"
81599
81623
  }
@@ -96635,7 +96659,7 @@
96635
96659
  "summary": "One message in the toast region.",
96636
96660
  "helix": {
96637
96661
  "since": "0.1.0",
96638
- "status": "experimental",
96662
+ "status": "stable",
96639
96663
  "documentation": "https://fusion.dev/helix/components/toast",
96640
96664
  "tier": "headless",
96641
96665
  "group": "BaseToast",
@@ -97079,12 +97103,11 @@
97079
97103
  "helix": {
97080
97104
  "category": "overlays",
97081
97105
  "since": "0.1.0",
97082
- "status": "experimental",
97106
+ "status": "stable",
97083
97107
  "documentation": "https://fusion.dev/helix/components/toast",
97084
97108
  "aliases": [
97085
97109
  "Toaster",
97086
- "Snackbar",
97087
- "Notifications"
97110
+ "Snackbar"
97088
97111
  ],
97089
97112
  "tier": "headless",
97090
97113
  "group": "BaseToast",
@@ -101154,14 +101177,6 @@
101154
101177
  "description": "A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves.",
101155
101178
  "name": "helix-tour-ask",
101156
101179
  "reactProp": "onhelix-tour-ask"
101157
- },
101158
- {
101159
- "type": {
101160
- "text": "Event"
101161
- },
101162
- "description": "Says the state moved, carrying `newState` and `oldState`.",
101163
- "name": "toggle",
101164
- "reactProp": "ontoggle"
101165
101180
  }
101166
101181
  ],
101167
101182
  "attributes": [
@@ -19,7 +19,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
19
19
  * of what the reader wrote.
20
20
  *
21
21
  * @since 0.1.0
22
- * @status experimental
22
+ * @status stable
23
23
  * @category forms
24
24
  * @aliases Message box, Prompt input
25
25
  * @documentation https://fusion.dev/helix/components/chat
@@ -23,7 +23,7 @@ export declare const FAILED_LABEL = "Not sent";
23
23
  * completion is the only version of this that works.
24
24
  *
25
25
  * @since 0.1.0
26
- * @status experimental
26
+ * @status stable
27
27
  * @category feedback
28
28
  * @aliases Message, Bubble, Turn
29
29
  * @documentation https://fusion.dev/helix/components/chat
@@ -40,7 +40,7 @@ let HelixHeadlessChatMessage = class HelixHeadlessChatMessage extends HelixEleme
40
40
  this.clock = Date.now();
41
41
  }
42
42
  hostAria(this.internals, { role: 'article' });
43
- this.toggleAttribute('aria-busy', this.state === 'streaming');
43
+ this.internals.ariaBusy = this.state === 'streaming' ? 'true' : null;
44
44
  }
45
45
  renderTime() {
46
46
  const moment = this.moment;
@@ -69,7 +69,7 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
69
69
  * turns as children.
70
70
  *
71
71
  * @since 0.1.0
72
- * @status experimental
72
+ * @status stable
73
73
  * @category feedback
74
74
  * @aliases Conversation, Transcript, Thread, Messages
75
75
  * @documentation https://fusion.dev/helix/components/chat
@@ -138,7 +138,6 @@ registerElementEvents({
138
138
  'helix-before-close',
139
139
  'helix-complete',
140
140
  'helix-tour-ask',
141
- 'toggle',
142
141
  ],
143
142
  'helix-headless-tour-stop': ['helix-tour-ask'],
144
143
  'helix-headless-tree': ['helix-activate', 'helix-before-activate', 'helix-select'],
@@ -37,7 +37,7 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
37
37
  * gives: what the words mean is the domain's.
38
38
  *
39
39
  * @since 0.23.0
40
- * @status experimental
40
+ * @status stable
41
41
  * @category forms
42
42
  * @aliases FilterToken, FacetChip
43
43
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -43,7 +43,7 @@ import type { HelixHeadlessFilterOperator } from '../operator/filter-operator.js
43
43
  * what fills a chip then is whatever the consumer types.
44
44
  *
45
45
  * @since 0.23.0
46
- * @status experimental
46
+ * @status stable
47
47
  * @category forms
48
48
  * @aliases Facet, Dimension
49
49
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -50,7 +50,7 @@ import '../../menu/index.js';
50
50
  * rather than inventing one. Refusing `helix-facet` refuses the change.
51
51
  *
52
52
  * @since 0.25.0
53
- * @status experimental
53
+ * @status stable
54
54
  * @category forms
55
55
  * @aliases FacetPicker, FilterBuilder
56
56
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -23,7 +23,7 @@ import { nothing, type PropertyValues } from 'lit';
23
23
  * consumer bug and the field reports it rather than guessing.
24
24
  *
25
25
  * @since 0.23.0
26
- * @status experimental
26
+ * @status stable
27
27
  * @category forms
28
28
  * @aliases Comparator, Predicate
29
29
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -83,7 +83,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
83
83
  * and the chip goes because the filter went.
84
84
  *
85
85
  * @since 0.1.0
86
- * @status experimental
86
+ * @status stable
87
87
  * @category forms
88
88
  * @aliases SearchField, FacetInput, TokenInput
89
89
  * @documentation https://fusion.dev/helix/components/filter-input
@@ -27,7 +27,7 @@ declare const HelixHeadlessNotificationGroup_base: (new (...args: any[]) => impo
27
27
  * Left off, it is the number of rows.
28
28
  *
29
29
  * @since 0.24.0
30
- * @status experimental
30
+ * @status stable
31
31
  * @category feedback
32
32
  * @aliases NotificationSection, FeedGroup
33
33
  * @documentation https://fusion.dev/helix/components/notifications
@@ -100,6 +100,22 @@ export declare class HelixHeadlessNotificationGroup extends HelixHeadlessNotific
100
100
  get items(): HelixHeadlessNotification[];
101
101
  /** How many the group says it stands for, which is the count or the rows. */
102
102
  get total(): number;
103
+ /**
104
+ * The same number where anything can know it, and nothing where nothing can.
105
+ *
106
+ * **A server has no rows to count and must not say zero.** `items` reads
107
+ * `knownChildren`, which is empty on a server and while hydrating what one
108
+ * sent, so a group written around three notifications with no `count` of its
109
+ * own served `<span part="count">0</span>`: a crawler, and a reader before the
110
+ * page has run, given a group that says it holds nothing over the three rows
111
+ * underneath it. Not knowing is not zero, which is the rule `MixinCollection`
112
+ * already states about an empty list.
113
+ *
114
+ * A declared `count` is a fact about the group rather than about its children,
115
+ * so it serves whole, which is the shape a feed trimmed by a server already
116
+ * wants: it is the number the rows were cut from.
117
+ */
118
+ private get counted();
103
119
  protected willUpdate(changed: PropertyValues): void;
104
120
  protected firstUpdated(changed: PropertyValues): void;
105
121
  protected updated(changed: PropertyValues): void;
@@ -119,8 +135,6 @@ export declare class HelixHeadlessNotificationGroup extends HelixHeadlessNotific
119
135
  */
120
136
  private assign;
121
137
  private readonly handleToggle;
122
- /** How many rows the collapse is hiding, for the control to say so. */
123
- private get folded();
124
138
  protected render(): TemplateResult;
125
139
  }
126
140
  declare global {
@@ -29,6 +29,9 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
29
29
  get total() {
30
30
  return this.count ?? this.items.length;
31
31
  }
32
+ get counted() {
33
+ return this.count ?? (this.childrenKnown ? this.items.length : undefined);
34
+ }
32
35
  willUpdate(changed) {
33
36
  super.willUpdate(changed);
34
37
  hostAria(this.internals, this.label === '' ? { role: 'group' } : { label: this.label, role: 'group' });
@@ -60,11 +63,9 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
60
63
  const label = this.renderRoot?.querySelector?.('slot[name="label"]');
61
64
  label?.assign?.(...[...this.children].filter((child) => child.getAttribute('slot') === 'label'));
62
65
  }
63
- get folded() {
64
- return Math.max(this.items.length - 1, 0);
65
- }
66
66
  render() {
67
- const collapsed = this.collapsible && !this.open;
67
+ const counted = this.counted;
68
+ const count = html `<span part="count">${counted ?? nothing}</span>`;
68
69
  return html `<div part="header">
69
70
  ${this.collapsible
70
71
  ? html `<button
@@ -76,17 +77,13 @@ let HelixHeadlessNotificationGroup = class HelixHeadlessNotificationGroup extend
76
77
  @click=${this.handleToggle}
77
78
  >
78
79
  <span part="label"><slot name="label">${this.label}</slot></span>
79
- <span part="count">${this.total}</span>
80
+ ${count}
80
81
  </button>`
81
82
  : html `<span part="label"><slot name="label">${this.label}</slot></span>
82
- <span part="count">${this.total}</span>`}
83
+ ${count}`}
83
84
  <span part="action"><slot name="action"></slot></span>
84
85
  </div>
85
- <div
86
- id=${ITEMS_ID}
87
- part="items"
88
- aria-label=${collapsed && this.folded > 0 ? `${this.folded} more` : nothing}
89
- >
86
+ <div id=${ITEMS_ID} part="items">
90
87
  <slot></slot>
91
88
  </div>`;
92
89
  }
@@ -30,7 +30,7 @@ import '../../format-date/index.js';
30
30
  * cancelable, and a `read` set by the consumer makes this element defer entirely.
31
31
  *
32
32
  * @since 0.1.0
33
- * @status experimental
33
+ * @status stable
34
34
  * @category feedback
35
35
  * @aliases Notification item, Inbox item
36
36
  * @documentation https://fusion.dev/helix/components/notifications
@@ -44,6 +44,8 @@ import '../../format-date/index.js';
44
44
  * @slot detail - A block belonging to the row rather than a line about it: a quoted
45
45
  * comment, a status moving from one value to another, a preview. Unlike `caption` nothing
46
46
  * here sets a size or an ink, because whatever fills it usually brings its own.
47
+ * @slot dismiss - Replaces the glyph inside the dismiss control, which is drawn
48
+ * only where the row is `dismissible`.
47
49
  * @slot media - An avatar or icon identifying who or what it came from.
48
50
  *
49
51
  * @event {HelixDismissEvent} helix-dismiss - Fired when a reader asks to remove it, and cancelable.
@@ -22,7 +22,7 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends HelixEle
22
22
  this.isNotification = true;
23
23
  this.readState = undefined;
24
24
  this.handleActions = () => {
25
- this.markControls();
25
+ this.markActions();
26
26
  };
27
27
  this.handleDismiss = (event) => {
28
28
  event.stopPropagation();
@@ -25,7 +25,7 @@ declare const HelixHeadlessNotifications_base: (new (...args: any[]) => import("
25
25
  * either of them counting.
26
26
  *
27
27
  * @since 0.1.0
28
- * @status experimental
28
+ * @status stable
29
29
  * @category feedback
30
30
  * @aliases Notification feed, Inbox, Activity
31
31
  * @documentation https://fusion.dev/helix/components/notifications
@@ -174,8 +174,13 @@ export declare class HelixHeadlessNotifications extends HelixHeadlessNotificatio
174
174
  * tier turns this into a width; this tier ships no stylesheet and is
175
175
  * unaffected.
176
176
  *
177
- * Read off the light DOM rather than off the items, so it does not depend on
178
- * which children have upgraded by the time this runs.
177
+ * **What is asked of each row is its light DOM, not the row.** A notification
178
+ * publishes no property for this, and asking one would make the answer depend
179
+ * on the row having drawn itself; a `[slot="media"]` child is there from the
180
+ * moment the markup is parsed. The rows themselves come from `items`, which
181
+ * does need them upgraded, and that is the honest limit rather than a claim
182
+ * this had made the other way: a row that upgrades late is counted on the next
183
+ * update, which its own `slotchange` asks for.
179
184
  */
180
185
  private markMedia;
181
186
  /**
@@ -13,7 +13,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
13
13
  * an option is in without the heading becoming something to land on.
14
14
  *
15
15
  * @since 0.1.0
16
- * @status experimental
16
+ * @status stable
17
17
  * @aliases OptGroup, Section
18
18
  * @documentation https://fusion.dev/helix/components/option
19
19
  *
@@ -29,6 +29,29 @@ export declare class HelixHeadlessOptionGroup extends HelixElement {
29
29
  */
30
30
  label: string;
31
31
  protected willUpdate(changed: PropertyValues): void;
32
+ protected updated(changed: PropertyValues): void;
33
+ /**
34
+ * Names the run after the heading it drew, whatever the heading is made of.
35
+ *
36
+ * **The string alone was not the name of the heading, it was the name of one
37
+ * way of writing it.** `label` reached `ariaLabel` and the slot reached the
38
+ * screen, so a consumer who took the documented `label` slot for a heading
39
+ * richer than a string got a `role="group"` with no accessible name at all,
40
+ * and the words they wrote were the one thing a reader could not hear: the
41
+ * heading carries `aria-hidden`, because the name was supposed to be on the
42
+ * host already. Set both and they disagreed, the host saying the string and
43
+ * the panel showing the slot.
44
+ *
45
+ * So the host points at the heading, which is the one thing that is always
46
+ * what a reader sees. `ElementInternals` and not an IDREF, because the host
47
+ * is in the light DOM and the heading is in this shadow root, which is the
48
+ * boundary `aria-labelledby` cannot cross. `MixinField.linkHost` does the
49
+ * same for a field's label and records the measurement.
50
+ *
51
+ * Client only. Element references need the nodes to exist, and the server has
52
+ * the string.
53
+ */
54
+ private nameFromHeading;
32
55
  protected render(): TemplateResult;
33
56
  }
34
57
  declare global {
@@ -16,6 +16,14 @@ let HelixHeadlessOptionGroup = class HelixHeadlessOptionGroup extends HelixEleme
16
16
  super.willUpdate(changed);
17
17
  hostAria(this.internals, { label: this.label, role: 'group' });
18
18
  }
19
+ updated(changed) {
20
+ super.updated(changed);
21
+ this.nameFromHeading();
22
+ }
23
+ nameFromHeading() {
24
+ const heading = this.partNamed('label');
25
+ hostAria(this.internals, { labelledBy: heading ? [heading] : [] });
26
+ }
19
27
  render() {
20
28
  return html `<span part="label" aria-hidden="true">
21
29
  <slot name="label">${this.label}</slot>
@@ -41,7 +41,7 @@ export declare const optionsAmong: (elements: readonly Element[]) => HelixHeadle
41
41
  * question and differs between a select and a command palette.
42
42
  *
43
43
  * @since 0.1.0
44
- * @status experimental
44
+ * @status stable
45
45
  * @category forms
46
46
  * @aliases Item, Choice
47
47
  * @documentation https://fusion.dev/helix/components/option
@@ -26,7 +26,7 @@ declare const HelixHeadlessRadioGroup_base: (new (...args: any[]) => import("@fu
26
26
  * anything is chosen.
27
27
  *
28
28
  * @since 0.1.0
29
- * @status experimental
29
+ * @status stable
30
30
  * @aliases RadioButtons, Choice group
31
31
  * @documentation https://fusion.dev/helix/components/radio
32
32
  *
@@ -29,17 +29,13 @@ let HelixHeadlessRadioGroup = class HelixHeadlessRadioGroup extends MixinControl
29
29
  }
30
30
  place() {
31
31
  const items = this.items;
32
+ const answer = items.find((item) => item.value === this.value);
32
33
  for (const item of items) {
33
- item.selected = item.value === this.value;
34
+ item.selected = item === answer;
34
35
  }
35
- const entered = this.value === '' && items.length > 0 ? items[0] : undefined;
36
+ const entry = answer ? undefined : items.find((item) => !item.disabled);
36
37
  for (const item of items) {
37
- if (item === entered) {
38
- item.setAttribute('tabindex', '0');
39
- }
40
- else {
41
- item.removeAttribute('tabindex');
42
- }
38
+ item.focusable = item === entry;
43
39
  }
44
40
  }
45
41
  sync() {
@@ -20,7 +20,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
20
20
  * be a second source of truth for which one is on.
21
21
  *
22
22
  * @since 0.1.0
23
- * @status experimental
23
+ * @status stable
24
24
  * @category forms
25
25
  * @aliases Option, Choice
26
26
  * @documentation https://fusion.dev/helix/components/radio
@@ -75,6 +75,23 @@ export declare class HelixHeadlessRadio extends HelixElement {
75
75
  */
76
76
  get defaultSelected(): boolean;
77
77
  set defaultSelected(next: boolean);
78
+ /**
79
+ * Whether this choice holds the group's tab stop while nothing is chosen.
80
+ *
81
+ * The stop rides on the selection, and an unanswered group has no selection,
82
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
83
+ * enter the set at all.
84
+ *
85
+ * **Written by the group, with no attribute, and it reaches the control
86
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
87
+ * is markup a consumer rendered and their server never sent, which React
88
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
89
+ * the element rather than on the `<button role="radio">` inside it, so a
90
+ * screen reader meets an unnamed box where a native group would have given
91
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
92
+ * same reason.
93
+ */
94
+ focusable: boolean;
78
95
  /**
79
96
  * Whether this is the current choice.
80
97
  *
@@ -11,6 +11,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
11
11
  constructor() {
12
12
  super(...arguments);
13
13
  this.disabled = false;
14
+ this.focusable = false;
14
15
  this.selected = false;
15
16
  this.value = '';
16
17
  }
@@ -40,7 +41,7 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
40
41
  role="radio"
41
42
  aria-checked=${this.selected ? 'true' : 'false'}
42
43
  ?disabled=${this.disabled}
43
- tabindex=${this.selected ? '0' : '-1'}
44
+ tabindex=${this.selected || this.focusable ? '0' : '-1'}
44
45
  @click=${() => this.select()}
45
46
  >
46
47
  <span part="box"><span part="mark" aria-hidden="true"></span></span>
@@ -52,6 +53,9 @@ let HelixHeadlessRadio = class HelixHeadlessRadio extends HelixElement {
52
53
  __decorate([
53
54
  property({ reflect: true, type: Boolean })
54
55
  ], HelixHeadlessRadio.prototype, "disabled", void 0);
56
+ __decorate([
57
+ property({ attribute: false })
58
+ ], HelixHeadlessRadio.prototype, "focusable", void 0);
55
59
  __decorate([
56
60
  property({ type: Boolean })
57
61
  ], HelixHeadlessRadio.prototype, "selected", void 0);
@@ -32,7 +32,7 @@ import { type TemplateResult } from 'lit';
32
32
  * broken.
33
33
  *
34
34
  * @since 0.1.0
35
- * @status experimental
35
+ * @status stable
36
36
  * @category navigation
37
37
  * @aliases Skip navigation, Skip to content
38
38
  * @documentation https://fusion.dev/helix/components/skip-link
@@ -16,7 +16,7 @@ import { type TemplateResult } from 'lit';
16
16
  * being there, so it is read, searched and translated like anything else.
17
17
  *
18
18
  * @since 0.1.0
19
- * @status experimental
19
+ * @status stable
20
20
  * @category actions
21
21
  * @aliases Speed dial action, FAB action, Quick action
22
22
  * @documentation https://fusion.dev/helix/components/speed-dial
@@ -28,7 +28,7 @@ declare const HelixHeadlessSpeedDial_base: (new (...args: any[]) => import("@fus
28
28
  * with no reason to argue with it.
29
29
  *
30
30
  * @since 0.1.0
31
- * @status experimental
31
+ * @status stable
32
32
  * @category actions
33
33
  * @aliases FAB, Floating action button, Action button, Quick actions
34
34
  * @documentation https://fusion.dev/helix/components/speed-dial
@@ -41,8 +41,8 @@ import { type PropertyValues, type TemplateResult } from 'lit';
41
41
  * @slot end - The pane after it.
42
42
  * @slot start - The pane before the divider.
43
43
  *
44
- * @event {HelixBeforeCollapseEvent} helix-before-collapse - Fired before a pane shuts or reopens, and cancelable.
45
- * @event {HelixBeforeResizeEvent} helix-before-resize - Fired before the divider settles, and cancelable.
44
+ * @event {HelixBeforeCollapseEvent} helix-before-collapse - Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.
45
+ * @event {HelixBeforeResizeEvent} helix-before-resize - Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.
46
46
  *
47
47
  * @csspart corner - The handle where two dividers cross, added on the client.
48
48
  * @csspart end - The box holding the second.
@@ -300,11 +300,18 @@ export declare class HelixHeadlessSplitter extends HelixElement {
300
300
  */
301
301
  get vertical(): boolean;
302
302
  /**
303
- * Asks to move, and applies it where the position is this element's to move.
303
+ * Moves the divider under a gesture that is still happening.
304
304
  *
305
305
  * Not announced per step: `helix-before-resize` is the settling, below. A drag that
306
306
  * announced every position would be asking a consumer to record a hundred
307
307
  * decisions to keep one.
308
+ *
309
+ * **A drag only, which is the whole of why applying before announcing is
310
+ * right here.** The divider has to follow the hand, and the one number worth
311
+ * a decision is where the hand let go. Nothing discrete calls this: a key and
312
+ * a double click go straight to `settle`, so their announcement comes before
313
+ * their change and refusing one takes the decision, and the corner's arrows
314
+ * already did.
308
315
  */
309
316
  private move;
310
317
  /**
@@ -312,6 +319,22 @@ export declare class HelixHeadlessSplitter extends HelixElement {
312
319
  *
313
320
  * Through the audited path, so a consumer who owns `position` is told what was
314
321
  * asked for and decides, and one who does not has already had it applied.
322
+ *
323
+ * **The second announcement is conditional on the first, because it is derived
324
+ * from it.** `shutAt` reads the position the gesture ended on, so a refused
325
+ * `helix-before-resize` leaves a collapse computed from a number that was
326
+ * refused. Announced anyway, the refusal produced the outcome it was refusing:
327
+ * measured on a divider at 40 with a listener calling `preventDefault`, Home
328
+ * left `position` reporting 0 and `collapsed` reporting `start`, because the
329
+ * collapse applied and `position` reads the sliver once a pane is shut. The
330
+ * return of the first request is what says so, and it was being dropped: a
331
+ * cancelable event dispatched as a bare statement with nothing reading the
332
+ * answer, which is the shape the navigation cohort named on `Outline` and left.
333
+ *
334
+ * Refusing the collapse alone still leaves the resize, which is the other way
335
+ * round and is coherent: the divider goes where the drag put it and the pane
336
+ * is not declared shut. What a consumer cannot have is a pane that collapses
337
+ * out from under a position they said no to.
315
338
  */
316
339
  private settle;
317
340
  private readonly handlePointerDown;
@@ -82,7 +82,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
82
82
  else {
83
83
  return;
84
84
  }
85
- this.move(next);
86
85
  this.settle(next);
87
86
  event.preventDefault();
88
87
  };
@@ -300,7 +299,7 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
300
299
  settle(next) {
301
300
  const wanted = this.clamp(next);
302
301
  const shut = this.shutAt(wanted);
303
- requestChange({
302
+ const moved = requestChange({
304
303
  apply: (value) => {
305
304
  const previous = this.position;
306
305
  this.positionState = value;
@@ -310,6 +309,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
310
309
  host: this,
311
310
  next: wanted,
312
311
  });
312
+ if (!moved) {
313
+ return;
314
+ }
313
315
  if (shut !== this.collapsed) {
314
316
  requestChange({
315
317
  apply: (value) => {
@@ -334,7 +336,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
334
336
  }
335
337
  reset() {
336
338
  const home = this.clamp(this.defaultPosition);
337
- this.move(home);
338
339
  this.settle(home);
339
340
  }
340
341
  cross(by, at) {
@@ -18,7 +18,7 @@ declare const HelixHeadlessToastItem_base: (new (...args: any[]) => import("@fus
18
18
  * something.
19
19
  *
20
20
  * @since 0.1.0
21
- * @status experimental
21
+ * @status stable
22
22
  * @documentation https://fusion.dev/helix/components/toast
23
23
  *
24
24
  * @slot - The message.
@@ -29,9 +29,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
29
29
  * test, or twice.
30
30
  *
31
31
  * @since 0.1.0
32
- * @status experimental
32
+ * @status stable
33
33
  * @category overlays
34
- * @aliases Toaster, Snackbar, Notifications
34
+ * @aliases Toaster, Snackbar
35
35
  * @documentation https://fusion.dev/helix/components/toast
36
36
  *
37
37
  * @slot - The messages, as `helix-toast-item` elements.
@@ -32,7 +32,6 @@ import type { HelixHeadlessTourStop } from '../stop/tour-stop.js';
32
32
  * @event {HelixCompleteEvent} helix-complete - Fired when the last stop is
33
33
  * finished.
34
34
  * @event {HelixTourAskEvent} helix-tour-ask - A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves.
35
- * @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
36
35
  *
37
36
  * @csspart spotlight - The box over the current target, which paints the scrim.
38
37
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.43.0",
4
+ "version": "0.45.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,14 +46,14 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.43.0",
50
- "@fusion.dev/helix-icons": "0.43.0",
49
+ "@fusion.dev/helix-core": "0.45.0",
50
+ "@fusion.dev/helix-icons": "0.45.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@custom-elements-manifest/analyzer": "^0.11.0",
55
- "@fusion.dev/helix-cem": "0.43.0",
56
- "@fusion.dev/helix-testing": "0.43.0",
55
+ "@fusion.dev/helix-cem": "0.45.0",
56
+ "@fusion.dev/helix-testing": "0.45.0",
57
57
  "@fusion.dev/scripts": "0.0.0-dev",
58
58
  "@happy-dom/global-registrator": "^20.0.0",
59
59
  "@lit-labs/ssr": "^4.1.0",
package/types/jsx.d.ts CHANGED
@@ -11715,6 +11715,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
11715
11715
  Splitting them means moving what every stylesheet selects on, which is a
11716
11716
  change of its own rather than a property being added. */
11717
11717
  defaultSelected?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
11718
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
11719
+
11720
+ The stop rides on the selection, and an unanswered group has no selection,
11721
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
11722
+ enter the set at all.
11723
+
11724
+ **Written by the group, with no attribute, and it reaches the control
11725
+ rather than the host.** A `tabindex` on the host is two faults at once: it
11726
+ is markup a consumer rendered and their server never sent, which React
11727
+ reports as a mismatch and does not patch up, and it puts the tab stop on
11728
+ the element rather than on the `<button role="radio">` inside it, so a
11729
+ screen reader meets an unnamed box where a native group would have given
11730
+ them an unchecked radio. `Toggle.Root` carries the same property for the
11731
+ same reason. */
11732
+ focusable?: (HelixHeadlessRadio["focusable"]) | undefined;
11718
11733
 
11719
11734
  /** Fired when this choice is made. Cancelable: refusing it takes the decision and nothing moves. */
11720
11735
  "onhelix-select"?: ((e: HelixHeadlessRadioHelixSelectElementEvent) => void) | undefined;
@@ -11755,6 +11770,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
11755
11770
  Splitting them means moving what every stylesheet selects on, which is a
11756
11771
  change of its own rather than a property being added. */
11757
11772
  "prop:defaultSelected"?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
11773
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
11774
+
11775
+ The stop rides on the selection, and an unanswered group has no selection,
11776
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
11777
+ enter the set at all.
11778
+
11779
+ **Written by the group, with no attribute, and it reaches the control
11780
+ rather than the host.** A `tabindex` on the host is two faults at once: it
11781
+ is markup a consumer rendered and their server never sent, which React
11782
+ reports as a mismatch and does not patch up, and it puts the tab stop on
11783
+ the element rather than on the `<button role="radio">` inside it, so a
11784
+ screen reader meets an unnamed box where a native group would have given
11785
+ them an unchecked radio. `Toggle.Root` carries the same property for the
11786
+ same reason. */
11787
+ "prop:focusable"?: (HelixHeadlessRadio["focusable"]) | undefined;
11758
11788
  /** Fired when this choice is made. Cancelable: refusing it takes the decision and nothing moves. */
11759
11789
  "on:helix-select"?: ((e: HelixHeadlessRadioHelixSelectElementEvent) => void) | undefined;
11760
11790
 
@@ -13023,11 +13053,11 @@ has an honest answer. */
13023
13053
  /** How far one arrow press moves it, as a percentage. */
13024
13054
  step?: (HelixHeadlessSplitter["step"]) | undefined;
13025
13055
 
13026
- /** Fired before a pane shuts or reopens, and cancelable. */
13056
+ /** Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it. */
13027
13057
  "onhelix-before-collapse"?: ((
13028
13058
  e: HelixHeadlessSplitterHelixBeforeCollapseElementEvent,
13029
13059
  ) => void) | undefined;
13030
- /** Fired before the divider settles, and cancelable. */
13060
+ /** Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own. */
13031
13061
  "onhelix-before-resize"?: ((
13032
13062
  e: HelixHeadlessSplitterHelixBeforeResizeElementEvent,
13033
13063
  ) => void) | undefined;
@@ -13127,11 +13157,11 @@ has an honest answer. */
13127
13157
  "prop:orientation"?: (HelixHeadlessSplitter["orientation"]) | undefined;
13128
13158
  /** How far one arrow press moves it, as a percentage. */
13129
13159
  "prop:step"?: (HelixHeadlessSplitter["step"]) | undefined;
13130
- /** Fired before a pane shuts or reopens, and cancelable. */
13160
+ /** Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it. */
13131
13161
  "on:helix-before-collapse"?: ((
13132
13162
  e: HelixHeadlessSplitterHelixBeforeCollapseElementEvent,
13133
13163
  ) => void) | undefined;
13134
- /** Fired before the divider settles, and cancelable. */
13164
+ /** Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own. */
13135
13165
  "on:helix-before-resize"?: ((
13136
13166
  e: HelixHeadlessSplitterHelixBeforeResizeElementEvent,
13137
13167
  ) => void) | undefined;
@@ -15977,8 +16007,6 @@ The shape is `Carousel`'s, which is the nearest thing in the library: a
15977
16007
  "onhelix-complete"?: ((e: HelixHeadlessTourHelixCompleteElementEvent) => void) | undefined;
15978
16008
  /** A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves. */
15979
16009
  "onhelix-tour-ask"?: ((e: HelixHeadlessTourHelixTourAskElementEvent) => void) | undefined;
15980
- /** Says the state moved, carrying `newState` and `oldState`. */
15981
- ontoggle?: ((e: HelixHeadlessTourElementEvent) => void) | undefined;
15982
16010
  };
15983
16011
 
15984
16012
  export type HelixHeadlessTourSolidJsProps = {
@@ -16033,8 +16061,6 @@ The shape is `Carousel`'s, which is the nearest thing in the library: a
16033
16061
  "on:helix-complete"?: ((e: HelixHeadlessTourHelixCompleteElementEvent) => void) | undefined;
16034
16062
  /** A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves. */
16035
16063
  "on:helix-tour-ask"?: ((e: HelixHeadlessTourHelixTourAskElementEvent) => void) | undefined;
16036
- /** Says the state moved, carrying `newState` and `oldState`. */
16037
- "on:toggle"?: ((e: HelixHeadlessTourElementEvent) => void) | undefined;
16038
16064
 
16039
16065
  /** Set the innerHTML of the element */
16040
16066
  innerHTML?: (string) | undefined;
@@ -26912,6 +26938,7 @@ export type CustomElements = {
26912
26938
  * - `actions`: What a reader can do about it, under the words it is about. Drawn always rather than on hover, because an action is the point of the row and not chrome around it.
26913
26939
  * - `caption`: A second line qualifying it, quieter than the first. A bare slot rather than a box, so an item without one pays nothing for it; style it through `::slotted()`.
26914
26940
  * - `detail`: A block belonging to the row rather than a line about it: a quoted comment, a status moving from one value to another, a preview. Unlike `caption` nothing here sets a size or an ink, because whatever fills it usually brings its own.
26941
+ * - `dismiss`: Replaces the glyph inside the dismiss control, which is drawn only where the row is `dismissible`.
26915
26942
  * - `media`: An avatar or icon identifying who or what it came from.
26916
26943
  *
26917
26944
  * ## Methods
@@ -29057,6 +29084,20 @@ export type CustomElements = {
29057
29084
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
29058
29085
  * Splitting them means moving what every stylesheet selects on, which is a
29059
29086
  * change of its own rather than a property being added. (property only)
29087
+ * - `focusable`: Whether this choice holds the group's tab stop while nothing is chosen.
29088
+ *
29089
+ * The stop rides on the selection, and an unanswered group has no selection,
29090
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
29091
+ * enter the set at all.
29092
+ *
29093
+ * **Written by the group, with no attribute, and it reaches the control
29094
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
29095
+ * is markup a consumer rendered and their server never sent, which React
29096
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
29097
+ * the element rather than on the `<button role="radio">` inside it, so a
29098
+ * screen reader meets an unnamed box where a native group would have given
29099
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
29100
+ * same reason. (property only)
29060
29101
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
29061
29102
  *
29062
29103
  * **Named `whenSettled` because `settled` is taken four times over**, by
@@ -30667,8 +30708,8 @@ export type CustomElements = {
30667
30708
  *
30668
30709
  * Events that will be emitted by the component.
30669
30710
  *
30670
- * - `helix-before-collapse`: Fired before a pane shuts or reopens, and cancelable.
30671
- * - `helix-before-resize`: Fired before the divider settles, and cancelable.
30711
+ * - `helix-before-collapse`: Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.
30712
+ * - `helix-before-resize`: Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.
30672
30713
  *
30673
30714
  * ## Slots
30674
30715
  *
@@ -33752,7 +33793,6 @@ export type CustomElements = {
33752
33793
  * - `helix-before-close`: Fired before the reader leaves early. Cancelable: refusing it takes the decision and nothing moves.
33753
33794
  * - `helix-complete`: Fired when the last stop is finished.
33754
33795
  * - `helix-tour-ask`: A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves.
33755
- * - `toggle`: Says the state moved, carrying `newState` and `oldState`.
33756
33796
  *
33757
33797
  * ## Slots
33758
33798
  *
@@ -44742,6 +44782,7 @@ export type CustomElementsSolidJs = {
44742
44782
  * - `actions`: What a reader can do about it, under the words it is about. Drawn always rather than on hover, because an action is the point of the row and not chrome around it.
44743
44783
  * - `caption`: A second line qualifying it, quieter than the first. A bare slot rather than a box, so an item without one pays nothing for it; style it through `::slotted()`.
44744
44784
  * - `detail`: A block belonging to the row rather than a line about it: a quoted comment, a status moving from one value to another, a preview. Unlike `caption` nothing here sets a size or an ink, because whatever fills it usually brings its own.
44785
+ * - `dismiss`: Replaces the glyph inside the dismiss control, which is drawn only where the row is `dismissible`.
44745
44786
  * - `media`: An avatar or icon identifying who or what it came from.
44746
44787
  *
44747
44788
  * ## Methods
@@ -46917,6 +46958,20 @@ export type CustomElementsSolidJs = {
46917
46958
  * name is the fault `TreeItem` avoids by mirroring to `data-selected`.
46918
46959
  * Splitting them means moving what every stylesheet selects on, which is a
46919
46960
  * change of its own rather than a property being added. (property only)
46961
+ * - `focusable`: Whether this choice holds the group's tab stop while nothing is chosen.
46962
+ *
46963
+ * The stop rides on the selection, and an unanswered group has no selection,
46964
+ * so without this every choice is `tabindex="-1"` and the keyboard cannot
46965
+ * enter the set at all.
46966
+ *
46967
+ * **Written by the group, with no attribute, and it reaches the control
46968
+ * rather than the host.** A `tabindex` on the host is two faults at once: it
46969
+ * is markup a consumer rendered and their server never sent, which React
46970
+ * reports as a mismatch and does not patch up, and it puts the tab stop on
46971
+ * the element rather than on the `<button role="radio">` inside it, so a
46972
+ * screen reader meets an unnamed box where a native group would have given
46973
+ * them an unchecked radio. `Toggle.Root` carries the same property for the
46974
+ * same reason. (property only)
46920
46975
  * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
46921
46976
  *
46922
46977
  * **Named `whenSettled` because `settled` is taken four times over**, by
@@ -48554,8 +48609,8 @@ export type CustomElementsSolidJs = {
48554
48609
  *
48555
48610
  * Events that will be emitted by the component.
48556
48611
  *
48557
- * - `helix-before-collapse`: Fired before a pane shuts or reopens, and cancelable.
48558
- * - `helix-before-resize`: Fired before the divider settles, and cancelable.
48612
+ * - `helix-before-collapse`: Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it.
48613
+ * - `helix-before-resize`: Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own.
48559
48614
  *
48560
48615
  * ## Slots
48561
48616
  *
@@ -51706,7 +51761,6 @@ export type CustomElementsSolidJs = {
51706
51761
  * - `helix-before-close`: Fired before the reader leaves early. Cancelable: refusing it takes the decision and nothing moves.
51707
51762
  * - `helix-complete`: Fired when the last stop is finished.
51708
51763
  * - `helix-tour-ask`: A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves.
51709
- * - `toggle`: Says the state moved, carrying `newState` and `oldState`.
51710
51764
  *
51711
51765
  * ## Slots
51712
51766
  *
package/types/vue.d.ts CHANGED
@@ -6021,6 +6021,21 @@ name is the fault `TreeItem` avoids by mirroring to `data-selected`.
6021
6021
  Splitting them means moving what every stylesheet selects on, which is a
6022
6022
  change of its own rather than a property being added. */
6023
6023
  defaultSelected?: (HelixHeadlessRadio["defaultSelected"]) | undefined;
6024
+ /** Whether this choice holds the group's tab stop while nothing is chosen.
6025
+
6026
+ The stop rides on the selection, and an unanswered group has no selection,
6027
+ so without this every choice is `tabindex="-1"` and the keyboard cannot
6028
+ enter the set at all.
6029
+
6030
+ **Written by the group, with no attribute, and it reaches the control
6031
+ rather than the host.** A `tabindex` on the host is two faults at once: it
6032
+ is markup a consumer rendered and their server never sent, which React
6033
+ reports as a mismatch and does not patch up, and it puts the tab stop on
6034
+ the element rather than on the `<button role="radio">` inside it, so a
6035
+ screen reader meets an unnamed box where a native group would have given
6036
+ them an unchecked radio. `Toggle.Root` carries the same property for the
6037
+ same reason. */
6038
+ focusable?: (HelixHeadlessRadio["focusable"]) | undefined;
6024
6039
  };
6025
6040
 
6026
6041
  export type HelixHeadlessRadioVueEvents = {
@@ -6722,9 +6737,9 @@ has an honest answer. */
6722
6737
  };
6723
6738
 
6724
6739
  export type HelixHeadlessSplitterVueEvents = {
6725
- /** Fired before a pane shuts or reopens, and cancelable. */
6740
+ /** Fired before a pane shuts or reopens, and only where the position it is derived from stood. Cancelable: refusing it leaves the pane as it was and the divider where the gesture put it. */
6726
6741
  "helix-before-collapse": HelixHeadlessSplitterHelixBeforeCollapseElementEvent;
6727
- /** Fired before the divider settles, and cancelable. */
6742
+ /** Fired before the divider settles. Cancelable: refusing it takes the whole of where the gesture ended, so no collapse follows from it either. A drag has already drawn itself by then and holds where it was dropped until the consumer assigns their own. */
6728
6743
  "helix-before-resize": HelixHeadlessSplitterHelixBeforeResizeElementEvent;
6729
6744
  };
6730
6745
 
@@ -8225,8 +8240,6 @@ export type HelixHeadlessTourVueEvents = {
8225
8240
  "helix-complete": HelixHeadlessTourHelixCompleteElementEvent;
8226
8241
  /** A stop's own controls asking the tour to move, dispatched by the stop and heard here because the tour is what moves. */
8227
8242
  "helix-tour-ask": HelixHeadlessTourHelixTourAskElementEvent;
8228
- /** Says the state moved, carrying `newState` and `oldState`. */
8229
- toggle: HelixHeadlessTourElementEvent;
8230
8243
  };
8231
8244
 
8232
8245
  /** `HelixHeadlessTourStop` component event */