@fusion.dev/helix-headless 0.170.0 → 0.172.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 (57) hide show
  1. package/custom-elements.json +1045 -154
  2. package/dist/chat/composer/chat-composer.d.ts +30 -2
  3. package/dist/chat/composer/chat-composer.js +29 -4
  4. package/dist/chat/root/chat-root.d.ts +73 -7
  5. package/dist/chat/root/chat-root.js +90 -5
  6. package/dist/code-block/root/code-block-root.d.ts +20 -2
  7. package/dist/code-block/root/code-block-root.js +29 -8
  8. package/dist/color-input/root/color-input-root.d.ts +15 -1
  9. package/dist/color-input/root/color-input-root.js +19 -1
  10. package/dist/color-picker/root/color-picker-root.d.ts +54 -7
  11. package/dist/color-picker/root/color-picker-root.js +62 -1
  12. package/dist/combobox/root/combobox-root.d.ts +16 -6
  13. package/dist/combobox/root/combobox-root.js +9 -2
  14. package/dist/command/root/command-root.d.ts +16 -1
  15. package/dist/command/root/command-root.js +8 -0
  16. package/dist/data-table/root/data-table-root.d.ts +5 -1
  17. package/dist/data-table/root/data-table-root.js +1 -0
  18. package/dist/dialog/root/dialog-root.js +1 -0
  19. package/dist/drawer/root/drawer-root.d.ts +4 -0
  20. package/dist/drawer/root/drawer-root.js +1 -0
  21. package/dist/element-events.generated.js +1 -0
  22. package/dist/filter-input/chip/filter-chip.d.ts +13 -3
  23. package/dist/filter-input/chip/filter-chip.js +5 -0
  24. package/dist/filter-input/menu/filter-menu.js +0 -1
  25. package/dist/filter-input/root/filter-input-root.d.ts +71 -4
  26. package/dist/filter-input/root/filter-input-root.js +87 -2
  27. package/dist/format-date/root/format-date-root.d.ts +3 -1
  28. package/dist/input/root/input-root.d.ts +6 -0
  29. package/dist/input/root/input-root.js +13 -0
  30. package/dist/lightbox/root/lightbox-root.js +1 -0
  31. package/dist/listbox/root/listbox-root.d.ts +8 -1
  32. package/dist/listbox/root/listbox-root.js +9 -3
  33. package/dist/menu/item/menu-item.d.ts +2 -0
  34. package/dist/menu/item/menu-item.js +2 -0
  35. package/dist/menu/root/menu-root.d.ts +73 -12
  36. package/dist/menu/root/menu-root.js +103 -7
  37. package/dist/number-input/root/number-input-root.d.ts +16 -6
  38. package/dist/number-input/root/number-input-root.js +15 -4
  39. package/dist/popover/root/popover-root.d.ts +59 -6
  40. package/dist/popover/root/popover-root.js +51 -3
  41. package/dist/select/root/select-root.d.ts +2 -2
  42. package/dist/select/root/select-root.js +4 -2
  43. package/dist/steps/root/steps-root.d.ts +32 -8
  44. package/dist/steps/root/steps-root.js +70 -9
  45. package/dist/steps/step/step.d.ts +9 -1
  46. package/dist/steps/step/step.js +5 -0
  47. package/dist/textarea/root/textarea-root.d.ts +8 -0
  48. package/dist/textarea/root/textarea-root.js +16 -0
  49. package/dist/video-playlist/root/video-playlist-root.d.ts +19 -1
  50. package/dist/video-playlist/root/video-playlist-root.js +14 -3
  51. package/dist/virtual-list/root/virtual-list-root.d.ts +8 -0
  52. package/dist/virtual-list/root/virtual-list-root.js +7 -2
  53. package/lint.json +1 -1
  54. package/package.json +5 -5
  55. package/types/jsx.d.ts +800 -144
  56. package/types/vue.d.ts +165 -31
  57. package/types/words.d.ts +20 -2
@@ -23184,7 +23184,7 @@
23184
23184
  "declarations": [
23185
23185
  {
23186
23186
  "kind": "class",
23187
- "description": "The composer is a separate element so you can replace it. If you need\nattachments, slash commands, or a model picker, replace the composer and keep\nthe transcript.\n\nBy default, Enter sends and Shift+Enter starts a new line, the convention in\nmessaging apps. Document editors mostly use the opposite, so `submit-on`\ntakes `enter` or `button`. Use `button` for a long-form composer.\n\nThe composer clears only when no listener cancels the send, so the text is\nkept when you cancel a send that failed.\n\n**In a form, the draft is the composer's value**, submitted under `name`. A\nform reset puts back the `value` the markup declared, and a `<fieldset\ndisabled>` turns the composer off. Sending is still `helix-send`, and it\ndoes not submit the form.\n\n**`required`, `minlength` and `maxlength` are checked by the browser**, as\non a `<textarea>`, and the message is the browser's. `maxlength` stops the\nfield taking more. A draft that fails them does not send, and the composer\ntakes `:state(invalid)` and `aria-invalid` once the reader has tried to send\nit, the form was submitted, or the reader edited the field and left it.\nTabbing past an empty composer marks nothing.\n\n**`typing` says whether the user is writing**, and `helix-typing` is sent\neach time it changes: when they start, and when they send or stop for\n`typing-timeout` milliseconds. Pass it on to show \"is typing\" to the other\nside.",
23187
+ "description": "The composer is a separate element so you can replace it. If you need\nattachments, slash commands, or a model picker, replace the composer and keep\nthe transcript.\n\nBy default, Enter sends and Shift+Enter starts a new line, the convention in\nmessaging apps. Document editors mostly use the opposite, so `submit-on`\ntakes `enter` or `button`. Use `button` for a long-form composer.\n\nThe composer clears only when no listener cancels the send, so the text is\nkept when you cancel a send that failed.\n\n**In a form, the draft is the composer's value**, submitted under `name`. A\nform reset puts back the `value` the markup declared, and a `<fieldset\ndisabled>` turns the composer off. Sending is still `helix-send`, and it\ndoes not submit the form.\n\n**`required`, `minlength` and `maxlength` are checked by the browser**, as\non a `<textarea>`, and the message is the browser's. `maxlength` stops the\nfield taking more. A draft that fails them does not send, and the composer\ntakes `:state(invalid)` and `aria-invalid` once the reader has tried to send\nit, the form was submitted, or the reader edited the field and left it.\nSending an empty required composer counts as trying. Tabbing past an empty\ncomposer marks nothing.\n\n**`feedback` is a message of your own about the draft**, such as one your\nserver sent back. While it is set, the composer is invalid at once, does\nnot send or submit, shows the message under the field, and a screen reader\nhears it as it appears. Clear it to let the draft go.\n\n**`typing` says whether the user is writing**, and `helix-typing` is sent\neach time it changes: when they start, and when they send or stop for\n`typing-timeout` milliseconds. Pass it on to show \"is typing\" to the other\nside.",
23188
23188
  "name": "HelixHeadlessChatComposer",
23189
23189
  "cssParts": [
23190
23190
  {
@@ -23195,6 +23195,10 @@
23195
23195
  "description": "The box around the `end` slot.",
23196
23196
  "name": "end"
23197
23197
  },
23198
+ {
23199
+ "description": "The `feedback` message, under the field while it is set.",
23200
+ "name": "feedback"
23201
+ },
23198
23202
  {
23199
23203
  "description": "The text field.",
23200
23204
  "name": "field"
@@ -23237,6 +23241,16 @@
23237
23241
  "default": "'helix-chat-composer'",
23238
23242
  "description": "The key this part's words are registered under, its styled tag:\n`registerWords('de', { 'helix-chat-composer': { sendLabel: 'Senden' } })`."
23239
23243
  },
23244
+ {
23245
+ "kind": "field",
23246
+ "name": "feedback",
23247
+ "type": {
23248
+ "text": "string"
23249
+ },
23250
+ "default": "''",
23251
+ "description": "A message about the draft, shown under the field. While set, the composer\nis invalid, does not send, and does not submit, as a field's `feedback`\ndoes, and the message is announced as it appears.",
23252
+ "attribute": "feedback"
23253
+ },
23240
23254
  {
23241
23255
  "kind": "field",
23242
23256
  "name": "label",
@@ -23361,6 +23375,16 @@
23361
23375
  "default": "false",
23362
23376
  "description": "Whether the reader has tried to send or submit the draft, or edited it and left."
23363
23377
  },
23378
+ {
23379
+ "kind": "field",
23380
+ "name": "edited",
23381
+ "type": {
23382
+ "text": "boolean"
23383
+ },
23384
+ "privacy": "private",
23385
+ "default": "false",
23386
+ "description": "Whether the reader has typed in the field since it last held a fresh\ndraft: since it rendered, since a send cleared it, or since a reset.\n\n**A send clears the field, and the field still sends `change` as it is\nleft**, because the platform compares the text with what it held when it\nwas focused. Measured in Chromium before this: a required composer sent\n\"hi\", cleared, and on Tab showed itself invalid over an empty draft\nnobody had written."
23387
+ },
23364
23388
  {
23365
23389
  "kind": "field",
23366
23390
  "name": "shownInvalid",
@@ -23454,6 +23478,13 @@
23454
23478
  "readonly": true,
23455
23479
  "description": "The reader has had a go at the draft, so whether it is wrong can be shown."
23456
23480
  },
23481
+ {
23482
+ "kind": "field",
23483
+ "name": "handleChange",
23484
+ "privacy": "private",
23485
+ "readonly": true,
23486
+ "description": "The field's `change`, which counts only after the reader typed in it."
23487
+ },
23457
23488
  {
23458
23489
  "kind": "method",
23459
23490
  "name": "formStateRestoreCallback",
@@ -24374,6 +24405,15 @@
24374
24405
  }
24375
24406
  ],
24376
24407
  "attributes": [
24408
+ {
24409
+ "name": "feedback",
24410
+ "type": {
24411
+ "text": "string"
24412
+ },
24413
+ "default": "''",
24414
+ "description": "A message about the draft, shown under the field. While set, the composer\nis invalid, does not send, and does not submit, as a field's `feedback`\ndoes, and the message is announced as it appears.",
24415
+ "fieldName": "feedback"
24416
+ },
24377
24417
  {
24378
24418
  "name": "label",
24379
24419
  "type": {
@@ -24491,7 +24531,7 @@
24491
24531
  ],
24492
24532
  "cssStates": [
24493
24533
  {
24494
- "description": "The draft fails `required`, `minlength`, `maxlength`, or a custom error, and the reader has tried to send or submit it, or edited the field and left it.",
24534
+ "description": "The draft fails `required`, `minlength`, `maxlength`, or a custom error, and the reader has tried to send or submit it, or edited the field and left it. Or `feedback` is set.",
24495
24535
  "name": "invalid"
24496
24536
  },
24497
24537
  {
@@ -25471,7 +25511,7 @@
25471
25511
  "declarations": [
25472
25512
  {
25473
25513
  "kind": "class",
25474
- "description": "Chat has no positioning, no backdrop, and no trigger, so you can put it in\nany container, such as a page, a sidebar, a modal, or a floating panel.\n\nThe transcript stays pinned to the bottom, including while a streamed answer\ngrows, until the user scrolls more than `threshold` away from it.\n\nWith `history` set to `more`, reaching the top fires `helix-history`. Set\n`loading` while you fetch, then prepend the older turns. The turn the user\nwas reading stays in place on screen.\n\nThe log is `aria-live=\"off\"`. A separate status region announces each new\nturn once, when it has finished arriving. It skips the user's own turns\nunless they failed to send, turns loaded as history, and the turns already\nthere when the transcript opened. A turn is said with its author first, such\nas \"Ada: It ships today\" (`arrival-label`), where its author can be named.\n\nPass the turns as children or as `turns` data. Do not use both. Children\nrender from the server and suit most conversations. `turns` draws a long\ntranscript one window at a time, so the log needs a block size of its own.\n\n**A conversation is built from three elements.** The chat holds the\ntranscript, a `Chat.Message` stands for each turn, and a `Chat.Composer` sits\nin the `composer` slot. When the user sends, `helix-send` reaches the chat:\nadd their turn with `author=\"me\"` and `state=\"sending\"`, then set `state` to\n`sent` or `failed` when your server answers. Add the reply as a turn with\n`state=\"streaming\"`, write its text as it arrives, and set `state` to `sent`\nwhen it is complete. While the user types, the composer sends\n`helix-typing`, which you can pass on to the other side. When the other side\ntypes, set `typing` to their name: the status band draws three dots and\n\"Ada is typing\" (`typing-label`), which a screen reader hears, and clearing\nit takes them away. Anything slotted into `status` is drawn first.\n\n**Keyboard.** The transcript moves between turns the way a feed does. Each\nturn says its place, such as \"3 of 40\", windowed or not.\n\n| Key | What it does |\n|---|---|\n| Page Down, Page Up | On a turn or inside one, moves focus to the next or the previous turn. On the transcript itself, moves focus to the turn at the top of the view. |\n| Control with End | Moves focus to the composer. |\n| Control with Home | Moves focus to the first control above the transcript, or to the transcript. |\n\nWhile you are scrolled away from the newest turn, the `resume` region shows\na button that takes you back, named by `resume-label`, \"Jump to latest\".\nSlot your own control into `resume` to replace it.\n\nA screen reader names each turn by its author and its time, such as \"You, 2\nminutes ago\". Set `me-label` and `message-label` to say it in your language,\nand give each turn by `them` an `author-label`. A turn with neither an author\nlabel nor a time is named by `turn-label`, \"Message\".\n\nA turn from `turns` is drawn in the chat's shadow root, so style it through\nthe forwarded `message-*` parts. A turn by `me` also gets each part with\n`-mine` added, and the turn's state adds `message-meta-failed`,\n`message-content-sending`, and `message-content-streaming`. To style each\nparticipant differently, give `author-styles` the declarations for each\nauthor, such as `{\"ada\": \"--chat-message-background-color: …\"}`: the message\ndrawn for each of their turns takes them as its `style`, so the message's\ncustom properties change for that author alone. Turns passed as children are\nstyled by their `author`, such as `helix-chat-message[author=\"ada\"]`.",
25514
+ "description": "Chat has no positioning, no backdrop, and no trigger, so you can put it in\nany container, such as a page, a sidebar, a modal, or a floating panel.\n\nThe transcript stays pinned to the bottom, including while a streamed answer\ngrows, until the user scrolls more than `threshold` away from it.\n\nWith `history` set to `more`, reaching the top fires `helix-history`. Set\n`loading` while you fetch, then prepend the older turns. The turn the user\nwas reading stays in place on screen.\n\nThe transcript is a `log`, the role for content added in order where the\nnewest matters most, and it answers the APG feed's keys. The `feed` role\nreads its articles as a list from the top, with no newest end to stay\npinned to, so the log keeps the role a conversation has. While `loading` is\nset or older turns load, the log carries `aria-busy`, as the APG asks of a\nfeed.\n\nThe log is `aria-live=\"off\"`. A separate status region announces each new\nturn once, when it has finished arriving. It skips the user's own turns\nunless they failed to send, turns loaded as history, and the turns already\nthere when the transcript opened. A turn is said with its author first, such\nas \"Ada: It ships today\" (`arrival-label`), where its author can be named.\n\nPass the turns as children or as `turns` data. Do not use both: a chat given\nboth draws every turn from `turns` and then the children, and says so once\nin the console. Children render from the server and suit most\nconversations. `turns` draws a long\ntranscript one window at a time, so the log needs a block size of its own.\n\nThe log scrolls on this tier. It carries `data-scroller`, and `core` ships\n`[data-scroller] { overflow: auto }` into every shadow root, so pinning,\nhistory and the window work with no stylesheet. You still need to give the\nlog a block size, such as `helix-headless-chat::part(log) { block-size:\n20rem }`.\n\n**A conversation is built from three elements.** The chat holds the\ntranscript, a `Chat.Message` stands for each turn, and a `Chat.Composer` sits\nin the `composer` slot. When the user sends, `helix-send` reaches the chat:\nadd their turn with `author=\"me\"` and `state=\"sending\"`, then set `state` to\n`sent` or `failed` when your server answers. Add the reply as a turn with\n`state=\"streaming\"`, write its text as it arrives, and set `state` to `sent`\nwhen it is complete. While the user types, the composer sends\n`helix-typing`, which you can pass on to the other side. When the other side\ntypes, set `typing` to their name: the status band draws three dots and\n\"Ada is typing\" (`typing-label`), which a screen reader hears, and clearing\nit takes them away. Anything slotted into `status` is drawn first.\n\n**Keyboard.** The transcript moves between turns the way a feed does. Each\nturn says its place, such as \"3 of 40\", windowed or not. The transcript is\na tab stop while it holds a turn, and the turn a reader moved to is one\ntoo. Moving to an older turn unpins a transcript that scrolls and scrolls\nthe turn into view, and moving to the newest pins it again. When the turn\nholding focus is removed, focus moves to the turn that took its place, or to\nthe one before it where it was the last.\n\n| Key | What it does |\n|---|---|\n| Page Down, Page Up | On a turn or inside one, moves focus to the next or the previous turn. On the transcript itself, moves focus to the turn at the top of the view. |\n| Control with End | Moves focus to the composer. |\n| Control with Home | Moves focus to the first control above the transcript, or to the transcript. |\n\nWhile you are scrolled away from the newest turn, the `resume` region shows\na button that takes you back, named by `resume-label`, \"Jump to latest\".\nSlot your own control into `resume` to replace it.\n\nA screen reader names each turn by its author and its time, such as \"You, 2\nminutes ago\". Set `me-label` and `message-label` to say it in your language,\nand give each turn by `them` an `author-label`. A turn with neither an author\nlabel nor a time is named by `turn-label`, \"Message\".\n\nA turn from `turns` is drawn in the chat's shadow root, so style it through\nthe forwarded `message-*` parts. A turn by `me` also gets each part with\n`-mine` added, and the turn's state adds `message-meta-failed`,\n`message-content-sending`, and `message-content-streaming`. To style each\nparticipant differently, give `author-styles` the declarations for each\nauthor, such as `{\"ada\": \"--chat-message-background-color: …\"}`: the message\ndrawn for each of their turns takes them as its `style`, so the message's\ncustom properties change for that author alone. Turns passed as children are\nstyled by their `author`, such as `helix-chat-message[author=\"ada\"]`.",
25475
25515
  "name": "HelixHeadlessChat",
25476
25516
  "cssParts": [
25477
25517
  {
@@ -25515,7 +25555,7 @@
25515
25555
  "name": "loading"
25516
25556
  },
25517
25557
  {
25518
- "description": "The transcript, which scrolls.",
25558
+ "description": "The transcript, which scrolls. On this tier, it needs only a block size.",
25519
25559
  "name": "log"
25520
25560
  },
25521
25561
  {
@@ -26112,7 +26152,7 @@
26112
26152
  "text": "void"
26113
26153
  }
26114
26154
  },
26115
- "description": "Makes the log a tab stop while it scrolls, and takes the stop away when it\ndoes not.\n\n**A reader on a keyboard scrolls the history from the log itself.**\nChromium and Firefox make a box that scrolls focusable on their own, and\nSafari does not, so there the arrow keys could not reach a long history.\nA log that fits is no stop at all, so Tab goes straight to the composer."
26155
+ "description": "Makes the log a tab stop while it holds a turn or scrolls, and takes the\nstop away from an empty log.\n\n**A reader on a keyboard scrolls the history from the log itself.**\nChromium and Firefox make a box that scrolls focusable on their own, and\nSafari does not, so there the arrow keys could not reach a long history.\n\n**And it is where the turns are entered.** Page Down from the log goes to\nthe turn at the top of the view. A log that fits was no stop at all, so a\nconversation whose turns held no control had no way into them by keyboard."
26116
26156
  },
26117
26157
  {
26118
26158
  "kind": "field",
@@ -26366,6 +26406,27 @@
26366
26406
  "readonly": true,
26367
26407
  "description": "A press anywhere in the way back takes the reader back.\n\nThe control is slotted so the consumer picks the words, which on its own\nleaves a button this element positions, lifts, shows and hides doing nothing\nuntil somebody wires it. The region exists for exactly one purpose, so a\npress inside it has exactly one meaning and this element is entitled to act\non it. `resume()` stays public for the other callers: a keyboard shortcut,\nor a consumer's own control somewhere else on the page."
26368
26408
  },
26409
+ {
26410
+ "kind": "field",
26411
+ "name": "warnedMixed",
26412
+ "type": {
26413
+ "text": "boolean"
26414
+ },
26415
+ "privacy": "private",
26416
+ "default": "false",
26417
+ "description": "Whether the chat has said it was given turns both ways."
26418
+ },
26419
+ {
26420
+ "kind": "method",
26421
+ "name": "warnMixed",
26422
+ "privacy": "private",
26423
+ "return": {
26424
+ "type": {
26425
+ "text": "void"
26426
+ }
26427
+ },
26428
+ "description": "Says once that turns arrived both as children and as `turns`."
26429
+ },
26369
26430
  {
26370
26431
  "kind": "method",
26371
26432
  "name": "follow",
@@ -26385,6 +26446,40 @@
26385
26446
  ],
26386
26447
  "description": "Keeps the window on the same turns when the set changes around them.\n\nTurns added before the first one are history, and the controller is told\nhow many so every measurement moves with its turn. A first turn that is gone\nmeans the set was trimmed or replaced, and a measurement that now names a\ndifferent turn is worse than the estimate."
26387
26448
  },
26449
+ {
26450
+ "kind": "field",
26451
+ "name": "held",
26452
+ "type": {
26453
+ "text": "{ at: number; id: unknown } | undefined"
26454
+ },
26455
+ "privacy": "private",
26456
+ "description": "The turn focus is in, by its place and its identity, once a reader is in one."
26457
+ },
26458
+ {
26459
+ "kind": "field",
26460
+ "name": "handleFocusIn",
26461
+ "privacy": "private",
26462
+ "readonly": true,
26463
+ "description": "Notes the turn focus went into, or that it went somewhere else."
26464
+ },
26465
+ {
26466
+ "kind": "field",
26467
+ "name": "handleFocusOut",
26468
+ "privacy": "private",
26469
+ "readonly": true,
26470
+ "description": "Forgets the turn once focus moves to something outside the transcript.\nA move to nothing is kept, since removing the turn that held focus is\none."
26471
+ },
26472
+ {
26473
+ "kind": "method",
26474
+ "name": "followRemoved",
26475
+ "privacy": "private",
26476
+ "return": {
26477
+ "type": {
26478
+ "text": "void"
26479
+ }
26480
+ },
26481
+ "description": "Moves focus to a neighbor where removing the turn that held it dropped\nfocus to the body: the turn that took its place, or the one before it\nwhere it was the last.\n\n**Focus went to the body**, and Page Down did nothing until the reader\ntabbed back into the transcript. List and Virtual List move focus to a\nneighbor the same way, as React Aria's GridList does. Focus anywhere but\nthe body is where you put it, and is left there."
26482
+ },
26388
26483
  {
26389
26484
  "kind": "method",
26390
26485
  "name": "tellTurns",
@@ -26445,6 +26540,25 @@
26445
26540
  ],
26446
26541
  "description": "Moves focus to turn `at`, drawing it first where the transcript is\nwindowed and it is not drawn.\n\n**A turn takes focus only once a reader moves to it**, so the `tabindex`\nit needs is written then, long after any server's markup has hydrated,\nand never on a turn nobody moved to."
26447
26542
  },
26543
+ {
26544
+ "kind": "method",
26545
+ "name": "showTurn",
26546
+ "privacy": "private",
26547
+ "return": {
26548
+ "type": {
26549
+ "text": "Promise<void>"
26550
+ }
26551
+ },
26552
+ "parameters": [
26553
+ {
26554
+ "name": "at",
26555
+ "type": {
26556
+ "text": "number"
26557
+ }
26558
+ }
26559
+ ],
26560
+ "description": "Draws turn `at` where it is windowed and not drawn, and visits it."
26561
+ },
26448
26562
  {
26449
26563
  "kind": "field",
26450
26564
  "name": "stop",
@@ -26473,6 +26587,34 @@
26473
26587
  ],
26474
26588
  "description": "Focuses a turn, making it the transcript's one stop a reader moved to.\n\n**`0`, and never `-1`.** Chromium leaves the whole of a shadow host at a\nnegative `tabindex` out of the Tab order, its own controls and what is\nslotted into it alike, so a turn at `-1` lost its links and its Try again\nonce a reader had paged through it. The stop moves with the reader, and a\n`tabindex` you wrote on a turn is left as it is."
26475
26589
  },
26590
+ {
26591
+ "kind": "field",
26592
+ "name": "placed",
26593
+ "type": {
26594
+ "text": "number | undefined"
26595
+ },
26596
+ "privacy": "private",
26597
+ "description": "Where this element last scrolled the transcript to show a turn, until the scroll that follows."
26598
+ },
26599
+ {
26600
+ "kind": "method",
26601
+ "name": "reveal",
26602
+ "privacy": "private",
26603
+ "return": {
26604
+ "type": {
26605
+ "text": "void"
26606
+ }
26607
+ },
26608
+ "parameters": [
26609
+ {
26610
+ "name": "turn",
26611
+ "type": {
26612
+ "text": "HTMLElement"
26613
+ }
26614
+ }
26615
+ ],
26616
+ "description": "Scrolls the transcript, and nothing around it, until `turn` is in view,\nand notes the turn at the top as the reader's place. A pinned transcript\nis left at the bottom, which is where whoever holds the pin wants it.\n\nThe edge nearest the turn moves the least, and a turn taller than the\ntranscript is shown from its start."
26617
+ },
26476
26618
  {
26477
26619
  "kind": "method",
26478
26620
  "name": "firstControlIn",
@@ -27315,6 +27457,32 @@
27315
27457
  }
27316
27458
  }
27317
27459
  ],
27460
+ "cssStates": [
27461
+ {
27462
+ "description": "The conversation has no turns.",
27463
+ "name": "empty"
27464
+ },
27465
+ {
27466
+ "description": "Older turns are loading: `history` is `loading`.",
27467
+ "name": "loading"
27468
+ },
27469
+ {
27470
+ "description": "The transcript follows its newest turn.",
27471
+ "name": "pinned"
27472
+ },
27473
+ {
27474
+ "description": "`typing` names someone on the other side.",
27475
+ "name": "typing"
27476
+ },
27477
+ {
27478
+ "description": "Set one frame after the first render. Transitions wait for it, so an element does not animate its own arrival.",
27479
+ "name": "settled",
27480
+ "inheritedFrom": {
27481
+ "name": "HelixElement",
27482
+ "module": "src/helix-element/helix-element.ts"
27483
+ }
27484
+ }
27485
+ ],
27318
27486
  "mixins": [
27319
27487
  {
27320
27488
  "name": "MixinCollection",
@@ -27359,16 +27527,6 @@
27359
27527
  },
27360
27528
  "tagName": "helix-headless-chat",
27361
27529
  "customElement": true,
27362
- "cssStates": [
27363
- {
27364
- "description": "Set one frame after the first render. Transitions wait for it, so an element does not animate its own arrival.",
27365
- "name": "settled",
27366
- "inheritedFrom": {
27367
- "name": "HelixElement",
27368
- "module": "src/helix-element/helix-element.ts"
27369
- }
27370
- }
27371
- ],
27372
27530
  "modulePath": "src/chat/root/chat-root.ts",
27373
27531
  "definitionPath": "src/chat/root/chat-root.ts"
27374
27532
  }
@@ -31404,7 +31562,7 @@
31404
31562
  "declarations": [
31405
31563
  {
31406
31564
  "kind": "class",
31407
- "description": "Renders a `<pre><code>` pair: `pre` keeps the whitespace, and `code` marks it\nas code. Long lines scroll inside the block and do not wrap, because a line\nbreak can be significant in code.\n\nA copy button is on by default. Set `no-copy` to remove it. The button copies\nthe slotted text, so there is no second copy of the code to keep in sync.\n\nFor a fragment of code inside a sentence, use `Code`.\n\nSet `line-numbers` to number the lines in a gutter beside the code, hidden\nfrom the accessibility tree and left out of a copy. Set `mark-lines` to\nmark lines a reader should look at, such as `2, 4-6`: the block draws a\nband behind each run, placed where the lines are laid out, so it follows a\nhighlighter's markup and lines that wrap. Set `wrap` to wrap long lines\nrather than scroll them; a gutter then numbers each line where it starts.\nSet `wrappable` for a button in the header that turns `wrap` on and off,\npressed while the lines wrap.\n\nThe header holds the name, the `actions` slot, and the copy button. Slot a\ncontrol into `actions`, such as a button that sets `wrap` or one that opens\nthe code in an editor, and the block draws a header for it even with no\nname. `copy-icon` and `copied-icon` replace the copy button's two icons. Set `highlight` to a\nfunction that turns the code into highlighted markup, such as one wrapping\nShiki or Prism, and the block draws what it returns; it is handed the\nblock's `language`, so one function serves every block. The code stays in\nthe slot, so the copy button and the page's own text are the code as\nwritten.\n\nSet `expandable` on a block whose height is capped, such as by\n`--code-block-max-block-size`, and a button under it shows every line and\nthen fewer again, as a docs site's long example does. The button is drawn\nonly while the cap hides something, says whether the block is expanded with\n`aria-expanded`, names the block it opens with `aria-controls`, and the block\ncarries `:state(expanded)` while it is.\n\nSet `diff` to show a change. Put a unified diff, the output of `git diff` or\n`diff -u`, in the same slot, and the block draws the lines it added and\nremoved. The block never computes a diff itself. Line numbers come from the\nhunk headers and are hidden from the accessibility tree. Changed lines are\n`<ins>` and `<del>` with the `+` and `-` kept as text, so the change does not\ndepend on color. Where a removed line is paired with the added line that\nreplaced it, the words each does not share with the other are the `changed`\npart, so the eye goes to what changed within the line. Each file in the patch is its own block under its own name,\nand its copy button copies the new version of the file. `variant=\"split\"`\nshows the old version beside the new, each removal opposite its replacement.\n`context-lines` folds each long run of unchanged lines, keeping that many\nbeside each change, behind a button that shows them and hides them again,\nas a code host draws a diff written with more context than a reader needs.\n\n**A block that scrolls is a tab stop.** While its lines run past its width or\nits cap, the scroller is a region named for the block, such as \"Code,\napp.ts\", that Tab reaches, so a keyboard scrolls it with the arrow keys in\nevery browser. Chromium and Firefox make a scrolling box focusable on their\nown, and Safari does not. A block that fits is no stop at all.\n\n**The code the block draws is marked untranslated**, `translate=\"no\"` on\nits `<pre>`, so a page translator leaves highlighted lines and a diff as\nwritten. Code in the slot takes its translation mode from the element, which\nwrites nothing onto itself, so set `translate=\"no\"` on the element to keep a\ntranslator off plain code too.\n\nOn the server, the slot renders as ordinary code, so a diff first appears as\nthe patch text in a `<pre>`. The client makes the same first render, which\nkeeps hydration clean, and draws the diff once it can read the content. Diff\nmode is experimental. The rest of the family is stable.\n\n**Several blocks are files**, one shown at a time under a row of tabs, as\nSandbox shows its source panes. Put Code Blocks in the default slot of a\nCode Block, and each is a file named on its tab by its `label` or, without\none, its `language`. `file` holds the name of the file shown. The tabs are a\ntab list: the arrow keys move to a tab and show its file, the two swapping\non a right-to-left page, and Home and End go to the first and the last.\nChoosing one sends `helix-before-change` with the file's name, which you can\ncancel, and `change` once `file` holds it. The files not shown carry\n`hidden`, and the one shown draws no name of its own, its tab naming it. One\nblock draws no tabs. A server cannot see the blocks inside, so the client\ndraws the tabs once it upgrades.",
31565
+ "description": "Renders a `<pre><code>` pair: `pre` keeps the whitespace, and `code` marks it\nas code. Long lines scroll inside the block and do not wrap, because a line\nbreak can be significant in code.\n\nA copy button is on by default. Set `no-copy` to remove it. The button copies\nthe slotted text, so there is no second copy of the code to keep in sync.\n\nFor a fragment of code inside a sentence, use `Code`.\n\nSet `line-numbers` to number the lines in a gutter beside the code, hidden\nfrom the accessibility tree and left out of a copy. Set `mark-lines` to\nmark lines a reader should look at, such as `2, 4-6`: the block draws a\nband behind each run, placed where the lines are laid out, so it follows a\nhighlighter's markup and lines that wrap. `added-lines` and\n`removed-lines` mark lines the same way as inserted and deleted, as\nExpressive Code's `ins` and `del` do, in code that is not a diff. The bands\nare drawn and hidden from screen readers, so say in the text around the\nblock what a marked line means. Set `wrap` to wrap long lines\nrather than scroll them; a gutter then numbers each line where it starts.\nSet `wrappable` for a button in the header that turns `wrap` on and off,\npressed while the lines wrap.\n\nThe header holds the name, the `actions` slot, and the copy button. Slot a\ncontrol into `actions`, such as a button that sets `wrap` or one that opens\nthe code in an editor, and the block draws a header for it even with no\nname. `copy-icon` and `copied-icon` replace the copy button's two icons. Set `highlight` to a\nfunction that turns the code into highlighted markup, such as one wrapping\nShiki or Prism, and the block draws what it returns; it is handed the\nblock's `language`, so one function serves every block. The code stays in\nthe slot, so the copy button and the page's own text are the code as\nwritten.\n\nSet `expandable` on a block whose height is capped, such as by\n`--code-block-max-block-size`, and a button under it shows every line and\nthen fewer again, as a docs site's long example does. The button is drawn\nonly while the cap hides something, says whether the block is expanded with\n`aria-expanded`, names the block it opens with `aria-controls`, and the block\ncarries `:state(expanded)` while it is.\n\nSet `diff` to show a change. Put a unified diff, the output of `git diff` or\n`diff -u`, in the same slot, and the block draws the lines it added and\nremoved. The block never computes a diff itself. Line numbers come from the\nhunk headers and are hidden from the accessibility tree. Changed lines are\n`<ins>` and `<del>` with the `+` and `-` kept as text, so the change does not\ndepend on color. Where a removed line is paired with the added line that\nreplaced it, the words each does not share with the other are the `changed`\npart, so the eye goes to what changed within the line. Each file in the patch is its own block under its own name,\nand its copy button copies the new version of the file. `variant=\"split\"`\nshows the old version beside the new, each removal opposite its replacement.\n`context-lines` folds each long run of unchanged lines, keeping that many\nbeside each change, behind a button that shows them and hides them again,\nas a code host draws a diff written with more context than a reader needs.\n\n**A block that scrolls is a tab stop.** While its lines run past its width or\nits cap, the scroller is a region named for the block, such as \"Code,\napp.ts\", that Tab reaches, so a keyboard scrolls it with the arrow keys in\nevery browser. Chromium and Firefox make a scrolling box focusable on their\nown, and Safari does not. A block that fits is no stop at all.\n\n**The code the block draws is marked untranslated**, `translate=\"no\"` on\nits `<pre>`, so a page translator leaves highlighted lines and a diff as\nwritten. Code in the slot takes its translation mode from the element, which\nwrites nothing onto itself, so set `translate=\"no\"` on the element to keep a\ntranslator off plain code too.\n\nOn the server, the slot renders as ordinary code, so a diff first appears as\nthe patch text in a `<pre>`. The client makes the same first render, which\nkeeps hydration clean, and draws the diff once it can read the content. Diff\nmode is experimental. The rest of the family is stable.\n\n**Several blocks are files**, one shown at a time under a row of tabs, as\nSandbox shows its source panes. Put Code Blocks in the default slot of a\nCode Block, and each is a file named on its tab by its `label` or, without\none, its `language`. `file` holds the name of the file shown. The tabs are a\ntab list: the arrow keys move to a tab and show its file, the two swapping\non a right-to-left page, and Home and End go to the first and the last.\nChoosing one sends `helix-before-change` with the file's name, which you can\ncancel, and `change` once `file` holds it. The files not shown carry\n`hidden`, and the one shown draws no name of its own, its tab naming it. One\nblock draws no tabs. A server cannot see the blocks inside, so the client\ndraws the tabs once it upgrades.",
31408
31566
  "name": "HelixHeadlessCodeBlock",
31409
31567
  "cssProperties": [
31410
31568
  {
@@ -31558,9 +31716,17 @@
31558
31716
  "name": "label"
31559
31717
  },
31560
31718
  {
31561
- "description": "The band behind a run of lines `mark-lines` names. The block writes where the run is laid out into `--code-block-mark-start`, `--code-block-mark-size`, and `--code-block-mark-inline-size` on it. Hidden from the accessibility tree.",
31719
+ "description": "The band behind a run of lines `mark-lines`, `added-lines` or `removed-lines` names. The block writes where the run is laid out into `--code-block-mark-start`, `--code-block-mark-size`, and `--code-block-mark-inline-size` on it. Hidden from the accessibility tree.",
31562
31720
  "name": "mark"
31563
31721
  },
31722
+ {
31723
+ "description": "The band behind a run of lines `added-lines` names. Applied with `mark`.",
31724
+ "name": "mark-added"
31725
+ },
31726
+ {
31727
+ "description": "The band behind a run of lines `removed-lines` names. Applied with `mark`.",
31728
+ "name": "mark-removed"
31729
+ },
31564
31730
  {
31565
31731
  "description": "In a diff, the `+`, `-`, or space that starts a line.",
31566
31732
  "name": "marker"
@@ -31610,6 +31776,14 @@
31610
31776
  {
31611
31777
  "description": "The copy button's icon before a copy. Defaults to a clipboard.",
31612
31778
  "name": "copy-icon"
31779
+ },
31780
+ {
31781
+ "description": "An icon on the button that shows every line of a capped block, before its words.",
31782
+ "name": "expand-icon"
31783
+ },
31784
+ {
31785
+ "description": "The block's name in the header, such as a file icon beside the file name. Replaces `label` and `language` there, and draws the header.",
31786
+ "name": "label"
31613
31787
  }
31614
31788
  ],
31615
31789
  "members": [
@@ -31623,6 +31797,16 @@
31623
31797
  "default": "'helix-code-block'",
31624
31798
  "description": "The key this family's words are registered under, its styled tag:\n`registerWords('de', { 'helix-code-block': { copyLabel: 'Code kopieren' } })`."
31625
31799
  },
31800
+ {
31801
+ "kind": "field",
31802
+ "name": "addedLines",
31803
+ "type": {
31804
+ "text": "string"
31805
+ },
31806
+ "default": "''",
31807
+ "description": "The lines to mark as inserted, as numbers and ranges counted from 1, such\nas `3-4`. Drawn as `mark-lines` is, in the added color. Not for a diff.",
31808
+ "attribute": "added-lines"
31809
+ },
31626
31810
  {
31627
31811
  "kind": "field",
31628
31812
  "name": "codeLabel",
@@ -31839,6 +32023,16 @@
31839
32023
  "attribute": "no-copy",
31840
32024
  "reflects": true
31841
32025
  },
32026
+ {
32027
+ "kind": "field",
32028
+ "name": "removedLines",
32029
+ "type": {
32030
+ "text": "string"
32031
+ },
32032
+ "default": "''",
32033
+ "description": "The lines to mark as deleted, as numbers and ranges counted from 1. Drawn\nas `mark-lines` is, in the removed color. Not for a diff.",
32034
+ "attribute": "removed-lines"
32035
+ },
31842
32036
  {
31843
32037
  "kind": "field",
31844
32038
  "name": "showUnchangedLabel",
@@ -32254,6 +32448,10 @@
32254
32448
  {
32255
32449
  "name": "drawn",
32256
32450
  "default": "name"
32451
+ },
32452
+ {
32453
+ "name": "own",
32454
+ "default": "false"
32257
32455
  }
32258
32456
  ],
32259
32457
  "description": "The name with the copy button beside it, or nothing when there is no name.\n\n**The button is in the header wherever there is one**, because beside the\ncode it stands over the block and the block has to make room for it. A\ndiff cannot: its washes run to the edge, so the button stood on the hunk\nline and the line under it. In the header it is over nothing.\n\n**No name draws no header**, unless the page put a control in `actions`\nor asked for the wrap button. A bar holding the copy button alone turned a\n`Usage` section of one-line imports into a run of empty strips. The button\nthen stands beside the code, in room the block keeps for it."
@@ -32870,6 +33068,15 @@
32870
33068
  }
32871
33069
  ],
32872
33070
  "attributes": [
33071
+ {
33072
+ "name": "added-lines",
33073
+ "type": {
33074
+ "text": "string"
33075
+ },
33076
+ "default": "''",
33077
+ "description": "The lines to mark as inserted, as numbers and ranges counted from 1, such\nas `3-4`. Drawn as `mark-lines` is, in the added color. Not for a diff.",
33078
+ "fieldName": "addedLines"
33079
+ },
32873
33080
  {
32874
33081
  "name": "code-label",
32875
33082
  "type": {
@@ -33030,6 +33237,15 @@
33030
33237
  "description": "Whether to leave the copy button off.\n\nNegative, which is not a style choice. A boolean attribute is true whenever\nit is *present*, so a `copyable` defaulting to true could never be turned\noff from markup: `copyable=\"false\"` is the string \"false\", which is\npresent, which is true. Measured, and it is why this is named the other way\nround. The platform already writes booleans like this for the same reason,\n`novalidate` and `nomodule` and `noreferrer` among them.\n\n**Reflected, because the gutter is a stylesheet rule.** A sheet reserves\nthe copy button's width with `:host(:not([no-copy]))`, so the attribute is\nwhat decides whether the block pays for a control. Unreflected, setting it\nfrom script takes the button away (`render` reads the property) and leaves\nthe 40px it stood in reserved.\n\nA false boolean reflects as the attribute's absence, so this costs no\n`no-copy=\"\"` on hosts that never asked for one, and the rule about a\nreflected property taking no default is satisfied by the default being\n`false`.",
33031
33238
  "fieldName": "noCopy"
33032
33239
  },
33240
+ {
33241
+ "name": "removed-lines",
33242
+ "type": {
33243
+ "text": "string"
33244
+ },
33245
+ "default": "''",
33246
+ "description": "The lines to mark as deleted, as numbers and ranges counted from 1. Drawn\nas `mark-lines` is, in the removed color. Not for a diff.",
33247
+ "fieldName": "removedLines"
33248
+ },
33033
33249
  {
33034
33250
  "name": "show-unchanged-label",
33035
33251
  "type": {
@@ -33603,7 +33819,7 @@
33603
33819
  "declarations": [
33604
33820
  {
33605
33821
  "kind": "class",
33606
- "description": "A text field with a button that opens `ColorPicker` in an overlay.\n`ColorPicker` provides the square, the ramps, the eyedropper, and the syntax\ntoggle, and you can also use it on its own, flat on the page. Color Input\nadds the field, the overlay, and the form value, the same arrangement Date\nInput has with `DatePicker`.\n\nYou can type or paste a color into the field without opening the picker.\nColor Input is a custom control, unlike `<input type=\"color\">`, so you can\nstyle the panel to match your interface.\n\nThe field reads every color syntax, including `#f00`, `#ff0000`,\n`rgb(255 0 0)`, `rgba(255,0,0,.5)`, `rgb(255 0 0 / 50%)`,\n`hsl(0 100% 50%)`, `oklch(62.8% 0.258 29.2)`, and `hsb(0, 100%, 100%)`. It\nwrites colors in the syntax that `format` names. The\nfield shows the value without reformatting it, because half-typed input does\nnot survive a round trip through a formatter. Date Input does the same with\ndates.\n\nTyping sends `input` on each keystroke and `change` when you leave the\nfield, as a text field does, so the message about a color it cannot read\nwaits until you are done. Dragging in the panel sends `input` as the color\nmoves and `change` when it settles.\n\nThe trigger and ArrowDown open the panel with focus in the square. Tab moves\nbetween the panel's controls without leaving it, and Escape closes it and\nreturns focus to the field.\n\nThe component sets `--color-input-swatch`, so do not set it yourself. It\nholds the resolved color, so the styled tier can paint the swatch without\nparsing the value.",
33822
+ "description": "A text field with a button that opens `ColorPicker` in an overlay.\n`ColorPicker` provides the square, the ramps, the eyedropper, and the syntax\ntoggle, and you can also use it on its own, flat on the page. Color Input\nadds the field, the overlay, and the form value, the same arrangement Date\nInput has with `DatePicker`.\n\nYou can type or paste a color into the field without opening the picker.\nColor Input is a custom control, unlike `<input type=\"color\">`, so you can\nstyle the panel to match your interface.\n\nThe field reads every color syntax, including `#f00`, `#ff0000`,\n`rgb(255 0 0)`, `rgba(255,0,0,.5)`, `rgb(255 0 0 / 50%)`,\n`hsl(0 100% 50%)`, `oklch(62.8% 0.258 29.2)`, and `hsb(0, 100%, 100%)`. It\nwrites colors in the syntax that `format` names. The\nfield shows the value without reformatting it, because half-typed input does\nnot survive a round trip through a formatter. Date Input does the same with\ndates.\n\nTyping sends `input` on each keystroke and `change` when you leave the\nfield, as a text field does, so the message about a color it cannot read\nwaits until you are done. Dragging in the panel sends `input` as the color\nmoves and `change` when it settles.\n\nThe trigger and ArrowDown open the panel with focus in the square. Tab moves\nbetween the panel's controls without leaving it, and Escape closes it and\nreturns focus to the control that opened it: the trigger after a press, and\nthe field after ArrowDown.\n\nThe component sets `--color-input-swatch`, so do not set it yourself. It\nholds the resolved color, so the styled tier can paint the swatch without\nparsing the value.",
33607
33823
  "name": "HelixHeadlessColorInput",
33608
33824
  "cssProperties": [
33609
33825
  {
@@ -34203,6 +34419,38 @@
34203
34419
  "module": "src/mixins/mixin-picker-field/mixin-picker-field.ts"
34204
34420
  }
34205
34421
  },
34422
+ {
34423
+ "kind": "field",
34424
+ "name": "pressedOpen",
34425
+ "type": {
34426
+ "text": "boolean"
34427
+ },
34428
+ "privacy": "private",
34429
+ "default": "false",
34430
+ "description": "Whether the trigger's press opened the panel that is open now."
34431
+ },
34432
+ {
34433
+ "kind": "field",
34434
+ "name": "handlePress",
34435
+ "privacy": "private",
34436
+ "readonly": true,
34437
+ "description": "The trigger's press, which notes that it opened the panel, so Escape hands\nfocus back to the trigger."
34438
+ },
34439
+ {
34440
+ "kind": "method",
34441
+ "name": "returnFocus",
34442
+ "privacy": "protected",
34443
+ "return": {
34444
+ "type": {
34445
+ "text": "void"
34446
+ }
34447
+ },
34448
+ "description": "Hands focus back to the control that opened the panel: the trigger after\nits press, which is the APG date picker dialog's rule, and the text after\nArrowDown from it.",
34449
+ "inheritedFrom": {
34450
+ "name": "MixinPickerField",
34451
+ "module": "src/mixins/mixin-picker-field/mixin-picker-field.ts"
34452
+ }
34453
+ },
34206
34454
  {
34207
34455
  "kind": "method",
34208
34456
  "name": "renderPicker",
@@ -34451,21 +34699,6 @@
34451
34699
  "module": "src/mixins/mixin-picker-field/mixin-picker-field.ts"
34452
34700
  }
34453
34701
  },
34454
- {
34455
- "kind": "method",
34456
- "name": "returnFocus",
34457
- "privacy": "protected",
34458
- "return": {
34459
- "type": {
34460
- "text": "void"
34461
- }
34462
- },
34463
- "description": "Puts the caret back in the field without the field reopening the panel.\n\nThe anchor rather than the text, which is the trigger where there is no\ntext: Color Input's swatch dropped focus to the body on Escape.",
34464
- "inheritedFrom": {
34465
- "name": "MixinPickerField",
34466
- "module": "src/mixins/mixin-picker-field/mixin-picker-field.ts"
34467
- }
34468
- },
34469
34702
  {
34470
34703
  "kind": "method",
34471
34704
  "name": "focus",
@@ -36524,7 +36757,7 @@
36524
36757
  "name": "eyedropper-text"
36525
36758
  },
36526
36759
  {
36527
- "description": "The message under the picker, while `feedback` is set.",
36760
+ "description": "The message under the picker: `feedback`, or what is wrong with the answer once the picker is marked invalid.",
36528
36761
  "name": "feedback"
36529
36762
  },
36530
36763
  {
@@ -36938,7 +37171,7 @@
36938
37171
  "text": "boolean"
36939
37172
  },
36940
37173
  "default": "false",
36941
- "description": "Requires a color before the form can submit.\n\n**A color is exactly as requirable as a date**, which is the comparison\nthat settles it: this element extends `HelixFormElement`, carries `name`\nand reports its value, so a consumer writing `<helix-headless-color-picker\nname=\"brand\" required>` inside a form has every reason to expect the form\nto refuse an empty one. Its two siblings in the same composition position,\nDate Picker and Time Picker, both check it.\n\nWritten out rather than taken from `MixinField`, for the reason those two\nrecord: the mixin brings a label, a hint and a feedback region, and this is\na panel composed *into* a field that already has all three, so taking it\nwould give one control two of each. What is left is the validity call and\nthe anchor the browser points its bubble at.\n\n**Text that is not a color is bad input**, so a form does not submit\n`#00` from a half-typed value. The picker draws no message of its own, so\nreporting it as each keystroke arrives shows nothing until the form asks;\nColor Input shows its own message against the field the reader typed\ninto.",
37174
+ "description": "Requires a color before the form can submit.\n\n**A color is exactly as requirable as a date**, which is the comparison\nthat settles it: this element extends `HelixFormElement`, carries `name`\nand reports its value, so a consumer writing `<helix-headless-color-picker\nname=\"brand\" required>` inside a form has every reason to expect the form\nto refuse an empty one. Its two siblings in the same composition position,\nDate Picker and Time Picker, both check it.\n\nWritten out rather than taken from `MixinField`, for the reason those two\nrecord: the mixin brings a label, a hint and a feedback region, and this is\na panel composed *into* a field that already has all three, so taking it\nwould give one control two of each. What is left is the validity call and\nthe anchor the browser points its bubble at.\n\n**Text that is not a color is bad input**, so a form does not submit\n`#00` from a half-typed value. The picker marks it, and says\n`badInputMessage` under itself, once the reader leaves the value field or\npresses Enter, and a missing color once the reader has committed one or a\nform has refused the picker. Color Input shows its own message against the\nfield the reader typed into.",
36942
37175
  "attribute": "required",
36943
37176
  "reflects": true
36944
37177
  },
@@ -37111,6 +37344,82 @@
37111
37344
  "description": "The ids a control in the picker is described by: the help, then the message.",
37112
37345
  "readonly": true
37113
37346
  },
37347
+ {
37348
+ "kind": "field",
37349
+ "name": "typing",
37350
+ "type": {
37351
+ "text": "boolean"
37352
+ },
37353
+ "privacy": "private",
37354
+ "default": "false",
37355
+ "description": "Whether the reader is typing into the value field, between the first\nkeystroke and the `change` that ends it.\n\nHalf-typed text is rarely a color: `#00ff00` passes through `#0` and\n`#00ff0` on the way. So text that is not a color is refused once the\nreader leaves the field or presses Enter, and not on every keystroke."
37356
+ },
37357
+ {
37358
+ "kind": "field",
37359
+ "name": "draft",
37360
+ "type": {
37361
+ "text": "string | undefined"
37362
+ },
37363
+ "privacy": "private",
37364
+ "default": "undefined",
37365
+ "description": "Text in the value field that is not a color, or undefined while the field\nholds the value.\n\n**Kept in the field and out of the value**, as a native number field keeps\n\"12e\": the text stays where the reader can correct it, the value stays the\nlast color, and the picker reports `badInput` until the text is a color or\nthe value moves. Measured before this: typed \"nonsense\" became the value,\nso a form read `brand=nonsense` and the square had no color to show."
37366
+ },
37367
+ {
37368
+ "kind": "field",
37369
+ "name": "failure",
37370
+ "type": {
37371
+ "text": "'missing' | 'refused' | undefined"
37372
+ },
37373
+ "privacy": "protected",
37374
+ "description": "A required picker with no color is missing, and text that is not a color\nis refused, once the reader has finished typing it. See\n`MixinPickerFeedback.marked`.\n\nMeasured before this: typed \"nonsense\" and a required picker its form\nrefused both failed `checkValidity()` while `:state(invalid)` stayed off,\nthe value field had no `aria-invalid`, and nothing was said.",
37375
+ "readonly": true,
37376
+ "inheritedFrom": {
37377
+ "name": "MixinPickerFeedback",
37378
+ "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
37379
+ }
37380
+ },
37381
+ {
37382
+ "kind": "field",
37383
+ "name": "said",
37384
+ "type": {
37385
+ "text": "string"
37386
+ },
37387
+ "privacy": "private",
37388
+ "description": "The line under the picker: `feedback`, or, where the picker is marked by\nits own validation, what is wrong.",
37389
+ "readonly": true
37390
+ },
37391
+ {
37392
+ "kind": "method",
37393
+ "name": "formResetCallback",
37394
+ "return": {
37395
+ "type": {
37396
+ "text": "void"
37397
+ }
37398
+ },
37399
+ "description": "A reset ends any typing and takes back text that was not a color, so the\nfield shows the color the reset put back.",
37400
+ "inheritedFrom": {
37401
+ "name": "HelixFormElement",
37402
+ "module": "src/helix-form-element/helix-form-element.ts"
37403
+ },
37404
+ "type": {
37405
+ "text": "formResetCallback() => void"
37406
+ }
37407
+ },
37408
+ {
37409
+ "kind": "method",
37410
+ "name": "renderPickerFeedback",
37411
+ "privacy": "protected",
37412
+ "return": {
37413
+ "type": {
37414
+ "text": "TemplateResult | typeof nothing"
37415
+ }
37416
+ },
37417
+ "description": "The line `said` holds, or nothing while it is empty. `role=\"alert\"`, as\nevery field's feedback line has, so the message is announced when it\nappears.",
37418
+ "inheritedFrom": {
37419
+ "name": "MixinPickerFeedback",
37420
+ "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
37421
+ }
37422
+ },
37114
37423
  {
37115
37424
  "kind": "field",
37116
37425
  "name": "dragging",
@@ -37635,52 +37944,6 @@
37635
37944
  "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
37636
37945
  }
37637
37946
  },
37638
- {
37639
- "kind": "field",
37640
- "name": "failure",
37641
- "type": {
37642
- "text": "'missing' | 'refused' | undefined"
37643
- },
37644
- "privacy": "protected",
37645
- "description": "What the picker's own validation finds: `missing`, `refused`, or\nnothing, which is what a picker that does not answer it reports.",
37646
- "readonly": true,
37647
- "inheritedFrom": {
37648
- "name": "MixinPickerFeedback",
37649
- "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
37650
- }
37651
- },
37652
- {
37653
- "kind": "method",
37654
- "name": "formResetCallback",
37655
- "return": {
37656
- "type": {
37657
- "text": "void"
37658
- }
37659
- },
37660
- "description": "Returns the control to its `defaultValue`, or to its `value` attribute\nwhere there is no `default-value`.\n\nBoth halves are dropped, the consumer's and the control's, because a\nreset means the form is as it was served. Clearing only the internal one\nwould leave a control that had been written to stuck at that value.",
37661
- "inheritedFrom": {
37662
- "name": "HelixFormElement",
37663
- "module": "src/helix-form-element/helix-form-element.ts"
37664
- },
37665
- "type": {
37666
- "text": "formResetCallback() => void"
37667
- }
37668
- },
37669
- {
37670
- "kind": "method",
37671
- "name": "renderPickerFeedback",
37672
- "privacy": "protected",
37673
- "return": {
37674
- "type": {
37675
- "text": "TemplateResult | typeof nothing"
37676
- }
37677
- },
37678
- "description": "The `feedback` line, or nothing while there is none.",
37679
- "inheritedFrom": {
37680
- "name": "MixinPickerFeedback",
37681
- "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
37682
- }
37683
- },
37684
37947
  {
37685
37948
  "kind": "field",
37686
37949
  "name": "defaultValue",
@@ -38861,7 +39124,7 @@
38861
39124
  "text": "boolean"
38862
39125
  },
38863
39126
  "default": "false",
38864
- "description": "Requires a color before the form can submit.\n\n**A color is exactly as requirable as a date**, which is the comparison\nthat settles it: this element extends `HelixFormElement`, carries `name`\nand reports its value, so a consumer writing `<helix-headless-color-picker\nname=\"brand\" required>` inside a form has every reason to expect the form\nto refuse an empty one. Its two siblings in the same composition position,\nDate Picker and Time Picker, both check it.\n\nWritten out rather than taken from `MixinField`, for the reason those two\nrecord: the mixin brings a label, a hint and a feedback region, and this is\na panel composed *into* a field that already has all three, so taking it\nwould give one control two of each. What is left is the validity call and\nthe anchor the browser points its bubble at.\n\n**Text that is not a color is bad input**, so a form does not submit\n`#00` from a half-typed value. The picker draws no message of its own, so\nreporting it as each keystroke arrives shows nothing until the form asks;\nColor Input shows its own message against the field the reader typed\ninto.",
39127
+ "description": "Requires a color before the form can submit.\n\n**A color is exactly as requirable as a date**, which is the comparison\nthat settles it: this element extends `HelixFormElement`, carries `name`\nand reports its value, so a consumer writing `<helix-headless-color-picker\nname=\"brand\" required>` inside a form has every reason to expect the form\nto refuse an empty one. Its two siblings in the same composition position,\nDate Picker and Time Picker, both check it.\n\nWritten out rather than taken from `MixinField`, for the reason those two\nrecord: the mixin brings a label, a hint and a feedback region, and this is\na panel composed *into* a field that already has all three, so taking it\nwould give one control two of each. What is left is the validity call and\nthe anchor the browser points its bubble at.\n\n**Text that is not a color is bad input**, so a form does not submit\n`#00` from a half-typed value. The picker marks it, and says\n`badInputMessage` under itself, once the reader leaves the value field or\npresses Enter, and a missing color once the reader has committed one or a\nform has refused the picker. Color Input shows its own message against the\nfield the reader typed into.",
38865
39128
  "fieldName": "required"
38866
39129
  },
38867
39130
  {
@@ -39566,7 +39829,7 @@
39566
39829
  "text": "boolean"
39567
39830
  },
39568
39831
  "default": "false",
39569
- "description": "Whether more than one option can be chosen.\n\nThe list already does the work: `value` comes apart on commas, a press\ntoggles rather than replaces, and the arrows move without choosing. What\nthis adds is the tags, and keeping the panel open between picks.\n\nA comma is therefore not available inside an option's value, which is the\nlistbox's contract and not this element's to change.\n\n**The list is told, and the field is not.** `aria-multiselectable` belongs\nto `listbox`, `grid`, `tree` and `tablist`, and `combobox` dropped it in\nARIA 1.2; the list sets it on itself through `ElementInternals`, where a\nreader on the field hears it through `aria-controls`. This element wrote it\nonto its own `role=\"combobox\"` input as well, which an audit reports as a\nproperty the role does not support and no spec here ever asserted: the one\nthat covers this says in as many words that telling the list is what this\nelement is responsible for.",
39832
+ "description": "Whether more than one option can be chosen.\n\nThe list already does the work: `value` is the chosen values joined with\ncommas, a press toggles rather than replaces, and the arrows move without\nchoosing. What this adds is the tags, and keeping the panel open between\npicks.\n\nAn option's value may hold a comma, and it is read as one value, as\n`Select` and `Listbox` read it. So may an answer the reader created.\n\n**The list is told, and the field is not.** `aria-multiselectable` belongs\nto `listbox`, `grid`, `tree` and `tablist`, and `combobox` dropped it in\nARIA 1.2; the list sets it on itself through `ElementInternals`, where a\nreader on the field hears it through `aria-controls`. This element wrote it\nonto its own `role=\"combobox\"` input as well, which an audit reports as a\nproperty the role does not support and no spec here ever asserted: the one\nthat covers this says in as many words that telling the list is what this\nelement is responsible for.",
39570
39833
  "attribute": "multiple"
39571
39834
  },
39572
39835
  {
@@ -39852,9 +40115,17 @@
39852
40115
  "text": "string[]"
39853
40116
  },
39854
40117
  "privacy": "private",
39855
- "description": "The values chosen, in the order they were chosen.",
40118
+ "description": "The values chosen, in the order they were chosen, read against the\noptions' values and the answers created here, so a value that holds a\ncomma is one value. See `chosenValues`.\n\nMeasured before this: choosing `Paris, France` drew two tags, \"Paris\" and\n\"France\", and the form received two entries.",
39856
40119
  "readonly": true
39857
40120
  },
40121
+ {
40122
+ "kind": "field",
40123
+ "name": "created",
40124
+ "privacy": "private",
40125
+ "readonly": true,
40126
+ "default": "new Set<string>()",
40127
+ "description": "The answers created here, which no option holds, kept so their commas read as theirs."
40128
+ },
39858
40129
  {
39859
40130
  "kind": "field",
39860
40131
  "name": "chosen",
@@ -42167,7 +42438,7 @@
42167
42438
  "text": "boolean"
42168
42439
  },
42169
42440
  "default": "false",
42170
- "description": "Whether more than one option can be chosen.\n\nThe list already does the work: `value` comes apart on commas, a press\ntoggles rather than replaces, and the arrows move without choosing. What\nthis adds is the tags, and keeping the panel open between picks.\n\nA comma is therefore not available inside an option's value, which is the\nlistbox's contract and not this element's to change.\n\n**The list is told, and the field is not.** `aria-multiselectable` belongs\nto `listbox`, `grid`, `tree` and `tablist`, and `combobox` dropped it in\nARIA 1.2; the list sets it on itself through `ElementInternals`, where a\nreader on the field hears it through `aria-controls`. This element wrote it\nonto its own `role=\"combobox\"` input as well, which an audit reports as a\nproperty the role does not support and no spec here ever asserted: the one\nthat covers this says in as many words that telling the list is what this\nelement is responsible for.",
42441
+ "description": "Whether more than one option can be chosen.\n\nThe list already does the work: `value` is the chosen values joined with\ncommas, a press toggles rather than replaces, and the arrows move without\nchoosing. What this adds is the tags, and keeping the panel open between\npicks.\n\nAn option's value may hold a comma, and it is read as one value, as\n`Select` and `Listbox` read it. So may an answer the reader created.\n\n**The list is told, and the field is not.** `aria-multiselectable` belongs\nto `listbox`, `grid`, `tree` and `tablist`, and `combobox` dropped it in\nARIA 1.2; the list sets it on itself through `ElementInternals`, where a\nreader on the field hears it through `aria-controls`. This element wrote it\nonto its own `role=\"combobox\"` input as well, which an audit reports as a\nproperty the role does not support and no spec here ever asserted: the one\nthat covers this says in as many words that telling the list is what this\nelement is responsible for.",
42171
42442
  "fieldName": "multiple"
42172
42443
  },
42173
42444
  {
@@ -42597,7 +42868,7 @@
42597
42868
  "declarations": [
42598
42869
  {
42599
42870
  "kind": "class",
42600
- "description": "The list is a `Listbox`, which handles filtering, the empty state, the match\nhighlight, `aria-activedescendant`, scrolling an option into view, and moving\nbetween options with the keyboard. The palette adds a dialog and a hotkey\nthat opens it.\n\nThe palette keeps no value. It is not a form control and submits nothing.\nChoosing an option dispatches `helix-select` once. To remember the last\ncommand, store it yourself. An option with `href` is a link to a page, which\nthe browser follows once the palette has closed.\n\nThe hotkey listener is on the document, so the hotkey works from anywhere on\nthe page, including inside another control's text field, whether or not the\npalette is open. `mod` means Command on a Mac and Control elsewhere, so you\nwrite one binding.\n\nWith `inline`, the palette is drawn in the page with no dialog, as a search\nover a list that is always shown.\n\n**Pages.** A command can open a page of commands of its own, as a palette\noffers \"Change theme\" and then the themes. Give the option `opens` with a\nname, and slot the page's commands as `page:` and that name:\n\n```html\n<helix-command>\n <helix-option value=\"theme\" opens=\"theme\">Change theme</helix-option>\n <helix-option value=\"dark\" slot=\"page:theme\">Dark</helix-option>\n <helix-option value=\"light\" slot=\"page:theme\">Light</helix-option>\n</helix-command>\n```\n\nChoosing the opener says `helix-select` with its value, and canceling it\nkeeps the page that is shown. The list then shows the page, named by the\nopener's words, and a screen reader hears those words. Backspace in an empty\nfilter goes back, and a screen reader hears the name of the page it goes\nback to. `page` says which page is shown.\n\nThe filter is cleared when the palette closes, so each opening starts from\nthe full list. Combobox and Select do the same. As typing narrows the list, a\nscreen reader hears how many commands are left, such as \"3 commands\".\n\n**Keyboard.** Focus goes to the filter field as the palette opens, and the\nhighlight moves through the commands while you type.\n\n| Key | What it does |\n|---|---|\n| `hotkey`, Command or Control with K by default | Opens the palette from anywhere on the page, or closes it. With `inline`, puts focus in its filter field. |\n| Down, Up | Moves the highlight to the next or the previous command, round from the last to the first. |\n| Page Down, Page Up | Moves the highlight by as many commands as the list shows at once. |\n| Home, End, Left, Right | Move the caret in the filter field. |\n| Enter | Runs the highlighted command, or follows it where it is a link, and closes the palette. On a command with `opens`, shows its page. |\n| Command or Control with Enter | Takes the highlighted command's `alternate` destination, where it has one. |\n| Backspace | In an empty filter on a page, goes back to the page before. |\n| Escape | Clears the filter. With the filter empty, closes the palette and puts focus back where it was. |\n| Tab | Stays in the palette, which is modal. With `inline`, moves on through the page. |\n\n**ARIA.** The palette is a modal `dialog` named by `label`, or with\n`inline` no dialog at all. Inside it the filter field is a text field with\n`aria-autocomplete=\"list\"`, `aria-controls` on the list, and\n`aria-activedescendant` on the highlighted command, as the field of React\nAria's `Autocomplete` is. The commands are a `listbox` of `option`s. How many commands typing left, the empty text and\nthe loading text are said in a polite live region.",
42871
+ "description": "The list is a `Listbox`, which handles filtering, the empty state, the match\nhighlight, `aria-activedescendant`, scrolling an option into view, and moving\nbetween options with the keyboard. The palette adds a dialog and a hotkey\nthat opens it.\n\nThe palette keeps no value. It is not a form control and submits nothing.\nChoosing an option dispatches `helix-select` once. To remember the last\ncommand, store it yourself. An option with `href` is a link to a page, which\nthe browser follows once the palette has closed.\n\nThe hotkey listener is on the document, so the hotkey works from anywhere on\nthe page, including inside another control's text field, whether or not the\npalette is open. `mod` means Command on a Mac and Control elsewhere, so you\nwrite one binding.\n\nWith `inline`, the palette is drawn in the page with no dialog, as a search\nover a list that is always shown.\n\n**Pages.** A command can open a page of commands of its own, as a palette\noffers \"Change theme\" and then the themes. Give the option `opens` with a\nname, and slot the page's commands as `page:` and that name:\n\n```html\n<helix-command>\n <helix-option value=\"theme\" opens=\"theme\">Change theme</helix-option>\n <helix-option value=\"dark\" slot=\"page:theme\">Dark</helix-option>\n <helix-option value=\"light\" slot=\"page:theme\">Light</helix-option>\n</helix-command>\n```\n\nChoosing the opener says `helix-select` with its value, and canceling it\nkeeps the page that is shown. The list then shows the page, named by the\nopener's words, and a screen reader hears those words. Backspace in an empty\nfilter goes back, and a screen reader hears the name of the page it goes\nback to. `page` says which page is shown.\n\nThe filter is cleared when the palette closes, so each opening starts from\nthe full list. Combobox and Select do the same. As typing narrows the list, a\nscreen reader hears how many commands are left, such as \"3 commands\".\n\n**Keyboard.** Focus goes to the filter field as the palette opens, and the\nhighlight moves through the commands while you type.\n\n| Key | What it does |\n|---|---|\n| `hotkey`, Command or Control with K by default | Opens the palette from anywhere on the page, or closes it. With `inline`, puts focus in its filter field. |\n| Down, Up | Moves the highlight to the next or the previous command, round from the last to the first. |\n| Page Down, Page Up | Moves the highlight by as many commands as the list shows at once. |\n| Home, End | Move the caret in the filter field. With `home-end=\"list\"`, move the highlight to the first or the last command. |\n| Left, Right | Move the caret in the filter field. |\n| Enter | Runs the highlighted command, or follows it where it is a link, and closes the palette. On a command with `opens`, shows its page. |\n| Command or Control with Enter | Takes the highlighted command's `alternate` destination, where it has one. |\n| Backspace | In an empty filter on a page, goes back to the page before. |\n| Escape | Clears the filter. With the filter empty, closes the palette and puts focus back where it was. |\n| Tab | Stays in the palette, which is modal. With `inline`, moves on through the page. |\n\n**ARIA.** The palette is a modal `dialog` named by `label`, or with\n`inline` no dialog at all. Inside it the filter field is a text field with\n`aria-autocomplete=\"list\"`, `aria-controls` on the list, and\n`aria-activedescendant` on the highlighted command, as the field of React\nAria's `Autocomplete` is. The commands are a `listbox` of `option`s. How many commands typing left, the empty text and\nthe loading text are said in a polite live region.",
42601
42872
  "name": "HelixHeadlessCommand",
42602
42873
  "cssParts": [
42603
42874
  {
@@ -42761,6 +43032,15 @@
42761
43032
  },
42762
43033
  "description": "Whether a command is kept by the filter: a function given the command's\nwords and the filter, trimmed, that returns `true` to keep it showing, as\nReact Aria's `Autocomplete` takes `filter`. See Listbox's `filterMatch`.\n\nAbsent, a command is kept where its words hold the filter, in any case. A\nproperty only, there being no way to write a function in markup."
42763
43034
  },
43035
+ {
43036
+ "kind": "field",
43037
+ "name": "homeEnd",
43038
+ "type": {
43039
+ "text": "'list' | undefined"
43040
+ },
43041
+ "description": "Where Home and End go from the filter field.\n\nAbsent, they move the caret, as APG's combobox has them. `list` moves the\nhighlight to the first or the last command, as React Aria's Autocomplete\ndoes, and Shift with either still selects the text.",
43042
+ "attribute": "home-end"
43043
+ },
42764
43044
  {
42765
43045
  "kind": "field",
42766
43046
  "name": "hotkey",
@@ -42804,6 +43084,16 @@
42804
43084
  "words": true
42805
43085
  }
42806
43086
  },
43087
+ {
43088
+ "kind": "field",
43089
+ "name": "layout",
43090
+ "type": {
43091
+ "text": "'grid' | 'stack' | undefined"
43092
+ },
43093
+ "description": "How the commands are laid out, as Listbox's `layout`: `grid` draws them in\nrows and columns, for a palette of icons or emoji, and the arrow keys\nfollow the rows and the columns. Absent is one command under another.",
43094
+ "attribute": "layout",
43095
+ "reflects": true
43096
+ },
42807
43097
  {
42808
43098
  "kind": "field",
42809
43099
  "name": "loadError",
@@ -43629,6 +43919,14 @@
43629
43919
  "description": "How long typing rests before the commands are filtered, in milliseconds,\nso a palette filtered on a server sends one `helix-filter` for a word.\n`0` filters on every keystroke. See Listbox's `filter-delay`.",
43630
43920
  "fieldName": "filterDelay"
43631
43921
  },
43922
+ {
43923
+ "name": "home-end",
43924
+ "type": {
43925
+ "text": "'list' | undefined"
43926
+ },
43927
+ "description": "Where Home and End go from the filter field.\n\nAbsent, they move the caret, as APG's combobox has them. `list` moves the\nhighlight to the first or the last command, as React Aria's Autocomplete\ndoes, and Shift with either still selects the text.",
43928
+ "fieldName": "homeEnd"
43929
+ },
43632
43930
  {
43633
43931
  "name": "hotkey",
43634
43932
  "type": {
@@ -43656,6 +43954,14 @@
43656
43954
  "description": "Accessible name for the overlay, which has no visible heading.",
43657
43955
  "fieldName": "label"
43658
43956
  },
43957
+ {
43958
+ "name": "layout",
43959
+ "type": {
43960
+ "text": "'grid' | 'stack' | undefined"
43961
+ },
43962
+ "description": "How the commands are laid out, as Listbox's `layout`: `grid` draws them in\nrows and columns, for a palette of icons or emoji, and the arrow keys\nfollow the rows and the columns. Absent is one command under another.",
43963
+ "fieldName": "layout"
43964
+ },
43659
43965
  {
43660
43966
  "name": "load-error",
43661
43967
  "type": {
@@ -45998,7 +46304,7 @@
45998
46304
  "name": "select-selected"
45999
46305
  },
46000
46306
  {
46001
- "description": "The grid.",
46307
+ "description": "The grid. While `windowed`, the box that scrolls, which needs only a block size on this tier.",
46002
46308
  "name": "table"
46003
46309
  },
46004
46310
  {
@@ -46640,7 +46946,7 @@
46640
46946
  "text": "boolean"
46641
46947
  },
46642
46948
  "default": "false",
46643
- "description": "Draws only the rows in view, with empty space in place of the rest.\n\n**Not a `mode`.** `mode` says where the rows come from and who filtered\nthem; this says how many of the ones we hold get drawn. They are\nindependent: a consumer fetching five thousand rows a chunk at a time wants\n`mode=\"server\"` and this at once, and folding this into `mode` would be an\nAPI that forbids that combination for no reason.\n\n**Composes with `paginated` rather than excluding it.** A window over a\npage is coherent -- the page is the slice of the result, the window is the\npart of that slice on screen -- and the arithmetic is the same either way,\nso there is nothing to forbid. Together they suit a table paging at a\nthousand rows, where the page is a reasonable fetch and a poor render.\n\nOff by default. Windowing costs a scroll listener, a measurement per row\ndrawn, and a `block-size` on the scrolling box, and a table of twenty rows\nshould pay none of them.",
46949
+ "description": "Draws only the rows in view, with empty space in place of the rest.\n\n**Not a `mode`.** `mode` says where the rows come from and who filtered\nthem; this says how many of the ones we hold get drawn. They are\nindependent: a consumer fetching five thousand rows a chunk at a time wants\n`mode=\"server\"` and this at once, and folding this into `mode` would be an\nAPI that forbids that combination for no reason.\n\n**Composes with `paginated` rather than excluding it.** A window over a\npage is coherent -- the page is the slice of the result, the window is the\npart of that slice on screen -- and the arithmetic is the same either way,\nso there is nothing to forbid. Together they suit a table paging at a\nthousand rows, where the page is a reasonable fetch and a poor render.\n\nOff by default. Windowing costs a scroll listener, a measurement per row\ndrawn, and a `block-size` on the scrolling box, and a table of twenty rows\nshould pay none of them.\n\nWhile windowed, the `table` part scrolls on this tier: it carries\n`data-scroller`, and `core` ships `[data-scroller] { overflow: auto }` into\nevery shadow root. You still need to give it a block size.",
46644
46950
  "attribute": "windowed",
46645
46951
  "reflects": true
46646
46952
  },
@@ -49653,7 +49959,7 @@
49653
49959
  "text": "boolean"
49654
49960
  },
49655
49961
  "default": "false",
49656
- "description": "Draws only the rows in view, with empty space in place of the rest.\n\n**Not a `mode`.** `mode` says where the rows come from and who filtered\nthem; this says how many of the ones we hold get drawn. They are\nindependent: a consumer fetching five thousand rows a chunk at a time wants\n`mode=\"server\"` and this at once, and folding this into `mode` would be an\nAPI that forbids that combination for no reason.\n\n**Composes with `paginated` rather than excluding it.** A window over a\npage is coherent -- the page is the slice of the result, the window is the\npart of that slice on screen -- and the arithmetic is the same either way,\nso there is nothing to forbid. Together they suit a table paging at a\nthousand rows, where the page is a reasonable fetch and a poor render.\n\nOff by default. Windowing costs a scroll listener, a measurement per row\ndrawn, and a `block-size` on the scrolling box, and a table of twenty rows\nshould pay none of them.",
49962
+ "description": "Draws only the rows in view, with empty space in place of the rest.\n\n**Not a `mode`.** `mode` says where the rows come from and who filtered\nthem; this says how many of the ones we hold get drawn. They are\nindependent: a consumer fetching five thousand rows a chunk at a time wants\n`mode=\"server\"` and this at once, and folding this into `mode` would be an\nAPI that forbids that combination for no reason.\n\n**Composes with `paginated` rather than excluding it.** A window over a\npage is coherent -- the page is the slice of the result, the window is the\npart of that slice on screen -- and the arithmetic is the same either way,\nso there is nothing to forbid. Together they suit a table paging at a\nthousand rows, where the page is a reasonable fetch and a poor render.\n\nOff by default. Windowing costs a scroll listener, a measurement per row\ndrawn, and a `block-size` on the scrolling box, and a table of twenty rows\nshould pay none of them.\n\nWhile windowed, the `table` part scrolls on this tier: it carries\n`data-scroller`, and `core` ships `[data-scroller] { overflow: auto }` into\nevery shadow root. You still need to give it a block size.",
49657
49963
  "fieldName": "windowed"
49658
49964
  },
49659
49965
  {
@@ -55833,7 +56139,7 @@
55833
56139
  "text": "TemplateResult | typeof nothing"
55834
56140
  }
55835
56141
  },
55836
- "description": "The `feedback` line, or nothing while there is none.",
56142
+ "description": "The `feedback` line, or nothing while there is none.\n\n`role=\"alert\"`, as a field's feedback line has, so the message is\nannounced when it appears and the label and hint are not read with it.",
55837
56143
  "inheritedFrom": {
55838
56144
  "name": "MixinPickerFeedback",
55839
56145
  "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
@@ -58508,7 +58814,7 @@
58508
58814
  "name": "commands",
58509
58815
  "privacy": "protected",
58510
58816
  "readonly": true,
58511
- "default": "new CommandController(this, { commands: { '--close': () => this.close(), '--show': () => this.show(), '--toggle': () => this.toggleOpen(), }, })"
58817
+ "default": "new CommandController(this, { commands: { '--close': () => this.close(), '--show': () => this.show(), '--toggle': () => this.toggleOpen(), }, expanded: () => this.open, })"
58512
58818
  },
58513
58819
  {
58514
58820
  "kind": "field",
@@ -61244,7 +61550,7 @@
61244
61550
  "declarations": [
61245
61551
  {
61246
61552
  "kind": "class",
61247
- "description": "Uses `MixinControllerOpen`, so `open`, `defaultOpen`, and the\n`helix-before-open` and `helix-before-close` events work the same way as on\nan accordion item.\n\nThe drawer is modal by default, which means four things:\n\n- focus is trapped inside: `Tab` from the last control goes to the first,\n and `Shift+Tab` from the first to the last, as in `Dialog`\n- Escape and a press outside close it\n- the page behind is covered\n- the page behind does not scroll\n\nA drawer with `modal=\"false\"` leaves the page usable and still closes on\nEscape, in a browser without `closedby` as well.\n\nSet `persistent` for a drawer that has to be answered: Escape, a press\noutside, and a swipe leave it open, and the close control and `close()`\nstill close it. `closedby=\"none\"` keeps it open on all three as well, and\n`closedby=\"closerequest\"` keeps it open on a press outside.\n\n**A touch or a pen swipes it away**, toward the edge it came from, resolved\nagainst the writing direction. A swipe past halfway, or a quick one, closes\nit through the same request as every other way out. `snap-points` lists the\nfractions of the panel a reader can leave on screen, such as `0.5 1` for a\nsheet that rests half open: it opens at the smallest, and a swipe settles at\nthe nearest. While a swipe moves the panel, the distance is written on it as\n`--drawer-swipe-x` and `--drawer-swipe-y`. `swipe=\"false\"` turns swiping off.\n\n`snap` is the snap point the panel rests at, and `:state(snapped)` is on the\nhost while that is less than the whole panel. `snapTo()` moves it to\nanother. **Focus moving to a control past the\nwindow's edge opens the panel to its largest snap point**, so a reader\nmoving through a half open sheet with `Tab` never lands on a control they\ncannot see.\n\nSet `dismissible` to draw a close control in the corner, labeled by\n`dismiss-label`, as `Dialog` draws one.\n\nThe panel's name is `label` when you set it. Otherwise it is the heading in\nthe `header` slot, or the elements an `aria-labelledby` on the host names,\nand `fallback-label` where nothing names it. A development build warns\nwhen a drawer opens with nothing but `fallback-label` to name it.\n\nThe panel is described by the words at the start of its body, as `Dialog`\nis: the elements in the default slot before the first one that is or holds\na control. An `aria-describedby` on the host replaces it.\n\n**Keyboard.** Opening moves focus to the first control in the panel.\n\n| Key | What it does |\n|---|---|\n| Tab, Shift+Tab | In a modal drawer, moves through the controls in the panel, from the last round to the first and from the first round to the last. In a drawer that is not modal, moves on to the page. |\n| Escape | Asks to close the drawer. A `helix-before-close` listener can refuse, and `persistent` or `closedby=\"none\"` keeps it open. |\n| Enter, Space | On the close control, closes the drawer. |\n\nContent stays mounted when the drawer is closed, so it is in the server\nmarkup and can animate. A closed `<dialog>` is already out of the tab order\nand the accessibility tree, so the drawer does not write `inert`. The `role`,\n`aria-modal`, and the scrim also come from the `<dialog>` element.\n`drawer-root.ssr.spec.ts` checks that the panel has no hand-written `inert`\nor `role`.\n\nOpen the drawer declaratively with the platform's invoker attributes:\n\n```html\n<helix-headless-button commandfor=\"nav\" command=\"--toggle\">Menu</helix-headless-button>\n<helix-headless-drawer id=\"nav\">…</helix-headless-drawer>\n```",
61553
+ "description": "Uses `MixinControllerOpen`, so `open`, `defaultOpen`, and the\n`helix-before-open` and `helix-before-close` events work the same way as on\nan accordion item.\n\nThe drawer is modal by default, which means four things:\n\n- focus is trapped inside: `Tab` from the last control goes to the first,\n and `Shift+Tab` from the first to the last, as in `Dialog`\n- Escape and a press outside close it\n- the page behind is covered\n- the page behind does not scroll\n\nA drawer with `modal=\"false\"` leaves the page usable and still closes on\nEscape, in a browser without `closedby` as well.\n\nSet `persistent` for a drawer that has to be answered: Escape, a press\noutside, and a swipe leave it open, and the close control and `close()`\nstill close it. `closedby=\"none\"` keeps it open on all three as well, and\n`closedby=\"closerequest\"` keeps it open on a press outside.\n\n**A touch or a pen swipes it away**, toward the edge it came from, resolved\nagainst the writing direction. A swipe past halfway, or a quick one, closes\nit through the same request as every other way out. `snap-points` lists the\nfractions of the panel a reader can leave on screen, such as `0.5 1` for a\nsheet that rests half open: it opens at the smallest, and a swipe settles at\nthe nearest. While a swipe moves the panel, the distance is written on it as\n`--drawer-swipe-x` and `--drawer-swipe-y`. `swipe=\"false\"` turns swiping off.\n\n`snap` is the snap point the panel rests at, and `:state(snapped)` is on the\nhost while that is less than the whole panel. `snapTo()` moves it to\nanother. **Focus moving to a control past the\nwindow's edge opens the panel to its largest snap point**, so a reader\nmoving through a half open sheet with `Tab` never lands on a control they\ncannot see.\n\nSet `dismissible` to draw a close control in the corner, labeled by\n`dismiss-label`, as `Dialog` draws one.\n\nThe panel's name is `label` when you set it. Otherwise it is the heading in\nthe `header` slot, or the elements an `aria-labelledby` on the host names,\nand `fallback-label` where nothing names it. A development build warns\nwhen a drawer opens with nothing but `fallback-label` to name it.\n\nThe panel is described by the words at the start of its body, as `Dialog`\nis: the elements in the default slot before the first one that is or holds\na control. An `aria-describedby` on the host replaces it.\n\n**Keyboard.** Opening moves focus to the first control in the panel.\n\n| Key | What it does |\n|---|---|\n| Tab, Shift+Tab | In a modal drawer, moves through the controls in the panel, from the last round to the first and from the first round to the last. In a drawer that is not modal, moves on to the page. |\n| Escape | Asks to close the drawer. A `helix-before-close` listener can refuse, and `persistent` or `closedby=\"none\"` keeps it open. |\n| Enter, Space | On the close control, closes the drawer. |\n\nContent stays mounted when the drawer is closed, so it is in the server\nmarkup and can animate. A closed `<dialog>` is already out of the tab order\nand the accessibility tree, so the drawer does not write `inert`. The `role`,\n`aria-modal`, and the scrim also come from the `<dialog>` element.\n`drawer-root.ssr.spec.ts` checks that the panel has no hand-written `inert`\nor `role`.\n\nOpen the drawer declaratively with the platform's invoker attributes:\n\n```html\n<helix-headless-button commandfor=\"nav\" command=\"--toggle\">Menu</helix-headless-button>\n<helix-headless-drawer id=\"nav\">…</helix-headless-drawer>\n```\n\nFrom its first press, a button that opens the drawer this way says whether\nthe drawer is open: a Helix button through `expanded`, and any other\nelement with `aria-expanded`.",
61248
61554
  "name": "HelixHeadlessDrawer",
61249
61555
  "cssProperties": [
61250
61556
  {
@@ -61316,7 +61622,7 @@
61316
61622
  "name": "commands",
61317
61623
  "privacy": "protected",
61318
61624
  "readonly": true,
61319
- "default": "new CommandController(this, { commands: { '--close': () => this.requestOpen(false), '--show': () => this.requestOpen(true), '--toggle': () => this.toggleOpen(), }, })"
61625
+ "default": "new CommandController(this, { commands: { '--close': () => this.requestOpen(false), '--show': () => this.requestOpen(true), '--toggle': () => this.toggleOpen(), }, expanded: () => this.open, })"
61320
61626
  },
61321
61627
  {
61322
61628
  "kind": "field",
@@ -67414,7 +67720,7 @@
67414
67720
  "declarations": [
67415
67721
  {
67416
67722
  "kind": "class",
67417
- "description": "Shows one filter declared by a `FilterField`: the field, the operator, and\nthe values, with a control to remove the filter.\n\nThe operator is shown only when it is not the field's default:\n\n```\nDisposition refunded, chargeback default operator, hidden\nDisposition is not refunded chosen operator, shown\nCreated after 1 Aug a scalar field has no default\n```\n\nOmit `operator-label` for the field's default operator and pass it for every\nother operator. The code that builds the chip has the field, and the field\ndefines its default.\n\nThe chip shows a filter and does not apply it. Dismissing fires\n`helix-dismiss` as a request, as on `Tag` and `Announcement`. The chip is\nyour element in your list, so you remove it, which keeps your model and the\ndocument in step. Call `preventDefault()` on `helix-dismiss` to cancel the\ndismissal, which is also how you take control of the interaction for one\npress. `requestChange` documents the pattern.\n\n**Keyboard.** The remove button is a tab stop of its own, and Enter or Space\non it asks to remove the filter. In a `FilterInput`, Backspace in the empty\nfield asks to remove the last chip, the arrow keys move between the field\nand the chips, and Delete or Backspace on a chip asks to remove it.\n\nThe remove button is named `dismiss-label` followed by the chip's words,\nsuch as \"Remove filter Status is not open\", so a row of chips says which\nfilter each button removes. Write `{label}` in `dismiss-label` to put the\nwords elsewhere.\n\nTo let the user change the operator, slot a menu trigger into `operator`.\nWith the text fallback, the operator is a label. The chip does not interpret\noperators: their meaning belongs to your domain, as `FilterOperator`\nexplains.",
67723
+ "description": "Shows one filter declared by a `FilterField`: the field, the operator, and\nthe values, with a control to remove the filter.\n\nThe operator is shown only when it is not the field's default:\n\n```\nDisposition refunded, chargeback default operator, hidden\nDisposition is not refunded chosen operator, shown\nCreated after 1 Aug a scalar field has no default\n```\n\nOmit `operator-label` for the field's default operator and pass it for every\nother operator. The code that builds the chip has the field, and the field\ndefines its default.\n\nThe chip shows a filter and does not apply it. Dismissing fires\n`helix-dismiss` as a request, as on `Tag` and `Announcement`. The chip is\nyour element in your list, so you remove it, which keeps your model and the\ndocument in step. Call `preventDefault()` on `helix-dismiss` to cancel the\ndismissal, which is also how you take control of the interaction for one\npress. `requestChange` documents the pattern.\n\n**Keyboard.** The remove button is a tab stop of its own, and Enter or Space\non it asks to remove the filter. In a `FilterInput`, the field's text is the\none tab stop: Backspace in the empty field asks to remove the last chip, the\narrow keys move between the field and the chips, and Delete or Backspace on\na chip asks to remove it.\n\nThe remove button is named `dismiss-label` followed by the chip's words,\nsuch as \"Remove filter Status is not open\", so a row of chips says which\nfilter each button removes. Write `{label}` in `dismiss-label` to put the\nwords elsewhere.\n\nTo let the user change the operator, slot a menu trigger into `operator`.\nWith the text fallback, the operator is a label. The chip does not interpret\noperators: their meaning belongs to your domain, as `FilterOperator`\nexplains.",
67418
67724
  "name": "HelixHeadlessFilterChip",
67419
67725
  "cssParts": [
67420
67726
  {
@@ -67484,6 +67790,15 @@
67484
67790
  "module": "src/mixins/mixin-dismissible/mixin-dismissible.ts"
67485
67791
  }
67486
67792
  },
67793
+ {
67794
+ "kind": "field",
67795
+ "name": "dismissTabStop",
67796
+ "type": {
67797
+ "text": "boolean"
67798
+ },
67799
+ "default": "true",
67800
+ "description": "Whether the remove button is a tab stop of its own.\n\n`FilterInput` writes `false` on the chips in its `tags` slot: the field's\ntext is its one tab stop, and the arrow keys move from the text to the\nchips, as in a `multiple` Combobox. No attribute: it is coordination\nbetween elements, as `Tag.dismissTabStop` is."
67801
+ },
67487
67802
  {
67488
67803
  "kind": "field",
67489
67804
  "name": "label",
@@ -69952,7 +70267,7 @@
69952
70267
  "declarations": [
69953
70268
  {
69954
70269
  "kind": "class",
69955
- "description": "Tokens are your own elements, slotted in. A filter can be a facet with a\nprefix, a saved query, or a range, and these belong to your model, so the\nelement does not render tokens from a list of strings. Removing a token is a\nrequest the element fires, and you apply it. Combobox works the other way:\nits tokens show a selection it owns, drawn from the options it was given.\n\n`value` is the typed text. Every keystroke fires `input` with the text on the\nelement, so a table can narrow as the user types. Backspace in an empty field\nfires `helix-dismiss` on the last token, the same event its remove control\nfires, so one handler covers the pointer and the keyboard.\n\n**What a form receives.** The text under `name`, and then one more entry\nunder `name` for each token, in the order the tokens stand:\n\n| Token | Entry |\n|---|---|\n| Any element with a `value` attribute | The value as written: `saved:mine` |\n| `FilterChip` with `name` and values | `status:open,pending` |\n| The same, with `operator=\"none\"` (what a typed `-status:` makes) | `-status:open,pending` |\n| The same, with any other `operator` except `any` | `created[after]:2024` |\n| A token with neither | Nothing |\n\nThe values are the chip's text outside its `field` and `operator` slots,\nwith the spaces around each comma removed. A server splits an entry at its\nfirst colon, reads a leading `-` as the negation, and reads a word in\nbrackets before the colon as the comparison.\n\n**Keyboard.** Focus stays in the text. The text names the highlighted\nsuggestion as its `aria-activedescendant`.\n\n| Key | What it does |\n|---|---|\n| Enter | Presses the highlighted suggestion. With none highlighted, reads the typed filters into chips, or submits the form when there are none. |\n| Down, Up | Moves the highlight through the suggestions, wrapping, and opens the panel. |\n| Home, End | Highlights the first or the last suggestion while the panel is open. |\n| Escape | Closes the panel and stops there, so a dialog around the field stays open. |\n| Backspace | In an empty field, asks the last token to go. |\n| Left | With the caret at the start of the text, moves to the last token's remove button. Right on a right-to-left page. |\n| Left, Right on a token | Move between the tokens, and past the last back to the text. |\n| Delete, Backspace on a token | Asks the token to go. |\n\n**Typed filters.** The field parses only the fields you declare. Write\n`FilterField` elements, usually inside the `FilterMenu` slotted into `start`,\nso the picker and the text share one set. On Enter, the field reads the text\nagainst them. `disposition:refunded,settled` and `-disposition:refunded` fire\n`helix-facet` exactly as ticking those values in the picker would, and the\nparsed text is removed. Everything else stays, so `refund tuesday\ndisposition:settled` is a filter and a search at once. Helix defines the\npunctuation and you define every word. `grammar.ts` has the full rule, so\nread it before you rely on it. A field with no `FilterField` in it parses\nnothing.\n\nA field whose values cannot be listed, such as `assignee:ada`, goes directly\ninside this element, outside the menu. It is not assigned to a slot, so\nnothing draws it and the menu has no page for it, and the grammar still reads\nit.\n\n**Suggestions.** Slot options into `suggestions` and the field shows them,\nmoves through them with the arrow keys, and presses one on Enter. The option\nfires its own `helix-select`, and you decide what choosing it means. Keys\nbefore a colon and values after it use the same panel with different\ncontents. `filter-suggestions` narrows the panel to the options whose words\nhold the term at the caret, after its colon, ignoring case and accents, and\nhides the rest. `readFilterText` is exported so you can narrow it yourself\nagainst partly typed text. While the panel is open, a reader hears how many\nsuggestions it offers. The panel opens when it has options and closes when it has\nnone, and a choice does not close it, so after picking a key you see its\nvalues next. Use `selectionStart` and `setSelectionRange` to write a\ncompletion back and put the caret after it, or the next parse reads the wrong\nterm.\n\n**State.** Three things have three different owners:\n\n- `value` is the text. The user types, the element applies it, and `input`\n reports it. Setting `value` seeds or corrects the text, and the next\n keystroke overwrites it, even in React. To control the value, cancel\n `helix-before-change` and write the value you want, as with every field in\n this library. `default-value` seeds the field.\n- `open` is the suggestions panel. Cancel `helix-before-open` or\n `helix-before-close` to control it, and use `default-open` to seed it.\n- Tokens are your elements in a slot and hold no state. A token goes when you\n remove it from your markup after `helix-dismiss`. A `FilterChip` naming a\n declared field works differently, because the picker holds what is ticked\n and removing the chip alone would leave its value ticked in the hidden\n menu. Its dismissal is treated as the field being filtered by nothing, so\n it fires `helix-facet` like every other change, and the chip goes when the\n filter goes.",
70270
+ "description": "Tokens are your own elements, slotted in. A filter can be a facet with a\nprefix, a saved query, or a range, and these belong to your model, so the\nelement does not render tokens from a list of strings. Removing a token is a\nrequest the element fires, and you apply it. Combobox works the other way:\nits tokens show a selection it owns, drawn from the options it was given.\n\n`value` is the typed text. Every keystroke fires `input` with the text on the\nelement, so a table can narrow as the user types. Backspace in an empty field\nfires `helix-dismiss` on the last token, the same event its remove control\nfires, so one handler covers the pointer and the keyboard.\n\n**What a form receives.** The text under `name`, and then one more entry\nunder `name` for each token, in the order the tokens stand:\n\n| Token | Entry |\n|---|---|\n| Any element with a `value` attribute | The value as written: `saved:mine` |\n| `FilterChip` with `name` and values | `status:open,pending` |\n| The same, with `operator=\"none\"` (what a typed `-status:` makes) | `-status:open,pending` |\n| The same, with any other `operator` except `any` | `created[after]:2024` |\n| A token with neither | Nothing |\n\nThe values are the chip's text outside its `field` and `operator` slots,\nwith the spaces around each comma removed. A server splits an entry at its\nfirst colon, reads a leading `-` as the negation, and reads a word in\nbrackets before the colon as the comparison.\n\n**Keyboard.** Focus stays in the text, which is the field's one tab stop. The\ntext names the highlighted suggestion as its `aria-activedescendant`, and the\narrow keys reach the tokens.\n\n| Key | What it does |\n|---|---|\n| Enter | Presses the highlighted suggestion. With none highlighted, reads the typed filters into chips, or submits the form when there are none. |\n| Down, Up | Moves the highlight through the suggestions, wrapping, and opens the panel. |\n| Home, End | Highlights the first or the last suggestion while the panel is open. |\n| Escape | Closes the panel and stops there, so a dialog around the field stays open. |\n| Backspace | In an empty field, asks the last token to go. |\n| Left | With the caret at the start of the text, moves to the last token's remove button. Right on a right-to-left page. |\n| Left, Right on a token | Move between the tokens, and past the last back to the text. |\n| Delete, Backspace on a token | Asks the token to go. |\n\n**Typed filters.** The field parses only the fields you declare. Write\n`FilterField` elements, usually inside the `FilterMenu` slotted into `start`,\nso the picker and the text share one set. On Enter, the field reads the text\nagainst them. `disposition:refunded,settled` and `-disposition:refunded` fire\n`helix-facet` exactly as ticking those values in the picker would, and the\nparsed text is removed. Everything else stays, so `refund tuesday\ndisposition:settled` is a filter and a search at once. Helix defines the\npunctuation and you define every word. `grammar.ts` has the full rule, so\nread it before you rely on it. A field with no `FilterField` in it parses\nnothing.\n\nA field whose values cannot be listed, such as `assignee:ada`, goes directly\ninside this element, outside the menu. It is not assigned to a slot, so\nnothing draws it and the menu has no page for it, and the grammar still reads\nit.\n\n**Suggestions.** Slot options into `suggestions` and the field shows them,\nmoves through them with the arrow keys, and presses one on Enter. The option\nfires its own `helix-select`, and you decide what choosing it means. Keys\nbefore a colon and values after it use the same panel with different\ncontents. `filter-suggestions` narrows the panel to the options whose words\nhold the term at the caret, after its colon, ignoring case and accents, and\nhides the rest. `readFilterText` is exported so you can narrow it yourself\nagainst partly typed text. While the panel is open, a reader hears how many\nsuggestions it offers. The panel opens when it has options and closes when it has\nnone, and a choice does not close it, so after picking a key you see its\nvalues next. Use `selectionStart` and `setSelectionRange` to write a\ncompletion back and put the caret after it, or the next parse reads the wrong\nterm.\n\n**State.** Three things have three different owners:\n\n- `value` is the text. The user types, the element applies it, and `input`\n reports it. Setting `value` seeds or corrects the text, and the next\n keystroke overwrites it, even in React. To control the value, cancel\n `helix-before-change` and write the value you want, as with every field in\n this library. `default-value` seeds the field.\n- `open` is the suggestions panel. Cancel `helix-before-open` or\n `helix-before-close` to control it, and use `default-open` to seed it.\n- Tokens are your elements in a slot and hold no state. A token goes when you\n remove it from your markup after `helix-dismiss`. A `FilterChip` naming a\n declared field works differently, because the picker holds what is ticked\n and removing the chip alone would leave its value ticked in the hidden\n menu. Its dismissal is treated as the field being filtered by nothing, so\n it fires `helix-facet` like every other change, and the chip goes when the\n filter goes. Set `chips` and the field adds, updates, and removes those\n chips itself, from every `helix-facet` you do not cancel.",
69956
70271
  "name": "HelixHeadlessFilterInput",
69957
70272
  "cssParts": [
69958
70273
  {
@@ -69963,6 +70278,10 @@
69963
70278
  "description": "The input itself.",
69964
70279
  "name": "control"
69965
70280
  },
70281
+ {
70282
+ "description": "Shown in the panel when `filter-suggestions` hides every suggestion.",
70283
+ "name": "empty"
70284
+ },
69966
70285
  {
69967
70286
  "description": "The error message under the field.",
69968
70287
  "name": "feedback"
@@ -69987,6 +70306,10 @@
69987
70306
  "description": "The live region that says how many suggestions the open panel offers. It is rendered from the first time the panel opens with suggestions.",
69988
70307
  "name": "live"
69989
70308
  },
70309
+ {
70310
+ "description": "Shown in the panel while the suggestions load.",
70311
+ "name": "loading"
70312
+ },
69990
70313
  {
69991
70314
  "description": "The count of tokens that did not fit. It is a `<button>` with `expandable` and a `<span>` without it.",
69992
70315
  "name": "rest"
@@ -70021,6 +70344,14 @@
70021
70344
  "description": "The label, for content richer than the `label` text.",
70022
70345
  "name": "label"
70023
70346
  },
70347
+ {
70348
+ "description": "What the panel shows while `loading` is set, for content richer than `loading-text`.",
70349
+ "name": "loading"
70350
+ },
70351
+ {
70352
+ "description": "What the panel shows when `filter-suggestions` hides every suggestion, for content richer than `no-results-text`.",
70353
+ "name": "no-results"
70354
+ },
70024
70355
  {
70025
70356
  "description": "Content before the text, such as a search icon.",
70026
70357
  "name": "start"
@@ -70071,6 +70402,16 @@
70071
70402
  "module": "src/mixins/mixin-controller-value/mixin-controller-value.ts"
70072
70403
  }
70073
70404
  },
70405
+ {
70406
+ "kind": "field",
70407
+ "name": "chips",
70408
+ "type": {
70409
+ "text": "boolean"
70410
+ },
70411
+ "default": "false",
70412
+ "description": "Whether the field keeps a `FilterChip` for each filter in its `tags` slot,\nfrom the `helix-facet` events it hears.\n\nOff, you build the chips, which is what you do when your own model holds\nthe filters. On, a filter typed, picked, or changed adds or updates the\nchip for its field, one with no values takes it away (unless its\ncomparison takes none, such as \"is empty\"), and removing the chip clears\nthe filter. A `helix-facet` you cancel changes no chip.\n\nThe chips are elements the field adds to its own children, so leave this\noff where a framework renders those children and would remove them.",
70413
+ "attribute": "chips"
70414
+ },
70074
70415
  {
70075
70416
  "kind": "field",
70076
70417
  "name": "clearable",
@@ -70104,6 +70445,40 @@
70104
70445
  "description": "Whether the panel narrows itself to the suggestions that hold the term\nbeing typed, as React Aria's Autocomplete filters. Off, the panel shows\nwhat you slot and you narrow it.",
70105
70446
  "attribute": "filter-suggestions"
70106
70447
  },
70448
+ {
70449
+ "kind": "field",
70450
+ "name": "loading",
70451
+ "type": {
70452
+ "text": "boolean"
70453
+ },
70454
+ "default": "false",
70455
+ "description": "Whether the suggestions are still loading, such as while a request for the\nvalues of a typed key is out.\n\nThe panel opens on `loading-text`, or the `loading` slot, while the field\nhas focus, and a screen reader hears it. Slot the options and clear this\nwhen they arrive.",
70456
+ "attribute": "loading",
70457
+ "reflects": true
70458
+ },
70459
+ {
70460
+ "kind": "field",
70461
+ "name": "loadingText",
70462
+ "type": {
70463
+ "text": "string"
70464
+ },
70465
+ "default": "'Loading'",
70466
+ "description": "Text the panel shows while the suggestions load.",
70467
+ "attribute": "loading-text",
70468
+ "helix": {
70469
+ "words": true
70470
+ }
70471
+ },
70472
+ {
70473
+ "kind": "field",
70474
+ "name": "noResultsText",
70475
+ "type": {
70476
+ "text": "string"
70477
+ },
70478
+ "default": "''",
70479
+ "description": "Text the panel shows when `filter-suggestions` hides every suggestion, such\nas \"No matching filters\".\n\nEmpty, or no `no-results` slot, and the panel closes instead, as it does\nwhen it has nothing to offer.",
70480
+ "attribute": "no-results-text"
70481
+ },
70107
70482
  {
70108
70483
  "kind": "field",
70109
70484
  "name": "placeholder",
@@ -70451,6 +70826,32 @@
70451
70826
  ],
70452
70827
  "description": "Says a field is now filtered by these values, through the picker where\nthere is one, and reports whether it stood.\n\nAnnounced by the menu rather than relayed by this element, so the ticks and\nthe event never come from two places. A consumer who wrote `FilterField`s\nwithout a picker gets it from here, which is the only case where nothing\nelse could have said it."
70453
70828
  },
70829
+ {
70830
+ "kind": "field",
70831
+ "name": "handleFacet",
70832
+ "privacy": "private",
70833
+ "readonly": true,
70834
+ "description": "Keeps the chip for a filter in step with it, under `chips`.\n\nAfter every listener has had its turn, so a consumer's `preventDefault()`\non the same event, on this element or above it, is read: `dispatchEvent`\nruns them all before the microtask does."
70835
+ },
70836
+ {
70837
+ "kind": "method",
70838
+ "name": "drawChip",
70839
+ "privacy": "private",
70840
+ "return": {
70841
+ "type": {
70842
+ "text": "void"
70843
+ }
70844
+ },
70845
+ "parameters": [
70846
+ {
70847
+ "name": "facet",
70848
+ "type": {
70849
+ "text": "FacetDetail"
70850
+ }
70851
+ }
70852
+ ],
70853
+ "description": "Adds, updates or removes the chip for one filter. See `chips`."
70854
+ },
70454
70855
  {
70455
70856
  "kind": "field",
70456
70857
  "name": "handleDismiss",
@@ -70523,6 +70924,16 @@
70523
70924
  },
70524
70925
  "description": "What is on offer, settled before the render that draws it.\n\n**Not left to `slotchange`, for the reason the token row is not.** That\nevent reports the assignment moving and nothing else, and it does not fire\nat all under the harness these are tested in, so a panel that learned only\nfrom it would be a panel that never opened. Reading the slot every update is\nwhat makes the answer true whenever anyone asks.\n\nState only. Opening is an announcement and belongs after the render, which\nis why `settleOpen` is not called from here."
70525
70926
  },
70927
+ {
70928
+ "kind": "field",
70929
+ "name": "unmatched",
70930
+ "type": {
70931
+ "text": "boolean"
70932
+ },
70933
+ "privacy": "private",
70934
+ "description": "Whether `filter-suggestions` has hidden every suggestion there is, where a\n`no-results-text` or a `no-results` slot says so. The panel stays open on\nit, and `:state(no-results)` is on the host.",
70935
+ "readonly": true
70936
+ },
70526
70937
  {
70527
70938
  "kind": "field",
70528
70939
  "name": "handleSuggestions",
@@ -70556,7 +70967,7 @@
70556
70967
  "name": "handleTokens",
70557
70968
  "privacy": "private",
70558
70969
  "readonly": true,
70559
- "description": "The row again, whenever what is in it changes."
70970
+ "description": "The row again, whenever what is in it changes.\n\n**And the text is the field's one tab stop.** Each token's remove button\nwas a stop of its own, so Tab through a field with five filters took six\npresses. A `Tag` or `FilterChip` takes `dismissTabStop` for this, as the\ntags of a `multiple` Combobox do, and the arrow keys still reach every one.\nA token not yet defined is told once it is."
70560
70971
  },
70561
70972
  {
70562
70973
  "kind": "method",
@@ -72396,6 +72807,15 @@
72396
72807
  }
72397
72808
  ],
72398
72809
  "attributes": [
72810
+ {
72811
+ "name": "chips",
72812
+ "type": {
72813
+ "text": "boolean"
72814
+ },
72815
+ "default": "false",
72816
+ "description": "Whether the field keeps a `FilterChip` for each filter in its `tags` slot,\nfrom the `helix-facet` events it hears.\n\nOff, you build the chips, which is what you do when your own model holds\nthe filters. On, a filter typed, picked, or changed adds or updates the\nchip for its field, one with no values takes it away (unless its\ncomparison takes none, such as \"is empty\"), and removing the chip clears\nthe filter. A `helix-facet` you cancel changes no chip.\n\nThe chips are elements the field adds to its own children, so leave this\noff where a framework renders those children and would remove them.",
72817
+ "fieldName": "chips"
72818
+ },
72399
72819
  {
72400
72820
  "name": "clearable",
72401
72821
  "type": {
@@ -72423,6 +72843,33 @@
72423
72843
  "description": "Whether the panel narrows itself to the suggestions that hold the term\nbeing typed, as React Aria's Autocomplete filters. Off, the panel shows\nwhat you slot and you narrow it.",
72424
72844
  "fieldName": "filterSuggestions"
72425
72845
  },
72846
+ {
72847
+ "name": "loading",
72848
+ "type": {
72849
+ "text": "boolean"
72850
+ },
72851
+ "default": "false",
72852
+ "description": "Whether the suggestions are still loading, such as while a request for the\nvalues of a typed key is out.\n\nThe panel opens on `loading-text`, or the `loading` slot, while the field\nhas focus, and a screen reader hears it. Slot the options and clear this\nwhen they arrive.",
72853
+ "fieldName": "loading"
72854
+ },
72855
+ {
72856
+ "name": "loading-text",
72857
+ "type": {
72858
+ "text": "string"
72859
+ },
72860
+ "default": "'Loading'",
72861
+ "description": "Text the panel shows while the suggestions load.",
72862
+ "fieldName": "loadingText"
72863
+ },
72864
+ {
72865
+ "name": "no-results-text",
72866
+ "type": {
72867
+ "text": "string"
72868
+ },
72869
+ "default": "''",
72870
+ "description": "Text the panel shows when `filter-suggestions` hides every suggestion, such\nas \"No matching filters\".\n\nEmpty, or no `no-results` slot, and the panel closes instead, as it does\nwhen it has nothing to offer.",
72871
+ "fieldName": "noResultsText"
72872
+ },
72426
72873
  {
72427
72874
  "name": "placeholder",
72428
72875
  "type": {
@@ -72673,6 +73120,14 @@
72673
73120
  "description": "The field has an error to show, from the browser's validation or from your `feedback`.",
72674
73121
  "name": "invalid"
72675
73122
  },
73123
+ {
73124
+ "description": "While `loading` is set.",
73125
+ "name": "loading"
73126
+ },
73127
+ {
73128
+ "description": "`filter-suggestions` hid every suggestion, and `no-results-text` or the `no-results` slot says so.",
73129
+ "name": "no-results"
73130
+ },
72676
73131
  {
72677
73132
  "description": "The suggestions are open.",
72678
73133
  "name": "open"
@@ -72990,7 +73445,7 @@
72990
73445
  "declarations": [
72991
73446
  {
72992
73447
  "kind": "class",
72993
- "description": "Formats a date with `Intl` where it is shown, so you do not write the same\n`Intl` call and options in your own code.\n\n**`elapsed` shows compact relative time for a feed**, such as \"42m\", \"3h\", or\n\"1d\". After six days it shows a date. In en-US, `Intl.NumberFormat` narrow\nrenders both `minute` and `month` as `m`, so a compact \"3m\" could mean three\nminutes or three months. `elapsedTime` in `helix/core` does the measurement.\n\n**`relative` shows the same reading in words**, such as \"3 hours ago\" at\n`long`, \"3 hr. ago\" at `short` and \"3h ago\" at `narrow`, the styles\n`sl-relative-time` offers. `numeric=\"always\"` says \"1 day ago\" where the\ndefault says \"yesterday\".\n\n**`date-style` and `time-style` take each locale's own pattern**, such as\n\"Tuesday, August 4, 2026\" at `full`, and stand in for the separate fields,\nwhich `Intl` refuses beside them. `hour-cycle` sets a twelve or twenty-four\nhour clock either way.\n\n**The text refreshes on one timer shared by the page.** Each element\nschedules an update for when its current reading expires, and an element\nshowing a date schedules none, so a long feed left open costs almost nothing.\n`frozen` draws the reading once and leaves it.\n\n**`fields` draws each field of a date as a part of its own**, such as\n`day`, `month` and `year`, so a stylesheet can set the year in a lighter ink\nor the weekday in capitals with `::part()`. The text between the fields is\nthe locale's own and has no part. An `elapsed` or `relative` reading is one\nphrase, with no parts inside it.\n\n**The output is always `<time datetime>`**, so copy and paste and scripts\nparsing the page get the exact instant, whatever the visible text says. A\nscreen reader reads the text, so an `elapsed` reading is named in full, such\nas \"3 hours ago\", and carries the whole date as its title.\n\n**A date with no time, such as `2026-08-04`, is that day everywhere.** It is\nformatted in UTC unless `time-zone` says otherwise, as Date Input and Date\nPicker format their dates, so a reader west of UTC sees August 4 and not\nAugust 3.\n\n**A date is written in a calendar Date Picker draws**: the locale's own\nwhere its months are the Gregorian months, as `th-TH` writes 2569 for 2026,\nand `gregory` where they are not, as for `fa-IR`. `calendar` picks any of\nthem, `persian` and `hebrew` included.",
73448
+ "description": "Formats a date with `Intl` where it is shown, so you do not write the same\n`Intl` call and options in your own code.\n\n**`elapsed` shows compact relative time for a feed**, such as \"42m\", \"3h\", or\n\"1d\". After six days it shows a date. In en-US, `Intl.NumberFormat` narrow\nrenders both `minute` and `month` as `m`, so a compact \"3m\" could mean three\nminutes or three months. `elapsedTime` in `helix/core` does the measurement.\n\n**`relative` shows the same reading in words**, such as \"3 hours ago\" at\n`long`, \"3 hr. ago\" at `short` and \"3h ago\" at `narrow`, the styles\n`sl-relative-time` offers. `numeric=\"always\"` says \"1 day ago\" where the\ndefault says \"yesterday\". Both readings round the count, as\n`sl-relative-time` does: 100 seconds ago is \"2 minutes ago\", and 59.6\nminutes is \"1 hour\".\n\n**`date-style` and `time-style` take each locale's own pattern**, such as\n\"Tuesday, August 4, 2026\" at `full`, and stand in for the separate fields,\nwhich `Intl` refuses beside them. `hour-cycle` sets a twelve or twenty-four\nhour clock either way.\n\n**The text refreshes on one timer shared by the page.** Each element\nschedules an update for when its current reading expires, and an element\nshowing a date schedules none, so a long feed left open costs almost nothing.\n`frozen` draws the reading once and leaves it.\n\n**`fields` draws each field of a date as a part of its own**, such as\n`day`, `month` and `year`, so a stylesheet can set the year in a lighter ink\nor the weekday in capitals with `::part()`. The text between the fields is\nthe locale's own and has no part. An `elapsed` or `relative` reading is one\nphrase, with no parts inside it.\n\n**The output is always `<time datetime>`**, so copy and paste and scripts\nparsing the page get the exact instant, whatever the visible text says. A\nscreen reader reads the text, so an `elapsed` reading is named in full, such\nas \"3 hours ago\", and carries the whole date as its title.\n\n**A date with no time, such as `2026-08-04`, is that day everywhere.** It is\nformatted in UTC unless `time-zone` says otherwise, as Date Input and Date\nPicker format their dates, so a reader west of UTC sees August 4 and not\nAugust 3.\n\n**A date is written in a calendar Date Picker draws**: the locale's own\nwhere its months are the Gregorian months, as `th-TH` writes 2569 for 2026,\nand `gregory` where they are not, as for `fa-IR`. `calendar` picks any of\nthem, `persian` and `hebrew` included.",
72994
73449
  "name": "HelixHeadlessFormatDate",
72995
73450
  "cssParts": [
72996
73451
  {
@@ -83862,6 +84317,48 @@
83862
84317
  },
83863
84318
  "description": "Where the selection starts, as an offset into the value."
83864
84319
  },
84320
+ {
84321
+ "kind": "method",
84322
+ "name": "setRangeText",
84323
+ "return": {
84324
+ "type": {
84325
+ "text": "void"
84326
+ }
84327
+ },
84328
+ "parameters": [
84329
+ {
84330
+ "name": "replacement",
84331
+ "type": {
84332
+ "text": "string"
84333
+ }
84334
+ },
84335
+ {
84336
+ "name": "start",
84337
+ "optional": true,
84338
+ "type": {
84339
+ "text": "number"
84340
+ }
84341
+ },
84342
+ {
84343
+ "name": "end",
84344
+ "optional": true,
84345
+ "type": {
84346
+ "text": "number"
84347
+ }
84348
+ },
84349
+ {
84350
+ "name": "selectMode",
84351
+ "optional": true,
84352
+ "type": {
84353
+ "text": "SelectionMode"
84354
+ }
84355
+ }
84356
+ ],
84357
+ "description": "Replaces a range of the text, as the native method does: the selection\nwhere `start` and `end` are left out, and `selectMode` says what is\nselected after. It sends no event, as the native method sends none.",
84358
+ "type": {
84359
+ "text": "setRangeText(replacement: string, start?: number, end?: number, selectMode?: SelectionMode) => void"
84360
+ }
84361
+ },
83865
84362
  {
83866
84363
  "kind": "method",
83867
84364
  "name": "setSelectionRange",
@@ -88456,7 +88953,7 @@
88456
88953
  "name": "commands",
88457
88954
  "privacy": "protected",
88458
88955
  "readonly": true,
88459
- "default": "new CommandController(this, { commands: { '--close': () => this.close(), '--fullscreen': () => this.toggleFullscreen(), '--next': () => this.next(), '--previous': () => this.previous(), '--show': () => this.show(), '--toggle': () => this.toggleOpen(), }, })"
88956
+ "default": "new CommandController(this, { commands: { '--close': () => this.close(), '--fullscreen': () => this.toggleFullscreen(), '--next': () => this.next(), '--previous': () => this.previous(), '--show': () => this.show(), '--toggle': () => this.toggleOpen(), }, expanded: () => this.open, })"
88460
88957
  },
88461
88958
  {
88462
88959
  "kind": "field",
@@ -95004,7 +95501,7 @@
95004
95501
  }
95005
95502
  }
95006
95503
  ],
95007
- "description": "Says how many options a filter left, in the live region.\n\n**A reader typing into a filter cannot see the list narrow**, and React\nAria and Web Awesome both say the count as it changes. Only \"No matches\"\nwas said here, so a query that left forty options and one that left one\nsounded the same. Said when the filter changes and the count with it, so\na keystroke that leaves the same options says nothing new. An empty filter\nsays nothing, being the list as it was given; no matches is `empty-text`,\nwhich the region says in place of this."
95504
+ "description": "Says how many options a filter left, in the live region.\n\n**A reader typing into a filter cannot see the list narrow**, and React\nAria and Web Awesome both say the count as it changes. Only \"No matches\"\nwas said here, so a query that left forty options and one that left one\nsounded the same. Said when the filter changes and the count with it, so\na keystroke that leaves the same options says nothing new. An empty filter\nsays nothing, being the list as it was given; no matches is `empty-text`,\nwhich the region says in place of this. A load that ends says the count\ntoo, whatever the filter."
95008
95505
  },
95009
95506
  {
95010
95507
  "kind": "method",
@@ -109545,6 +110042,14 @@
109545
110042
  "description": "The row is checked.",
109546
110043
  "name": "checked"
109547
110044
  },
110045
+ {
110046
+ "description": "The row is disabled.",
110047
+ "name": "disabled"
110048
+ },
110049
+ {
110050
+ "description": "Under `submenus=\"flyout\"`, the row's submenu is open beside it.",
110051
+ "name": "expanded"
110052
+ },
109548
110053
  {
109549
110054
  "description": "In a menu with `filterable`, the arrow keys or the pointer reached the row while focus stays in the filter field.",
109550
110055
  "name": "highlighted"
@@ -109617,7 +110122,7 @@
109617
110122
  "declarations": [
109618
110123
  {
109619
110124
  "kind": "class",
109620
- "description": "The trigger is slotted and stays your own element, as with the tooltip. The\nmenu renders its surface in the top layer with the popover API, so an\nancestor with `overflow: hidden` does not clip it and it needs no `z-index`.\n\nSlot a `helix-button` or `helix-icon-button` as the trigger, and the menu\ntells it that it opens a menu and whether the menu is open, inside its own\nshadow root. On a plain element, such as a `<button>`, the menu writes\n`aria-haspopup=\"menu\"` and `aria-expanded`, from a frame after its own first\nrender, so a server's markup is never changed while a framework hydrates it.\nIt never writes `aria-expanded` where you wrote it yourself.\n\nSubmenus drill down by default. A submenu replaces the panel's content, and a\nback row returns to the previous page. One panel stays inside a narrow\nwindow, and works on touch screens, which have no hover.\n\n**`submenus=\"flyout\"` opens each submenu in a panel of its own beside its\nrow**, as React Aria's `SubmenuTrigger` and Radix's `Sub` do. Resting the\npointer on the row opens it, and a pointer crossing toward the open panel\nover the rows between keeps it open, as Radix's grace area does. Right Arrow\nopens a row's submenu on its first row, and Left Arrow or Escape closes it\nand returns focus to the row, the two arrows swapping on a right-to-left\npage. The row says `aria-expanded`, and the panel is a `menu` named by the\nrow.\n\nThe root holds which page is open, because only one page shows at a time. It\nsets this on items as a property, never as an attribute. An attribute written\nonto your element after it upgrades causes a hydration mismatch.\n\nGroup rows under a heading with `Menu.Section`, which is a `group` named by\nits heading. Between runs of rows, a `<helix-divider>` is a separator a\nscreen reader announces.\n\nKeys: Enter, Space or Down Arrow on the trigger opens the menu on its first\nrow, and Up Arrow on its last. In the menu, Up and Down Arrow move between\nrows, Home and End go to the first and the last, a typed letter moves to\nthe next row whose name starts with it, Enter and Space choose (Space on a\nchoice row changes it and leaves the menu open), Right Arrow\nopens a row's submenu and Left Arrow goes back, the two swapping on a\nright-to-left page. Left Arrow on the top page does nothing. Escape closes\nthe menu and returns focus to the trigger. Tab closes it and moves on.\n\nWhile it is open, the surface carries the trigger's width as\n`--floating-anchor-inline-size` and the room left below it as\n`--floating-available-block-size`, as Radix's dropdown menu publishes its\ntrigger's width, for a menu as wide as its trigger:\n`helix-menu::part(surface) { min-inline-size: var(--floating-anchor-inline-size) }`.\n\nWith `context`, the trigger is an area and the menu is its context menu: a\nright-click, a long press, Shift+F10 or the menu key over the area opens it\nat the pointer, or at the area's corner from the keyboard, as Radix's context\nmenu does. A press on the area does not open it, and closing it returns focus\nto where it was. A touch or a pen held still on the area opens it too, on\nevery browser, since iOS sends no `contextmenu` for one.\n\n**A trigger with its own action.** With `open-on=\"long-press\"`, a press on\nthe trigger is the trigger's own, and a press held for half a second opens\nthe menu, as React Aria's `trigger=\"longPress\"` does. Alt+Down or Alt+Up\nopens it from the keyboard, and the trigger is described by\n`long-press-label`, which says so.\n\n**A filter field.** With `filterable`, the menu opens with focus in a field\nabove its rows, and typing narrows the rows of the page the keys move in,\nas React Aria's `Autocomplete` around a `Menu` does. A row is kept where its\nwords hold the filter, in any case, or where `filterMatch` says so. Each\npage starts unfiltered, and the filter is cleared as the menu closes. Where\ntyping keeps no row, the menu shows `no-results-text`. Focus stays in the\nfield: Up and Down Arrow move the highlight through the rows that are left,\nEnter chooses the highlighted row, Right Arrow opens its submenu, Left Arrow\non a submenu goes back, and Escape clears the filter and then closes the\nmenu. Home, End and the other keys move the caret and type. The field names\nthe highlighted row with `aria-activedescendant`, and the rows are a `menu`\nafter the field. Cancel `helix-filter` to keep every row\nshowing and narrow the rows yourself, such as by replacing them with rows\nfrom a server.",
110125
+ "description": "The trigger is slotted and stays your own element, as with the tooltip. The\nmenu renders its surface in the top layer with the popover API, so an\nancestor with `overflow: hidden` does not clip it and it needs no `z-index`.\n\nSlot a `helix-button` or `helix-icon-button` as the trigger, and the menu\ntells it that it opens a menu and whether the menu is open, inside its own\nshadow root. On a plain element, such as a `<button>`, the menu writes\n`aria-haspopup=\"menu\"` and `aria-expanded`, from a frame after its own first\nrender, so a server's markup is never changed while a framework hydrates it.\nIt never writes `aria-expanded` where you wrote it yourself.\n\nSubmenus drill down by default. A submenu replaces the panel's content, and a\nback row returns to the previous page. One panel stays inside a narrow\nwindow, and works on touch screens, which have no hover. Focus follows a\nmouse resting on a row in either mode, and a drilled page is a `menu` named\nby the row that opened it.\n\n**`submenus=\"flyout\"` opens each submenu in a panel of its own beside its\nrow**, as React Aria's `SubmenuTrigger` and Radix's `Sub` do. Resting the\npointer on the row opens it, and a pointer crossing toward the open panel\nover the rows between keeps it open, as Radix's grace area does. Right Arrow\nopens a row's submenu on its first row, and Left Arrow or Escape closes it\nand returns focus to the row, the two arrows swapping on a right-to-left\npage. The row says `aria-expanded`, and the panel is a `menu` named by the\nrow.\n\nThe root holds which page is open, because only one page shows at a time. It\nsets this on items as a property, never as an attribute. An attribute written\nonto your element after it upgrades causes a hydration mismatch.\n\nGroup rows under a heading with `Menu.Section`, which is a `group` named by\nits heading. Between runs of rows, a `<helix-divider>` is a separator a\nscreen reader announces.\n\nKeys: Enter, Space or Down Arrow on the trigger opens the menu on its first\nrow, and Up Arrow on its last. In the menu, Up and Down Arrow move between\nrows, Home and End go to the first and the last, a typed letter moves to\nthe next row whose name starts with it, Enter and Space choose (Space on a\nchoice row changes it and leaves the menu open), Right Arrow\nopens a row's submenu and Left Arrow goes back, the two swapping on a\nright-to-left page. Left Arrow on the top page does nothing. Escape closes\nthe menu and returns focus to the trigger. Tab closes it and moves on.\n\nWhile it is open, the surface carries the trigger's width as\n`--floating-anchor-inline-size` and the room left below it as\n`--floating-available-block-size`, as Radix's dropdown menu publishes its\ntrigger's width, for a menu as wide as its trigger:\n`helix-menu::part(surface) { min-inline-size: var(--floating-anchor-inline-size) }`.\n\n**The choices in one place.** `checkedValues` holds the values of the rows\nthat are checked, anywhere in the menu, and setting it checks exactly\nthose rows, as React Aria's `selectedKeys` does. `change` is sent on the\nmenu when a reader's choice changes it.\n\nWith `context`, the trigger is an area and the menu is its context menu: a\nright-click, a long press, Shift+F10 or the menu key over the area opens it\nat the pointer, or at the area's corner from the keyboard, as Radix's context\nmenu does. A press on the area does not open it, and closing it returns focus\nto where it was. A touch or a pen held still on the area opens it too, on\nevery browser, since iOS sends no `contextmenu` for one.\n\n**A trigger with its own action.** With `open-on=\"long-press\"`, a press on\nthe trigger is the trigger's own, and a press held for half a second opens\nthe menu, as React Aria's `trigger=\"longPress\"` does. Alt+Down or Alt+Up\nopens it from the keyboard, and the trigger is described by\n`long-press-label`, which says so.\n\n**A filter field.** With `filterable`, the menu opens with focus in a field\nabove its rows, and typing narrows the rows of the page the keys move in,\nas React Aria's `Autocomplete` around a `Menu` does. A row is kept where its\nwords hold the filter, in any case, or where `filterMatch` says so. Each\npage starts unfiltered, and the filter is cleared as the menu closes. Where\ntyping keeps no row, the menu shows `no-results-text`. Focus stays in the\nfield: Up and Down Arrow move the highlight through the rows that are left,\nEnter chooses the highlighted row, Right Arrow opens its submenu, Left Arrow\non a submenu goes back, and Escape clears the filter and then closes the\nmenu. A screen reader hears how many rows the filter keeps\n(`results-label`). Home, End and the other keys move the caret and type. The field names\nthe highlighted row with `aria-activedescendant`, and the rows are a `menu`\nafter the field. Cancel `helix-filter` to keep every row\nshowing and narrow the rows yourself, such as by replacing them with rows\nfrom a server.",
109621
110126
  "name": "HelixHeadlessMenu",
109622
110127
  "cssProperties": [
109623
110128
  {
@@ -109672,6 +110177,14 @@
109672
110177
  "description": "The items.",
109673
110178
  "name": ""
109674
110179
  },
110180
+ {
110181
+ "description": "What the menu shows when it has no items, for content richer than `empty-text`.",
110182
+ "name": "empty"
110183
+ },
110184
+ {
110185
+ "description": "What the menu shows while its items load, for content richer than `loading-text`.",
110186
+ "name": "loading"
110187
+ },
109675
110188
  {
109676
110189
  "description": "What the menu shows when the filter keeps no rows, for content richer than `no-results-text`.",
109677
110190
  "name": "no-results"
@@ -109696,6 +110209,20 @@
109696
110209
  "module": "src/mixins/mixin-collection/mixin-collection.ts"
109697
110210
  }
109698
110211
  },
110212
+ {
110213
+ "kind": "field",
110214
+ "name": "statusSlots",
110215
+ "type": {
110216
+ "text": "boolean"
110217
+ },
110218
+ "privacy": "protected",
110219
+ "default": "true",
110220
+ "description": "Whether the status line is an `empty` or `loading` slot, with\n`empty-text` or `loading-text` as its fallback.\n\n**Off unless a family turns it on**, because a slot is published API:\neach family that renders one documents it in its own manifest. The live\nregion keeps saying the text either way, because a slotted picture has no\nwords to say.",
110221
+ "inheritedFrom": {
110222
+ "name": "MixinCollection",
110223
+ "module": "src/mixins/mixin-collection/mixin-collection.ts"
110224
+ }
110225
+ },
109699
110226
  {
109700
110227
  "kind": "field",
109701
110228
  "name": "backLabel",
@@ -109914,6 +110441,16 @@
109914
110441
  "description": "Space between the trigger and the surface, in pixels.",
109915
110442
  "attribute": "offset"
109916
110443
  },
110444
+ {
110445
+ "kind": "field",
110446
+ "name": "openOn",
110447
+ "type": {
110448
+ "text": "'click' | 'long-press'"
110449
+ },
110450
+ "default": "'click'",
110451
+ "description": "What opens the menu from its trigger. `long-press` leaves a press to the\ntrigger's own action and opens the menu on a press held for half a second,\nor on Alt+Down or Alt+Up.",
110452
+ "attribute": "open-on"
110453
+ },
109917
110454
  {
109918
110455
  "kind": "field",
109919
110456
  "name": "pressHeld",
@@ -109941,16 +110478,6 @@
109941
110478
  "privacy": "private",
109942
110479
  "description": "Where a held press on a context menu's area is, to open the menu there."
109943
110480
  },
109944
- {
109945
- "kind": "field",
109946
- "name": "openOn",
109947
- "type": {
109948
- "text": "'click' | 'long-press'"
109949
- },
109950
- "default": "'click'",
109951
- "description": "What opens the menu from its trigger. `long-press` leaves a press to the\ntrigger's own action and opens the menu on a press held for half a second,\nor on Alt+Down or Alt+Up.",
109952
- "attribute": "open-on"
109953
- },
109954
110481
  {
109955
110482
  "kind": "field",
109956
110483
  "name": "placement",
@@ -109961,6 +110488,19 @@
109961
110488
  "description": "The side the menu opens on, when there is room.\n\nIt moves if that side does not fit.",
109962
110489
  "attribute": "placement"
109963
110490
  },
110491
+ {
110492
+ "kind": "field",
110493
+ "name": "resultsLabel",
110494
+ "type": {
110495
+ "text": "string"
110496
+ },
110497
+ "default": "'{count, plural, one {# result} other {# results}}'",
110498
+ "description": "What a screen reader hears when the filter narrows the rows. `{count}` is\nhow many rows are left, written in the locale, and each language writes\nthe forms it has, as Listbox's `results-label` does.",
110499
+ "attribute": "results-label",
110500
+ "helix": {
110501
+ "words": true
110502
+ }
110503
+ },
109964
110504
  {
109965
110505
  "kind": "field",
109966
110506
  "name": "submenus",
@@ -109987,6 +110527,13 @@
109987
110527
  "default": "new KeyboardNavigationController(this, { /* None while closed: the rows are hidden, and a key on the trigger is this element's to answer. Down and Up there open the menu. */ items: () => (this.open ? this.items : []), reserved: () => ['ArrowLeft', 'ArrowRight'], selects: () => false, typeahead: typeaheadBy((item) => slottedWords(item as HelixHeadlessMenuItem, [''])), })",
109988
110528
  "description": "Moving between rows, which is not this element's own business any more.\n\n`selects` false, because arriving at Delete must not delete: moving and\nchoosing are one act in a radio group and two acts here, which is the\ndistinction the controller was already drawing for `Tabs`.\n\nThe origin is the selection, and `focus` is the wrong reading however right\nit looks: that origin also carries a rule a menu must not follow, that a\npress which did not come from the set moves nothing. That rule is there for\na tab panel, which is a tab stop beside the tabs; a menu's panel holds the\nrows and nothing else, and Down on a menu that has just opened is how a\nreader gets to the first row at all.\n\nThe horizontal pair is reserved rather than released. Right opens a\nsubmenu and left comes back, which are the platform's own bindings for a\nmenu that drills, so those keys are spoken for here rather than unhandled."
109989
110529
  },
110530
+ {
110531
+ "kind": "field",
110532
+ "name": "phases",
110533
+ "privacy": "protected",
110534
+ "readonly": true,
110535
+ "default": "new OpenPhaseController(this, { mark: { entering: (on) => this.toggleState('entering', on), exiting: (on) => this.toggleState('exiting', on), }, open: () => this.open, surface: () => this.partNamed('surface'), })"
110536
+ },
109990
110537
  {
109991
110538
  "kind": "field",
109992
110539
  "name": "aria",
@@ -110210,6 +110757,22 @@
110210
110757
  ],
110211
110758
  "description": "Moves the reader to a row: focus, or with `filterable` the highlight,\nwhich the field names with `aria-activedescendant`. An element reference,\nbecause the field is in this shadow root and the row in the tree around\nit, the one direction a reference crosses."
110212
110759
  },
110760
+ {
110761
+ "kind": "field",
110762
+ "name": "rowWatch",
110763
+ "type": {
110764
+ "text": "MutationObserver | undefined"
110765
+ },
110766
+ "privacy": "private",
110767
+ "description": "Watches for rows taken away while the menu is open."
110768
+ },
110769
+ {
110770
+ "kind": "field",
110771
+ "name": "followRemoved",
110772
+ "privacy": "private",
110773
+ "readonly": true,
110774
+ "description": "Moves the reader to a neighbor when the page takes away the row they were\non: the row that took its place, or the one before it where it was the\nlast. A menu with no rows left closes and gives focus to its trigger.\n\n**Focus went to the body**, and the menu then ignored every key, its\nlistener being on the host: measured in Chromium, removing the focused\nrow and pressing ArrowUp did nothing. List and Virtual List move focus to\na neighbor the same way, as React Aria's collections do. With `filterable`,\nfocus stays in the field and the highlight moves. Focus a reader put\nanywhere but the body is left there."
110775
+ },
110213
110776
  {
110214
110777
  "kind": "method",
110215
110778
  "name": "show",
@@ -110311,6 +110874,44 @@
110311
110874
  },
110312
110875
  "description": "Returns to the page above, or closes when there is none."
110313
110876
  },
110877
+ {
110878
+ "kind": "field",
110879
+ "name": "choiceRows",
110880
+ "type": {
110881
+ "text": "HelixHeadlessMenuItem[]"
110882
+ },
110883
+ "privacy": "private",
110884
+ "description": "The choice rows anywhere in the menu, in document order.",
110885
+ "readonly": true
110886
+ },
110887
+ {
110888
+ "kind": "field",
110889
+ "name": "checkedValues",
110890
+ "type": {
110891
+ "text": "string[]"
110892
+ },
110893
+ "description": "The values of the choice rows that are checked, anywhere in the menu, in\ndocument order.\n\nSet it to check exactly those rows and uncheck every other choice row, as\nReact Aria's `selectedKeys` controls a menu's selection from one place.\nEach row keeps its own `checked` too, which this reads and writes. `change`\nis sent on the menu when a reader's choice changes it."
110894
+ },
110895
+ {
110896
+ "kind": "field",
110897
+ "name": "toldChecked",
110898
+ "type": {
110899
+ "text": "string | undefined"
110900
+ },
110901
+ "privacy": "private",
110902
+ "description": "The checked values `change` last said, joined, so a choice that moved nothing says nothing."
110903
+ },
110904
+ {
110905
+ "kind": "method",
110906
+ "name": "tellChecked",
110907
+ "privacy": "private",
110908
+ "return": {
110909
+ "type": {
110910
+ "text": "void"
110911
+ }
110912
+ },
110913
+ "description": "Sends `change` when a reader's choice has changed which rows are checked."
110914
+ },
110314
110915
  {
110315
110916
  "kind": "field",
110316
110917
  "name": "handleSelect",
@@ -110507,7 +111108,7 @@
110507
111108
  "name": "handleHover",
110508
111109
  "privacy": "private",
110509
111110
  "readonly": true,
110510
- "description": "A mouse resting on a row, under `flyout`: focus follows it, and after a\nmoment the row's submenu opens, or the submenus below the row's own page\nclose. Not while the pointer is crossing toward an open submenu."
111111
+ "description": "A mouse resting on a row: focus follows it, and under `flyout`, after a\nmoment, the row's submenu opens, or the submenus below the row's own page\nclose. Not while the pointer is crossing toward an open submenu.\n\n**In every mode**, as the APG's menus, React Aria's and Radix's have it.\nUnder `drill` the pointer did not move focus, so the row under the\npointer and the row the keys moved from were two rows, measured in\nChromium: hovering Delete left focus on Cut."
110511
111112
  },
110512
111113
  {
110513
111114
  "kind": "field",
@@ -110535,6 +111136,16 @@
110535
111136
  ],
110536
111137
  "description": "Whether the pointer is inside the grace area, while it holds."
110537
111138
  },
111139
+ {
111140
+ "kind": "field",
111141
+ "name": "namedByPage",
111142
+ "type": {
111143
+ "text": "boolean"
111144
+ },
111145
+ "privacy": "private",
111146
+ "default": "false",
111147
+ "description": "Whether the surface is named by a submenu's page, which `nameSurface` undoes at the top."
111148
+ },
110538
111149
  {
110539
111150
  "kind": "method",
110540
111151
  "name": "nameSurface",
@@ -110544,7 +111155,7 @@
110544
111155
  "text": "void"
110545
111156
  }
110546
111157
  },
110547
- "description": "Names the surface by its trigger, where nothing else names it.\n\nBy element reference, which is the one direction that crosses: the surface\nis in this shadow root and the trigger is in the tree around it. A host\n`aria-labelledby` is carried down by `aria`, and a host `aria-label` or\n`label` is a name of its own, so each of them clears this reference."
111158
+ "description": "Names the surface by its trigger, where nothing else names it, and a\nsubmenu's page by its title.\n\nBy element reference, which is the one direction that crosses: the surface\nis in this shadow root and the trigger is in the tree around it. A host\n`aria-labelledby` is carried down by `aria`, and a host `aria-label` or\n`label` is a name of its own, so each of them clears this reference."
110548
111159
  },
110549
111160
  {
110550
111161
  "kind": "method",
@@ -110776,20 +111387,6 @@
110776
111387
  "module": "src/mixins/mixin-collection/mixin-collection.ts"
110777
111388
  }
110778
111389
  },
110779
- {
110780
- "kind": "field",
110781
- "name": "statusSlots",
110782
- "type": {
110783
- "text": "boolean"
110784
- },
110785
- "privacy": "protected",
110786
- "default": "false",
110787
- "description": "Whether the status line is an `empty` or `loading` slot, with\n`empty-text` or `loading-text` as its fallback.\n\n**Off unless a family turns it on**, because a slot is published API:\neach family that renders one documents it in its own manifest. The live\nregion keeps saying the text either way, because a slotted picture has no\nwords to say.",
110788
- "inheritedFrom": {
110789
- "name": "MixinCollection",
110790
- "module": "src/mixins/mixin-collection/mixin-collection.ts"
110791
- }
110792
- },
110793
111390
  {
110794
111391
  "kind": "field",
110795
111392
  "name": "defaultOpen",
@@ -111251,6 +111848,14 @@
111251
111848
  }
111252
111849
  ],
111253
111850
  "events": [
111851
+ {
111852
+ "type": {
111853
+ "text": "Event"
111854
+ },
111855
+ "description": "A reader's choice checked or unchecked rows. Read `checkedValues` for the rows that are checked now.",
111856
+ "name": "change",
111857
+ "reactProp": "onchange"
111858
+ },
111254
111859
  {
111255
111860
  "type": {
111256
111861
  "text": "HelixBeforeCloseEvent"
@@ -111405,6 +112010,15 @@
111405
112010
  "description": "The side the menu opens on, when there is room.\n\nIt moves if that side does not fit.",
111406
112011
  "fieldName": "placement"
111407
112012
  },
112013
+ {
112014
+ "name": "results-label",
112015
+ "type": {
112016
+ "text": "string"
112017
+ },
112018
+ "default": "'{count, plural, one {# result} other {# results}}'",
112019
+ "description": "What a screen reader hears when the filter narrows the rows. `{count}` is\nhow many rows are left, written in the locale, and each language writes\nthe forms it has, as Listbox's `results-label` does.",
112020
+ "fieldName": "resultsLabel"
112021
+ },
111408
112022
  {
111409
112023
  "name": "submenus",
111410
112024
  "type": {
@@ -111484,6 +112098,14 @@
111484
112098
  "description": "A submenu's page is showing, so content that is not a row can hide with the top page.",
111485
112099
  "name": "drilled"
111486
112100
  },
112101
+ {
112102
+ "description": "From the moment the panel shows until its entrance has finished.",
112103
+ "name": "entering"
112104
+ },
112105
+ {
112106
+ "description": "From the moment the panel hides until its exit has finished.",
112107
+ "name": "exiting"
112108
+ },
111487
112109
  {
111488
112110
  "description": "A row on the current page has a checkbox or a link, so every row keeps the trailing column.",
111489
112111
  "name": "mark"
@@ -116935,7 +117557,7 @@
116935
117557
  "text": "string"
116936
117558
  },
116937
117559
  "default": "'Decrease'",
116938
- "description": "The name of the step down button, read by screen readers.",
117560
+ "description": "The name of the step down button, read by screen readers. The field's\nlabel follows it, so a reader hears \"Decrease Quantity\", as React Aria\nnames its steppers.",
116939
117561
  "attribute": "decrement-label",
116940
117562
  "helix": {
116941
117563
  "words": true
@@ -116957,7 +117579,7 @@
116957
117579
  "text": "string"
116958
117580
  },
116959
117581
  "default": "'Increase'",
116960
- "description": "The name of the step up button, read by screen readers.",
117582
+ "description": "The name of the step up button, read by screen readers. The field's\nlabel follows it, so a reader hears \"Increase Quantity\", as React Aria\nnames its steppers.",
116961
117583
  "attribute": "increment-label",
116962
117584
  "helix": {
116963
117585
  "words": true
@@ -117333,13 +117955,16 @@
117333
117955
  },
117334
117956
  "parameters": [
117335
117957
  {
117336
- "name": "large",
117337
- "default": "false"
117958
+ "name": "count",
117959
+ "default": "1",
117960
+ "type": {
117961
+ "text": "number | boolean"
117962
+ }
117338
117963
  }
117339
117964
  ],
117340
- "description": "Steps the value up, by `largeStep` steps when `large` is set.",
117965
+ "description": "Steps the value up by `step`, `count` times, as the native method and\n`Input.stepUp` do. `true` takes `largeStep` steps, as `PageUp` does.",
117341
117966
  "type": {
117342
- "text": "stepUp(large = false) => void"
117967
+ "text": "stepUp(count: number | boolean = 1) => void"
117343
117968
  }
117344
117969
  },
117345
117970
  {
@@ -117352,15 +117977,37 @@
117352
117977
  },
117353
117978
  "parameters": [
117354
117979
  {
117355
- "name": "large",
117356
- "default": "false"
117980
+ "name": "count",
117981
+ "default": "1",
117982
+ "type": {
117983
+ "text": "number | boolean"
117984
+ }
117357
117985
  }
117358
117986
  ],
117359
- "description": "Steps the value down, by `largeStep` steps when `large` is set.\n\nThe same path a press of the down arrow takes, so a consumer driving this\nfrom their own control gets the clamping and the announcement rather than\nhaving to repeat both.",
117987
+ "description": "Steps the value down by `step`, `count` times. `true` takes `largeStep`\nsteps, as `PageDown` does.\n\nThe same path a press of the down arrow takes, so a consumer driving this\nfrom their own control gets the clamping and the announcement rather than\nhaving to repeat both.",
117360
117988
  "type": {
117361
- "text": "stepDown(large = false) => void"
117989
+ "text": "stepDown(count: number | boolean = 1) => void"
117362
117990
  }
117363
117991
  },
117992
+ {
117993
+ "kind": "method",
117994
+ "name": "stepsOf",
117995
+ "privacy": "private",
117996
+ "return": {
117997
+ "type": {
117998
+ "text": "number"
117999
+ }
118000
+ },
118001
+ "parameters": [
118002
+ {
118003
+ "name": "count",
118004
+ "type": {
118005
+ "text": "number | boolean"
118006
+ }
118007
+ }
118008
+ ],
118009
+ "description": "How many steps a `stepUp` argument asks for: a count, or `largeStep` for `true`."
118010
+ },
117364
118011
  {
117365
118012
  "kind": "field",
117366
118013
  "name": "repeat",
@@ -118974,7 +119621,7 @@
118974
119621
  "text": "string"
118975
119622
  },
118976
119623
  "default": "'Decrease'",
118977
- "description": "The name of the step down button, read by screen readers.",
119624
+ "description": "The name of the step down button, read by screen readers. The field's\nlabel follows it, so a reader hears \"Decrease Quantity\", as React Aria\nnames its steppers.",
118978
119625
  "fieldName": "decrementLabel"
118979
119626
  },
118980
119627
  {
@@ -118991,7 +119638,7 @@
118991
119638
  "text": "string"
118992
119639
  },
118993
119640
  "default": "'Increase'",
118994
- "description": "The name of the step up button, read by screen readers.",
119641
+ "description": "The name of the step up button, read by screen readers. The field's\nlabel follows it, so a reader hears \"Increase Quantity\", as React Aria\nnames its steppers.",
118995
119642
  "fieldName": "incrementLabel"
118996
119643
  },
118997
119644
  {
@@ -129527,7 +130174,7 @@
129527
130174
  "declarations": [
129528
130175
  {
129529
130176
  "kind": "class",
129530
- "description": "Popover is the general overlay that the specific ones build on. `Menu` holds\ncommands, `Select` holds options, and `Tooltip` holds a description. Popover\nholds any content you put in it, so it has no keyboard handling of its own.\n\nThe trigger is slotted and stays your own element. A Helix trigger is told\nwhether the popover is open inside its own shadow root. On a plain element,\nsuch as a `<button>`, the popover writes `aria-expanded`, from a frame after\nits own first render, or from the first press where that comes sooner. It\nnever writes it where you wrote `aria-expanded` yourself, and writes nothing\nelse onto your markup.\n\nThe panel is in the top layer through the popover API, so it needs no\n`z-index`, and an ancestor with `overflow: hidden`, `transform`, `filter`, or\n`contain` does not clip or move it.\n\nIt opens on click and from a command. `command=\"--show\"` and the related\ncommands work as they do on `Dialog` and `Drawer`, so a button anywhere on\nthe page can open the popover with no script.\n\n**Opening moves focus into the panel**, to the first control in it, or to the\npanel itself where it holds none, and closing from inside gives it back to\nthe trigger. It closes on Escape, on a press outside it, and when focus moves\nout of the trigger and the panel to somewhere else on the page.\n\n`modal` keeps `Tab` inside the panel while it is open and says so with\n`aria-modal`, for a panel that has to be answered before the page goes on.\nThe rest of the page is hidden from screen readers with `aria-hidden` while\nit is open, so the virtual cursor stays in the panel too. Without it the\npopover does not trap focus.\n\nThe panel's name is `label` when you set it. Otherwise it is the first\nheading in the panel, or the elements an `aria-labelledby` on the host names.\nA development build warns when a popover opens with nothing but\n`fallback-label` to name it.\n\n**Keyboard.** Opening moves focus to the first control in the panel, or to\nthe panel where it holds none.\n\n| Key | What it does |\n|---|---|\n| Enter, Space | On the trigger, opens the panel, or closes it while it is open. |\n| Escape | Closes the panel and puts focus back on the trigger. |\n| Tab, Shift+Tab | Moves through the controls in the panel. Leaving the panel and the trigger closes it. With `modal`, moves from the last control round to the first and from the first round to the last. |\n\n`arrow` draws a point on the panel's edge facing the trigger. The side the\npanel landed on is on the host as `:state(placed-top)` and its kin, and the\ntrigger's width is on the panel as `--floating-anchor-inline-size`, for a\npanel as wide as its trigger:\n\n```css\nhelix-popover::part(popover) {\n min-inline-size: var(--floating-anchor-inline-size);\n}\n```",
130177
+ "description": "Popover is the general overlay that the specific ones build on. `Menu` holds\ncommands, `Select` holds options, and `Tooltip` holds a description. Popover\nholds any content you put in it, so it has no keyboard handling of its own.\n\nThe trigger is slotted and stays your own element. A Helix trigger is told\nwhether the popover is open inside its own shadow root. On a plain element,\nsuch as a `<button>`, the popover writes `aria-expanded`, from a frame after\nits own first render, or from the first press where that comes sooner. It\nnever writes it where you wrote `aria-expanded` yourself, and writes nothing\nelse onto your markup.\n\nThe panel is in the top layer through the popover API, so it needs no\n`z-index`, and an ancestor with `overflow: hidden`, `transform`, `filter`, or\n`contain` does not clip or move it.\n\nIt opens on click and from a command. `command=\"--show\"` and the related\ncommands work as they do on `Dialog` and `Drawer`, so a button anywhere on\nthe page can open the popover with no script. From its first press, a\nbutton that opens it this way says whether the popover is open: a Helix\nbutton through `expanded`, and any other element with `aria-expanded`.\n\n**Opening moves focus into the panel**, to the first control in it, or to the\npanel itself where it holds none, and closing from inside gives it back to\nthe trigger. It closes on Escape, on a press outside it, and when focus moves\nout of the trigger and the panel to somewhere else on the page.\n\n`modal` keeps `Tab` inside the panel while it is open and says so with\n`aria-modal`, for a panel that has to be answered before the page goes on.\nThe rest of the page is hidden from screen readers with `aria-hidden` while\nit is open, so the virtual cursor stays in the panel too, and the page does\nnot scroll behind it. Without it the popover does not trap focus.\n\nA press outside closes the panel and leaves focus where the press put it.\nWhere it put focus nowhere, as a press on text or on the page's background\ndoes, focus goes back to the trigger.\n\nThe panel's name is `label` when you set it. Otherwise it is the first\nheading in the panel, or the elements an `aria-labelledby` on the host names.\nA development build warns when a popover opens with nothing but\n`fallback-label` to name it.\n\n**Keyboard.** Opening moves focus to the first control in the panel, or to\nthe panel where it holds none.\n\n| Key | What it does |\n|---|---|\n| Enter, Space | On the trigger, opens the panel, or closes it while it is open. |\n| Escape | Closes the panel and puts focus back on the trigger. |\n| Tab, Shift+Tab | Moves through the controls in the panel. Leaving the panel and the trigger closes it. With `modal`, moves from the last control round to the first and from the first round to the last. |\n\nThe panel opens against the trigger, or against the element `anchor` names,\nsuch as a field beside the button that opens it. `offset` moves it away from\nthat element, `cross-offset` slides it along the side it is on,\n`container-padding` keeps it that far from the window's edges, and\n`flip=\"false\"` keeps it on its side where the other side has more room.\n\n`arrow` draws a point on the panel's edge facing the trigger. The side the\npanel landed on is on the host as `:state(placed-top)` and its kin, its\nalignment along that side as `:state(aligned-start)` or\n`:state(aligned-end)`, and the trigger's width is on the panel as\n`--floating-anchor-inline-size`, for a panel as wide as its trigger:\n\n```css\nhelix-popover::part(popover) {\n min-inline-size: var(--floating-anchor-inline-size);\n}\n```",
129531
130178
  "name": "HelixHeadlessPopover",
129532
130179
  "cssProperties": [
129533
130180
  {
@@ -129590,6 +130237,16 @@
129590
130237
  "default": "'helix-popover'",
129591
130238
  "description": "The key this family's words are registered under, its styled tag:\n`registerWords('de', { 'helix-popover': { fallbackLabel: 'Popup' } })`."
129592
130239
  },
130240
+ {
130241
+ "kind": "field",
130242
+ "name": "anchor",
130243
+ "type": {
130244
+ "text": "Element | string"
130245
+ },
130246
+ "default": "''",
130247
+ "description": "The element the panel opens against, where that is not the trigger: its\n`id` in the same document or shadow root, or the element itself. Left\nempty, the panel opens against the trigger, or against the button whose\ncommand opened it.",
130248
+ "attribute": "anchor"
130249
+ },
129593
130250
  {
129594
130251
  "kind": "field",
129595
130252
  "name": "arrow",
@@ -129600,6 +130257,25 @@
129600
130257
  "description": "Draws a point on the panel's edge facing the trigger.\n\nOff by default, which is the house style: a panel that opens from its\ntrigger already reads as belonging to it.",
129601
130258
  "attribute": "arrow"
129602
130259
  },
130260
+ {
130261
+ "kind": "field",
130262
+ "name": "containerPadding",
130263
+ "type": {
130264
+ "text": "number | undefined"
130265
+ },
130266
+ "description": "How close to the window's edges the panel may come, in pixels, when it\nslides, shrinks or moves to the other side to fit. Left unset, it slides\nand shrinks to 8 pixels from an edge and moves to the other side only\nonce it would cross one.",
130267
+ "attribute": "container-padding"
130268
+ },
130269
+ {
130270
+ "kind": "field",
130271
+ "name": "crossOffset",
130272
+ "type": {
130273
+ "text": "number"
130274
+ },
130275
+ "default": "0",
130276
+ "description": "How far to slide the panel along the side it is on, in pixels: to the\nright on the top or the bottom, and down on the left or the right.",
130277
+ "attribute": "cross-offset"
130278
+ },
129603
130279
  {
129604
130280
  "kind": "field",
129605
130281
  "name": "fallbackLabel",
@@ -129613,6 +130289,16 @@
129613
130289
  "words": true
129614
130290
  }
129615
130291
  },
130292
+ {
130293
+ "kind": "field",
130294
+ "name": "flip",
130295
+ "type": {
130296
+ "text": "boolean"
130297
+ },
130298
+ "default": "true",
130299
+ "description": "Whether the panel moves to the other side of the trigger when its own\nside has no room. On by default. Write `flip=\"false\"` to keep it on its\nside, where it shrinks to fit, as React Aria's `shouldFlip` turns off.",
130300
+ "attribute": "flip"
130301
+ },
129616
130302
  {
129617
130303
  "kind": "field",
129618
130304
  "name": "label",
@@ -129672,7 +130358,7 @@
129672
130358
  "name": "panel",
129673
130359
  "privacy": "private",
129674
130360
  "readonly": true,
129675
- "default": "new PopoverController(this, { anchor: () => this.trigger, arrow: () => this.partNamed('arrow'), /* The host *and* whatever invoked it, never the panel alone. An outside press is measured against this, and the host is not a box to rely on, a sheet being free to give it none, so a boundary of the panel alone would close on the very press that opened it. A `--toggle` button outside the host is part of the control by any reading a reader would give it: pressing it while the panel is open should close it once, not close it on `pointerdown` and reopen it on the `click` that follows. */ boundary: (): (HTMLElement | undefined)[] => [this, this.invoker], /* Measured to the point where there is one, as `Tooltip` measures it: the arrow is a square turned on its center, which reaches `n / sqrt(2)` past the edge it sits on. */ offset: () => this.offset + arrowReach(this.partNamed('arrow')), onDismiss: () => this.hide(), /* The side the panel landed on, for a sheet outside that draws by side, where no attribute can follow `::part()`. As `Tooltip` records it. */ onPlace: (placement) => { const side = sideOf(placement) this.toggleState('placed-bottom', side === 'bottom') this.toggleState('placed-left', side === 'left') this.toggleState('placed-right', side === 'right') this.toggleState('placed-top', side === 'top') this.side = side }, open: () => this.open, placement: () => this.placement, surface: () => this.partNamed('popover'), })"
130361
+ "default": "new PopoverController(this, { anchor: () => this.placedAgainst, arrow: () => this.partNamed('arrow'), /* The host *and* whatever invoked it, never the panel alone. An outside press is measured against this, and the host is not a box to rely on, a sheet being free to give it none, so a boundary of the panel alone would close on the very press that opened it. A `--toggle` button outside the host is part of the control by any reading a reader would give it: pressing it while the panel is open should close it once, not close it on `pointerdown` and reopen it on the `click` that follows. */ boundary: (): (HTMLElement | undefined)[] => [this, this.invoker], crossOffset: () => this.crossOffset, flip: () => this.flip, /* Measured to the point where there is one, as `Tooltip` measures it: the arrow is a square turned on its center, which reaches `n / sqrt(2)` past the edge it sits on. */ offset: () => this.offset + arrowReach(this.partNamed('arrow')), onDismiss: () => this.dismiss(), /* The side the panel landed on, for a sheet outside that draws by side, where no attribute can follow `::part()`. As `Tooltip` records it. */ onPlace: (placement) => { const side = sideOf(placement) this.toggleState('aligned-end', placement.endsWith('-end')) this.toggleState('aligned-start', placement.endsWith('-start')) this.toggleState('placed-bottom', side === 'bottom') this.toggleState('placed-left', side === 'left') this.toggleState('placed-right', side === 'right') this.toggleState('placed-top', side === 'top') this.side = side }, open: () => this.open, padding: () => this.containerPadding, placement: () => this.placement, surface: () => this.partNamed('popover'), })"
129676
130362
  },
129677
130363
  {
129678
130364
  "kind": "field",
@@ -129735,11 +130421,11 @@
129735
130421
  "name": "commands",
129736
130422
  "privacy": "protected",
129737
130423
  "readonly": true,
129738
- "default": "new CommandController(this, { commands: { '--close': () => this.hide(), '--show': (source: Element | null) => { this.invoker = source instanceof HTMLElement ? source : undefined this.show() }, '--toggle': (source: Element | null) => { this.invoker = source instanceof HTMLElement ? source : undefined if (this.open) { this.hide() } else { this.show() } }, }, })"
130424
+ "default": "new CommandController(this, { commands: { '--close': () => this.hide(), '--show': (source: Element | null) => { this.invoker = source instanceof HTMLElement ? source : undefined this.show() }, '--toggle': (source: Element | null) => { this.invoker = source instanceof HTMLElement ? source : undefined if (this.open) { this.hide() } else { this.show() } }, }, expanded: () => this.open, })"
129739
130425
  },
129740
130426
  {
129741
130427
  "kind": "field",
129742
- "name": "trigger",
130428
+ "name": "placedAgainst",
129743
130429
  "type": {
129744
130430
  "text": "HTMLElement"
129745
130431
  },
@@ -129747,6 +130433,27 @@
129747
130433
  "description": "The element the panel opens against.",
129748
130434
  "readonly": true
129749
130435
  },
130436
+ {
130437
+ "kind": "field",
130438
+ "name": "trigger",
130439
+ "type": {
130440
+ "text": "HTMLElement"
130441
+ },
130442
+ "privacy": "private",
130443
+ "description": "The element that opened the panel, which focus goes back to.",
130444
+ "readonly": true
130445
+ },
130446
+ {
130447
+ "kind": "method",
130448
+ "name": "dismiss",
130449
+ "privacy": "private",
130450
+ "return": {
130451
+ "type": {
130452
+ "text": "void"
130453
+ }
130454
+ },
130455
+ "description": "A press outside, or Escape, closing the panel.\n\n**Focus goes back to the trigger where the press put it nowhere.** The\npress is heard on `pointerdown`, and the browser moves focus a moment\nlater: onto what was pressed where that takes focus, and to the body where\nit does not. A popover dismissed by a press on text was left with focus on\nthe body, measured in Chromium, where React Aria's returns it to the\ntrigger. Read after the press, once the browser has moved it."
130456
+ },
129750
130457
  {
129751
130458
  "kind": "method",
129752
130459
  "name": "show",
@@ -130383,6 +131090,15 @@
130383
131090
  }
130384
131091
  ],
130385
131092
  "attributes": [
131093
+ {
131094
+ "name": "anchor",
131095
+ "type": {
131096
+ "text": "Element | string"
131097
+ },
131098
+ "default": "''",
131099
+ "description": "The element the panel opens against, where that is not the trigger: its\n`id` in the same document or shadow root, or the element itself. Left\nempty, the panel opens against the trigger, or against the button whose\ncommand opened it.",
131100
+ "fieldName": "anchor"
131101
+ },
130386
131102
  {
130387
131103
  "name": "arrow",
130388
131104
  "type": {
@@ -130392,6 +131108,23 @@
130392
131108
  "description": "Draws a point on the panel's edge facing the trigger.\n\nOff by default, which is the house style: a panel that opens from its\ntrigger already reads as belonging to it.",
130393
131109
  "fieldName": "arrow"
130394
131110
  },
131111
+ {
131112
+ "name": "container-padding",
131113
+ "type": {
131114
+ "text": "number | undefined"
131115
+ },
131116
+ "description": "How close to the window's edges the panel may come, in pixels, when it\nslides, shrinks or moves to the other side to fit. Left unset, it slides\nand shrinks to 8 pixels from an edge and moves to the other side only\nonce it would cross one.",
131117
+ "fieldName": "containerPadding"
131118
+ },
131119
+ {
131120
+ "name": "cross-offset",
131121
+ "type": {
131122
+ "text": "number"
131123
+ },
131124
+ "default": "0",
131125
+ "description": "How far to slide the panel along the side it is on, in pixels: to the\nright on the top or the bottom, and down on the left or the right.",
131126
+ "fieldName": "crossOffset"
131127
+ },
130395
131128
  {
130396
131129
  "name": "fallback-label",
130397
131130
  "type": {
@@ -130401,6 +131134,15 @@
130401
131134
  "description": "The panel's name where nothing else names it: no `label`, no heading in\nthe panel, and no `aria-labelledby` on the host.",
130402
131135
  "fieldName": "fallbackLabel"
130403
131136
  },
131137
+ {
131138
+ "name": "flip",
131139
+ "type": {
131140
+ "text": "boolean"
131141
+ },
131142
+ "default": "true",
131143
+ "description": "Whether the panel moves to the other side of the trigger when its own\nside has no room. On by default. Write `flip=\"false\"` to keep it on its\nside, where it shrinks to fit, as React Aria's `shouldFlip` turns off.",
131144
+ "fieldName": "flip"
131145
+ },
130404
131146
  {
130405
131147
  "name": "label",
130406
131148
  "type": {
@@ -130464,6 +131206,14 @@
130464
131206
  }
130465
131207
  ],
130466
131208
  "cssStates": [
131209
+ {
131210
+ "description": "While the panel is aligned to the end of the side it sits on.",
131211
+ "name": "aligned-end"
131212
+ },
131213
+ {
131214
+ "description": "While the panel is aligned to the start of the side it sits on.",
131215
+ "name": "aligned-start"
131216
+ },
130467
131217
  {
130468
131218
  "description": "From the moment the panel shows until its entrance has finished.",
130469
131219
  "name": "entering"
@@ -140434,7 +141184,7 @@
140434
141184
  "declarations": [
140435
141185
  {
140436
141186
  "kind": "class",
140437
- "description": "A trigger that opens a `Listbox`. The list is the same element you can use on\nits own, with the same keyboard behavior, filtering, and multi-select rules.\nSelect adds a trigger that shows what is chosen, a floating surface, and a\nplace for focus to return to when the list closes.\n\nYour options stay in your light DOM and are slotted through into the listbox,\nso the listbox looks through slots to find them. For a list too long to draw\nwhole, set `items` instead: the list draws only the options in sight, as\nListbox's `items` does, and the trigger, its keys and the form read the\nwhole set.\n\n`Select` owns the value, passes it down to the listbox, and takes changes\nback, so the value lives in one place. With `multiple`, the value is the\nchosen values joined with commas, and the form receives one entry for each.\nAn option's value may hold a comma, and it is read as one value.\n\nSet `valueContent` to draw the trigger's content from the chosen options,\nand several chosen options are written as a list in the reader's language.\n\n**Keyboard.** On the closed trigger:\n\n| Key | What it does |\n|---|---|\n| Down, Enter, Space | Opens the list on the chosen option, or the first. |\n| Up | Opens the list on the chosen option, or the last. |\n| Home, End | Opens the list on the first or the last option. |\n| Left, Right | Chooses the option before or after the chosen one without opening, as React Aria's Select does, in either direction of text. |\n| A letter or a word | Chooses the option whose words start with what you typed, as a native `<select>` does. With `multiple`, opens the list on it. |\n\nIn the open list, which is a `Listbox`:\n\n| Key | What it does |\n|---|---|\n| Down, Up, Home, End, Page Down, Page Up | Moves the highlight. |\n| A letter or a word | Moves the highlight to the next option whose words start with what you typed. |\n| Enter, Space | Chooses the highlighted option and closes the list. With `multiple`, toggles it and leaves the list open. |\n| Escape | Closes the list and puts focus back on the trigger. |\n| Tab | Closes the list and moves on. |\n\nA choice made on the closed trigger is said aloud on Apple devices, where\nVoiceOver does not read the trigger's new name.\n\n**ARIA.** The trigger is a `button` with `aria-haspopup=\"listbox\"` and\n`aria-expanded`, named by the label and then the chosen words, as a native\n`<select>` is. The list is the listbox `Listbox` describes, named by the\nlabel, or by `list-label` where there is none.\n\nSet `autocomplete` to let a browser's autofill choose, as a native `<select>`\nwith the same attribute would.",
141187
+ "description": "A trigger that opens a `Listbox`. The list is the same element you can use on\nits own, with the same keyboard behavior, filtering, and multi-select rules.\nSelect adds a trigger that shows what is chosen, a floating surface, and a\nplace for focus to return to when the list closes.\n\nYour options stay in your light DOM and are slotted through into the listbox,\nso the listbox looks through slots to find them. For a list too long to draw\nwhole, set `items` instead: the list draws only the options in sight, as\nListbox's `items` does, and the trigger, its keys and the form read the\nwhole set.\n\n`Select` owns the value, passes it down to the listbox, and takes changes\nback, so the value lives in one place. With `multiple`, the value is the\nchosen values joined with commas, and the form receives one entry for each.\nAn option's value may hold a comma, and it is read as one value.\n\nSet `valueContent` to draw the trigger's content from the chosen options,\nand several chosen options are written as a list in the reader's language.\n\n**Keyboard.** On the closed trigger:\n\n| Key | What it does |\n|---|---|\n| Down, Enter, Space | Opens the list on the chosen option, or the first. |\n| Up | Opens the list on the chosen option, or the last. |\n| Home, End | Opens the list on the first or the last option. |\n| Left, Right | Chooses the option before or after the chosen one without opening, as React Aria's Select does. On a right-to-left page Left chooses the one after. |\n| A letter or a word | Chooses the option whose words start with what you typed, as a native `<select>` does. With `multiple`, opens the list on it. |\n\nIn the open list, which is a `Listbox`:\n\n| Key | What it does |\n|---|---|\n| Down, Up, Home, End, Page Down, Page Up | Moves the highlight. |\n| A letter or a word | Moves the highlight to the next option whose words start with what you typed. |\n| Enter, Space | Chooses the highlighted option and closes the list. With `multiple`, toggles it and leaves the list open. |\n| Escape | Closes the list and puts focus back on the trigger. |\n| Tab | Closes the list and moves on. |\n\nA choice made on the closed trigger is said aloud on Apple devices, where\nVoiceOver does not read the trigger's new name.\n\n**ARIA.** The trigger is a `button` with `aria-haspopup=\"listbox\"` and\n`aria-expanded`, named by the label and then the chosen words, as a native\n`<select>` is. The list is the listbox `Listbox` describes, named by the\nlabel, or by `list-label` where there is none.\n\nSet `autocomplete` to let a browser's autofill choose, as a native `<select>`\nwith the same attribute would.",
140438
141188
  "name": "HelixHeadlessSelect",
140439
141189
  "cssProperties": [
140440
141190
  {
@@ -140967,7 +141717,7 @@
140967
141717
  "name": "handleTriggerKeydown",
140968
141718
  "privacy": "private",
140969
141719
  "readonly": true,
140970
- "description": "The keys of the closed trigger.\n\nLetters choose without opening, the way a native `<select>` and React\nAria's Select do, so a reader who knows the answer types it. A Space in the\nmiddle of those letters is one of them. With `multiple`, a letter opens the\nlist on the match instead, because adding to a set is a choice the reader\nmakes in the list. ArrowLeft and ArrowRight choose the option before and\nafter the chosen one."
141720
+ "description": "The keys of the closed trigger.\n\nLetters choose without opening, the way a native `<select>` and React\nAria's Select do, so a reader who knows the answer types it. A Space in the\nmiddle of those letters is one of them. With `multiple`, a letter opens the\nlist on the match instead, because adding to a set is a choice the reader\nmakes in the list. ArrowLeft and ArrowRight choose the option before and\nafter the chosen one, the other way round on a right-to-left page."
140971
141721
  },
140972
141722
  {
140973
141723
  "kind": "method",
@@ -152522,7 +153272,7 @@
152522
153272
  "declarations": [
152523
153273
  {
152524
153274
  "kind": "class",
152525
- "description": "`active` sets where the user is, and `complete` stays on each step. Returning\nto stage one of three leaves stages one and two finished, so the root sets\n`current` on the steps and never changes `complete`.\n\n`active` is optional. Without it, each step keeps its own `current`, so the\nsequence is correct in the server's markup, where the root cannot count its\nslotted children.\n\n`linear` keeps a reader from passing a stage they have not finished, as Ark\nUI's linear steps do: a stage ahead is out of reach, and its button disabled,\nuntil every stage before it is `complete` or `optional`. Going back is always\nallowed. `next()` and `previous()` move one stage either way, under the\nsame rules, for your own buttons. `reset()` goes back to the first stage and\nclears every stage's `complete`, as Ark UI's `resetStep` does.\n\n`completion=\"passed\"` marks a stage `complete` once a reader moves past it,\nas Ark UI counts a stage before the current one as completed. Going back\nleaves it complete. In a `linear` sequence the next stage is then in reach\nfrom the current one, and `validate` decides whether the reader may go.\n\nTo check a stage before a reader moves on from it, set `validate` to a\nfunction that answers whether the stage is valid, as Ark UI's `isStepValid`\ndoes. Moving forward asks it about the stage the reader is on, and where it\nanswers `false` the reader stays and the stage is `:state(invalid)` until it\nanswers `true`. An `optional` stage is not asked. `helix-before-activate`\nis still sent for every move, for a check of your own.\n\n`progress` is how far through the sequence is, from 0 to 100: the share of\nits stages that are `complete`. The `progress` part carries it as a\n`progressbar` and as `--steps-progress`, and is hidden until a stylesheet\ngives it a `display`, so a bar of your own costs nothing until you draw it.\nA screen reader hears it as `progress-label` says, \"33% complete\" unless\nset, as Ark UI's steps write it. The sequence is `:state(complete)` once\nevery stage is.\n\nThe sequence is a list named by `label`, and each step a list item. The\ncurrent stage's button says `aria-current=\"step\"`, and a finished stage's\nbutton is described by its `complete-label`.\n\n**Keyboard.** Each stage's name is a button and its own tab stop, and Enter\nor Space goes to the stage. Set `arrows` and the arrow keys move focus\nbetween the names as well, as Ark UI's steps do: Left and Right along a row,\nUp and Down a column, Home and End to the first and the last. The arrows\npass over a disabled stage and one a linear sequence holds out of reach,\nand they move focus without going to the stage.",
153275
+ "description": "`active` sets where the user is, and `complete` stays on each step. Returning\nto stage one of three leaves stages one and two finished, so the root sets\n`current` on the steps and never changes `complete`.\n\n`active` is optional. Without it, each step keeps its own `current`, so the\nsequence is correct in the server's markup, where the root cannot count its\nslotted children.\n\n`active` names a step by its `value`. A step with no `value` is named by its\nplace, counted from 1, so a sequence written without values moves as one\nwritten with them, and `helix-activate` says `1`, `2` and so on. Give every\nstep a value or none, because a value of `2` and the second place are one\nname.\n\n`linear` keeps a reader from passing a stage they have not finished, as Ark\nUI's linear steps do: a stage ahead is out of reach, and its button disabled,\nuntil every stage before it is `complete` or `optional`. Going back is always\nallowed. `next()` and `previous()` move one stage either way, under the\nsame rules, for your own buttons. `reset()` goes back to the first stage and\nclears every stage's `complete`, as Ark UI's `resetStep` does. A button\nanywhere on the page calls them with no script, as Ark UI's previous and\nnext triggers do: give it `commandfor` with the sequence's `id`, and\n`command=\"--previous\"`, `\"--next\"` or `\"--reset\"`.\n\n`completion=\"passed\"` marks a stage `complete` once a reader moves past it,\nas Ark UI counts a stage before the current one as completed. Going back\nleaves it complete. In a `linear` sequence the next stage is then in reach\nfrom the current one, and `validate` decides whether the reader may go.\n\nTo check a stage before a reader moves on from it, set `validate` to a\nfunction that answers whether the stage is valid, as Ark UI's `isStepValid`\ndoes. Moving forward asks it about the stage the reader is on, and where it\nanswers `false` the reader stays and the stage is `:state(invalid)` until it\nanswers `true`. An `optional` stage is not asked. `helix-before-activate`\nis still sent for every move, for a check of your own.\n\n`progress` is how far through the sequence is, from 0 to 100: the share of\nits stages that are `complete`. The `progress` part carries it as a\n`progressbar` and as `--steps-progress`, and is hidden until a stylesheet\ngives it a `display`, so a bar of your own costs nothing until you draw it.\nA screen reader hears it as `progress-label` says, \"33% complete\" unless\nset, as Ark UI's steps write it. The sequence is `:state(complete)` once\nevery stage is.\n\nThe sequence is a list named by `label`, and each step a list item. The\ncurrent stage's button says `aria-current=\"step\"`, and a finished stage's\nbutton is described by its `complete-label`.\n\n**Keyboard.** Each stage's name is a button and its own tab stop, and Enter\nor Space goes to the stage. Set `arrows` and the names are one tab stop, the\ncurrent stage's, and the arrow keys move focus between them: Left and Right\nalong a row, Up and Down a column, Home and End to the first and the last.\nThe stop follows focus, so Tab leaves the sequence and Shift+Tab comes back\nto the name the reader left. The arrows pass over a disabled stage and one a\nlinear sequence holds out of reach, and they move focus without going to the\nstage.",
152526
153276
  "name": "HelixHeadlessSteps",
152527
153277
  "cssProperties": [
152528
153278
  {
@@ -152560,7 +153310,7 @@
152560
153310
  "text": "boolean"
152561
153311
  },
152562
153312
  "default": "false",
152563
- "description": "Whether the arrow keys move focus between the stages' names, along the\nway the steps run. Off by default, so the arrows keep scrolling the page.",
153313
+ "description": "Whether the arrow keys move focus between the stages' names, along the\nway the steps run, and the names are one tab stop. Off by default, so the\narrows keep scrolling the page.",
152564
153314
  "attribute": "arrows"
152565
153315
  },
152566
153316
  {
@@ -152654,6 +153404,13 @@
152654
153404
  "readonly": true,
152655
153405
  "default": "new KeyboardNavigationController(this, { items: () => (this.arrows ? this.steps.map(navigable) : []), origin: 'focus', reserved: () => this.orientation === 'vertical' ? ['ArrowLeft', 'ArrowRight'] : ['ArrowDown', 'ArrowUp'], selects: () => false, wrap: false, })"
152656
153406
  },
153407
+ {
153408
+ "kind": "field",
153409
+ "name": "commands",
153410
+ "privacy": "protected",
153411
+ "readonly": true,
153412
+ "default": "new CommandController(this, { commands: { '--next': () => this.next(), '--previous': () => this.previous(), '--reset': () => this.reset(), }, })"
153413
+ },
152657
153414
  {
152658
153415
  "kind": "field",
152659
153416
  "name": "steps",
@@ -152674,6 +153431,25 @@
152674
153431
  "description": "The index of the stage the reader is on, or `-1`.\n\nRead from `active` once anything has set it, because a move writes\n`active` at once and each step's `current` only on the next update: two\ncalls to `next()` in one task then move two stages.",
152675
153432
  "readonly": true
152676
153433
  },
153434
+ {
153435
+ "kind": "method",
153436
+ "name": "named",
153437
+ "privacy": "private",
153438
+ "return": {
153439
+ "type": {
153440
+ "text": "HelixHeadlessStep | undefined"
153441
+ }
153442
+ },
153443
+ "parameters": [
153444
+ {
153445
+ "name": "steps",
153446
+ "type": {
153447
+ "text": "HelixHeadlessStep[]"
153448
+ }
153449
+ }
153450
+ ],
153451
+ "description": "The step `active` names, by `value` or by place, or the first enabled one."
153452
+ },
152677
153453
  {
152678
153454
  "kind": "method",
152679
153455
  "name": "requestActive",
@@ -152839,6 +153615,32 @@
152839
153615
  "privacy": "private",
152840
153616
  "readonly": true
152841
153617
  },
153618
+ {
153619
+ "kind": "field",
153620
+ "name": "holder",
153621
+ "type": {
153622
+ "text": "HelixHeadlessStep | undefined"
153623
+ },
153624
+ "privacy": "private",
153625
+ "description": "The step whose name holds the sequence's one tab stop under `arrows`, once focus has chosen it."
153626
+ },
153627
+ {
153628
+ "kind": "method",
153629
+ "name": "rove",
153630
+ "privacy": "private",
153631
+ "return": {
153632
+ "type": {
153633
+ "text": "void"
153634
+ }
153635
+ },
153636
+ "description": "Gives the one tab stop to a name, under `arrows`: the one focus last left,\nelse the current stage's, else the first a reader can go to. Without\n`arrows` every name is its own stop. Client only."
153637
+ },
153638
+ {
153639
+ "kind": "field",
153640
+ "name": "handleFocus",
153641
+ "privacy": "private",
153642
+ "readonly": true
153643
+ },
152842
153644
  {
152843
153645
  "kind": "field",
152844
153646
  "name": "handleSlotChange",
@@ -153321,7 +154123,7 @@
153321
154123
  "text": "boolean"
153322
154124
  },
153323
154125
  "default": "false",
153324
- "description": "Whether the arrow keys move focus between the stages' names, along the\nway the steps run. Off by default, so the arrows keep scrolling the page.",
154126
+ "description": "Whether the arrow keys move focus between the stages' names, along the\nway the steps run, and the names are one tab stop. Off by default, so the\narrows keep scrolling the page.",
153325
154127
  "fieldName": "arrows"
153326
154128
  },
153327
154129
  {
@@ -153636,7 +154438,7 @@
153636
154438
  "text": "string"
153637
154439
  },
153638
154440
  "default": "''",
153639
- "description": "The value the step sends in `helix-select`.",
154441
+ "description": "The value the step sends in `helix-select`, and what names the step in its\nsequence's `active`. Without one, the sequence names the step by its place,\ncounted from 1.",
153640
154442
  "attribute": "value"
153641
154443
  },
153642
154444
  {
@@ -154132,7 +154934,7 @@
154132
154934
  "text": "string"
154133
154935
  },
154134
154936
  "default": "''",
154135
- "description": "The value the step sends in `helix-select`.",
154937
+ "description": "The value the step sends in `helix-select`, and what names the step in its\nsequence's `active`. Without one, the sequence names the step by its place,\ncounted from 1.",
154136
154938
  "fieldName": "value"
154137
154939
  }
154138
154940
  ],
@@ -165656,6 +166458,61 @@
165656
166458
  "text": "setSelectionRange(start: number, end: number, direction?: SelectionDirection) => void"
165657
166459
  }
165658
166460
  },
166461
+ {
166462
+ "kind": "method",
166463
+ "name": "setRangeText",
166464
+ "return": {
166465
+ "type": {
166466
+ "text": "void"
166467
+ }
166468
+ },
166469
+ "parameters": [
166470
+ {
166471
+ "name": "replacement",
166472
+ "type": {
166473
+ "text": "string"
166474
+ }
166475
+ },
166476
+ {
166477
+ "name": "start",
166478
+ "optional": true,
166479
+ "type": {
166480
+ "text": "number"
166481
+ }
166482
+ },
166483
+ {
166484
+ "name": "end",
166485
+ "optional": true,
166486
+ "type": {
166487
+ "text": "number"
166488
+ }
166489
+ },
166490
+ {
166491
+ "name": "selectMode",
166492
+ "optional": true,
166493
+ "type": {
166494
+ "text": "SelectionMode"
166495
+ }
166496
+ }
166497
+ ],
166498
+ "description": "Replaces a range of the text, as the native method does: the selection\nwhere `start` and `end` are left out, and `selectMode` says what is\nselected after. It sends no event, as the native method sends none.",
166499
+ "type": {
166500
+ "text": "setRangeText(replacement: string, start?: number, end?: number, selectMode?: SelectionMode) => void"
166501
+ }
166502
+ },
166503
+ {
166504
+ "kind": "method",
166505
+ "name": "select",
166506
+ "return": {
166507
+ "type": {
166508
+ "text": "void"
166509
+ }
166510
+ },
166511
+ "description": "Selects everything in the field.",
166512
+ "type": {
166513
+ "text": "select() => void"
166514
+ }
166515
+ },
165659
166516
  {
165660
166517
  "kind": "field",
165661
166518
  "name": "textLength",
@@ -172877,7 +173734,7 @@
172877
173734
  "text": "TemplateResult | typeof nothing"
172878
173735
  }
172879
173736
  },
172880
- "description": "The `feedback` line, or nothing while there is none.",
173737
+ "description": "The `feedback` line, or nothing while there is none.\n\n`role=\"alert\"`, as a field's feedback line has, so the message is\nannounced when it appears and the label and hint are not read with it.",
172881
173738
  "inheritedFrom": {
172882
173739
  "name": "MixinPickerFeedback",
172883
173740
  "module": "src/mixins/mixin-picker-feedback/mixin-picker-feedback.ts"
@@ -189733,7 +190590,7 @@
189733
190590
  "declarations": [
189734
190591
  {
189735
190592
  "kind": "class",
189736
- "description": "**You slot the player**, so it keeps your appearance, extra controls,\ncaptions, and poster treatment. `Sandbox` takes its preview and `Menu` its\ntrigger the same way.\n\n**Entries are markup.** React skips properties it would set imperatively\nduring hydration, so an array property on a custom element is missing on the\nfirst paint of a server-rendered page. `using-the-elements.mdx` has the\nmeasurement. Markup also makes each entry a real button with its name inside.\n\n**The playlist does not play media.** It sets `src` on the player and tracks\nwhich entry is current. The current entry moves when:\n\n- an entry is pressed,\n- the player dispatches `helix-next` or `helix-previous`, or\n- the player reaches the end.\n\nOn `ended` the playlist advances and the next clip starts playing, because\nthe user already pressed play once. `no-advance` turns that off, and the\nplaylist stays on the entry that ended. `repeat` decides what happens at the\nend: `one` plays the same entry again, and `all` goes from the last entry\nback to the first. Without it, the playlist stops on the last entry and\nsends `helix-end`. `shuffle` plays the entries in a random order, starting\nfrom the one that is current, and `previous` retraces that order.\n\n**`helix-before-activate` says why the playlist is moving**, in\n`detail.cause`: `press` for an entry or a menu choice, `next` and\n`previous` for the player's controls and the methods, `ended` at the end of\na clip, and `removed` when the current entry leaves the playlist. Cancel an\n`ended` move to show your own \"Up next\" countdown, then call `next()`.\n\n**Removing the current entry moves the playlist on**, to the entry after it,\nor to the one before it when it was the last. A clip that was playing keeps\nplaying in the entry that takes its place.\n\n**The player is named by the current entry**, and the device's own media\ncontrols show the entry as the title. A player you gave a `label` keeps\nyour name, and only the title follows the entry.\n\n**Keyboard.** The entries take one stop in the tab order: the entry you\nlast focused, or the current entry before you have focused one. Enter or\nSpace plays it. The arrow keys move focus to the entry before or after, and\nHome and End to the first and the last, passing over disabled entries. The\ncurrent entry carries `aria-current`. While focus is on the player, the\nplayer's own keys work, and `Video` lists them, and Shift with N or P plays\nthe next or the previous entry. The player's previous and next controls\nmove through the entries in the same order the playlist does.\n\n**Moving entries.** `reorderable` lets a reader change the order of the\nqueue:\n\n- Alt with Up or Down moves the focused entry a place, and Alt with Home or\n End moves it to either end, as the APG's rearrangeable listbox does.\n Focus stays on it.\n- A pointer drags an entry, with a drop indicator, and the page scrolls near\n an edge. A press that does not travel still plays the entry.\n- A drop sends `helix-before-reorder`, which you can refuse, and then\n `helix-reorder`, whose `keys` hold every entry's `value` in the new order.\n **You move the entries**: the playlist moves no element of yours, and the\n next entry is the one after the current entry in the order you leave.\n- Each move is said in the live region, in words you can change:\n `moved-label`, `dropped-label`, `canceled-label`, and `picked-up-label`.\n\n**What a screen reader hears.** A list named by `label`, with a button for\neach entry. When the playlist moves to another entry, by a press, by the\nplayer's controls, or at the end of a clip, a live region says\n`playing-label`, \"Now playing\" and the entry's name.",
190593
+ "description": "**You slot the player**, so it keeps your appearance, extra controls,\ncaptions, and poster treatment. `Sandbox` takes its preview and `Menu` its\ntrigger the same way.\n\n**The player is a `Video` or a plain `<video>`.** Either takes `src` and\n`poster` from the entry, is named by it, plays on, and answers Shift with N\nor P while focused. A plain `<video>` has no previous and next controls\nthat send `helix-previous` or `helix-next`, so its moves are the entries,\nthe menu, and your own buttons calling `previous()` and `next()`.\n\n**Entries are markup.** React skips properties it would set imperatively\nduring hydration, so an array property on a custom element is missing on the\nfirst paint of a server-rendered page. `using-the-elements.mdx` has the\nmeasurement. Markup also makes each entry a real button with its name inside.\n\n**The playlist does not play media.** It sets `src` on the player and tracks\nwhich entry is current. The current entry moves when:\n\n- an entry is pressed,\n- the player dispatches `helix-next` or `helix-previous`, or\n- the player reaches the end.\n\nOn `ended` the playlist advances and the next clip starts playing, because\nthe user already pressed play once. `no-advance` turns that off, and the\nplaylist stays on the entry that ended. `repeat` decides what happens at the\nend: `one` plays the same entry again, and `all` goes from the last entry\nback to the first. Without it, the playlist stops on the last entry and\nsends `helix-end`. `shuffle` plays the entries in a random order, starting\nfrom the one that is current, and `previous` retraces that order.\n\n**`helix-before-activate` says why the playlist is moving**, in\n`detail.cause`: `press` for an entry or a menu choice, `next` and\n`previous` for the player's controls and the methods, `ended` at the end of\na clip, and `removed` when the current entry leaves the playlist. Cancel an\n`ended` move to show your own \"Up next\" countdown, then call `next()`.\n\n**Removing the current entry moves the playlist on**, to the entry after it,\nor to the one before it when it was the last. A clip that was playing keeps\nplaying in the entry that takes its place.\n\n**The player is named by the current entry**, and the device's own media\ncontrols show the entry as the title. A player you gave a `label` keeps\nyour name, and only the title follows the entry. A plain `<video>` is\nnamed through `aria-label`, unless you wrote one.\n\n**Keyboard.** The entries take one stop in the tab order: the entry you\nlast focused, or the current entry before you have focused one. Enter or\nSpace plays it. The arrow keys move focus to the entry before or after, and\nHome and End to the first and the last, passing over disabled entries. The\ncurrent entry carries `aria-current`. While focus is on the player, the\nplayer's own keys work, and `Video` lists them, and Shift with N or P plays\nthe next or the previous entry. The player's previous and next controls\nmove through the entries in the same order the playlist does.\n\n**Moving entries.** `reorderable` lets a reader change the order of the\nqueue:\n\n- Alt with Up or Down moves the focused entry a place, and Alt with Home or\n End moves it to either end, as the APG's rearrangeable listbox does.\n Focus stays on it.\n- A pointer drags an entry, with a drop indicator, and the page scrolls near\n an edge. A press that does not travel still plays the entry.\n- A drop sends `helix-before-reorder`, which you can refuse, and then\n `helix-reorder`, whose `keys` hold every entry's `value` in the new order.\n **You move the entries**: the playlist moves no element of yours, and the\n next entry is the one after the current entry in the order you leave.\n- Each move is said in the live region, in words you can change:\n `moved-label`, `dropped-label`, `canceled-label`, and `picked-up-label`.\n\n**What a screen reader hears.** A list named by `label`, with a button for\neach entry. When the playlist moves to another entry, by a press, by the\nplayer's controls, or at the end of a clip, a live region says\n`playing-label`, \"Now playing\" and the entry's name.",
189737
190594
  "name": "HelixHeadlessVideoPlaylist",
189738
190595
  "cssParts": [
189739
190596
  {
@@ -190022,6 +190879,40 @@
190022
190879
  "privacy": "private",
190023
190880
  "description": "The entry that holds the list's tab stop: the one a reader last focused.\nBefore they focus one, the current entry holds it."
190024
190881
  },
190882
+ {
190883
+ "kind": "field",
190884
+ "name": "wroteName",
190885
+ "type": {
190886
+ "text": "string | undefined"
190887
+ },
190888
+ "privacy": "private",
190889
+ "description": "The name this playlist last wrote on a plain media player, to tell it from yours."
190890
+ },
190891
+ {
190892
+ "kind": "method",
190893
+ "name": "name",
190894
+ "privacy": "private",
190895
+ "return": {
190896
+ "type": {
190897
+ "text": "void"
190898
+ }
190899
+ },
190900
+ "parameters": [
190901
+ {
190902
+ "name": "player",
190903
+ "type": {
190904
+ "text": "Player"
190905
+ }
190906
+ },
190907
+ {
190908
+ "name": "text",
190909
+ "type": {
190910
+ "text": "string"
190911
+ }
190912
+ }
190913
+ ],
190914
+ "description": "Names the player by the entry.\n\nThe consumer's own name for the player stands. Only a player named by\nnobody takes the entry's words as its name: `label` on a player that has\none, and `aria-label` on a plain `<video>` or `<audio>`, which has none and\nwas heard as an unnamed video."
190915
+ },
190025
190916
  {
190026
190917
  "kind": "field",
190027
190918
  "name": "pointed",
@@ -194511,7 +195402,7 @@
194511
195402
  "declarations": [
194512
195403
  {
194513
195404
  "kind": "class",
194514
- "description": "The items arrive as data, in one of two ways:\n\n- `items` takes an array. A framework sets it as a property, and React\n hydration keeps it.\n- `for` names a `<script type=\"application/json\">` that holds an array, which\n a server sends as markup.\n\nEach item is drawn into a row in one of two ways:\n\n- `renderItem` is a function from an item and its index to the row's\n content: a Lit template, a string, or a DOM node.\n- A `<template>` child defines one row. `data-field=\"name\"` fills an element\n with the item's `name` as text, and `data-field-href=\"url\"` writes the\n item's `url` into `href`, the same for any attribute. A field may be a path\n of keys, such as `author.name`.\n\nRows are your markup in the light DOM, so your stylesheet styles them.\n\n**A grid.** `columns` lays the items out that many to a line, as a grid of\nequal columns, and windows the set a line at a time, as React Aria's grid\nlayout does. Each item draws one element, a cell of the grid, and\n`estimate` is the height of a line. `min-column-width` lays out as many\ncolumns as fit the box instead, as React Aria's grid layout does with its\nsmallest item size, and follows the box as it resizes. `gap` spaces the rows, and the columns\nof a grid, by a number of pixels the list counts in each row's place.\n\n**Across.** `orientation=\"horizontal\"` lays the items out along a line and\nscrolls it sideways, as a strip of cards does, windowed the same way:\n`estimate` is then an item's width, and the box needs an inline size.\n\n**Sections.** `section-field` names the field of each item that says which\nsection it is in, and the list draws a header before each section's items,\nas React Aria's list box sections do. Each section is an element with the\n`group` role, named by its header, which is its first child and carries\n`data-header`, so your stylesheet styles it, such as with `position:\nsticky`. A section starts wherever the field changes, so give the items in\norder. The window counts the headers with the items, and a section the\nwindow starts inside is drawn with its header. `renderSection` draws a\nheader of your own.\n\n**Keyboard.** The list is one tab stop.\n\n| Key | What it does |\n|---|---|\n| Down, Up | Moves to the next or the previous item, or in a grid to the item in the same column on the next or the previous line. |\n| Right, Left | In a grid, moves to the next or the previous item along the line, the other way round on a right-to-left page. In a list that runs across, moves to the next or the previous item the same way, and Down and Up are left to the row. |\n| Home, End | Moves to the first or the last item. |\n| Page Down, Page Up | Moves by the number of items, or of lines in a grid, on screen. |\n| A letter or a word | Moves to the next item whose text starts with what you typed. |\n| Escape | Lets go of every chosen item, in a list with `selection-mode`. |\n| Tab | Moves into the focused row's own links and buttons, then out of the list. |\n\nAn item that is not drawn is scrolled into view and focused, so a keyboard\nreaches the whole set. When the row holding the tab stop scrolls away, the\nstop moves to the first row on screen. Typing matches each item's\n`text-field`, which is the first `data-field` of the row template by default,\nor the item itself when it is a string or a number.\n\nEach row says its place in the whole set with `aria-posinset` and\n`aria-setsize`, so a screen reader says \"4,000 of 10,000\". The row holding\nfocus stays drawn when it scrolls out.\n\n**Selecting.** `selection-mode` makes the list a list box a reader chooses\nfrom: `single` selects one item and `multiple` any number. Space or a press\nselects an item, Shift with Space, an arrow, Home, End or a press takes in\nthe items from the last one chosen, and Control or Command with A selects\nthem all, in `multiple`. Escape lets go of every chosen item, and keeps the\nkey from the dialog or menu around the list while there was something to\nlet go of. `value` holds the chosen items' keys, separated by commas, in\nthe order of the set: `key-field` names each item's key, and a string or a\nnumber item is its own key. A change asks with `helix-before-change` and\ntells with `change`. Each row is an `option` marked `aria-selected`, so a\nrow's own links and buttons are not reached there: keep those to a list\nwithout `selection-mode`.\n\n`selection-behavior=\"replace\"` selects the item pressed alone, as a file\nlist does, and the arrow keys carry the selection with them. Control or\nCommand with a press or with Space adds or takes out one item, and with an\narrow moves without selecting.\n\n**Disabled items.** `disabled-keys` names items by their keys, separated by\ncommas. Their rows carry `aria-disabled`, the keys and typing pass over\nthem, and a press, Space, or Control with A does not select them.\n\n**Loading more.** `helix-range-change` fires as the items on screen change,\nand `range` holds them. `helix-load-more` fires once a reader is within one\nscreen of the end, once for each length of the set: add to `items` and it can\nfire again. While you fetch, set `loading`: the list is busy, and a row after\nthe last item says \"Loading more\" (`loading-label`, or your own content in\nthe `loading` slot). `scrollToIndex` takes a reader to an item.\n\n**An empty list.** Set `empty-label`, or put your own content in the `empty`\nslot, for a row shown when there are no items and none are loading, such as\n\"No messages yet\" and a link to write one. Without either, an empty list\nshows nothing, since a list whose items have not arrived yet has not said\nit has none.\n\nThe list takes the `empty` state while it has read its items and holds none,\nand the `loading` state while `loading` is set, so your stylesheet can\nchange the box around it for either. A list with no name is reported once\nin a development build. Name it with `label` or `aria-label`.\n\n**Row height.** Rows are placed by `estimate` until they are drawn. Then the\nlist reads the drawn rows' height once and places every row by it, so a\nwrong estimate still reaches the last item. Set `measured` for rows of\ndifferent heights. Once the list draws rows of different heights, such as\nrows that wrap on a narrow screen, it places each row by its own height\nwhether `measured` is set or not.\n\nThe list follows its source. A new `items` array, an edit to the script's\ntext, or a script replaced under the same id draws the new rows. A row that\nstays in view keeps its element as the list scrolls.\n\nThe viewport scrolls on this tier. It carries `data-scroller`, and `core`\nships `[data-scroller] { overflow: auto }` into every shadow root, so\n`scrollTop` works with no stylesheet. You still need to set a block size.",
195405
+ "description": "The items arrive as data, in one of two ways:\n\n- `items` takes an array. A framework sets it as a property, and React\n hydration keeps it.\n- `for` names a `<script type=\"application/json\">` that holds an array, which\n a server sends as markup.\n\nEach item is drawn into a row in one of two ways:\n\n- `renderItem` is a function from an item and its index to the row's\n content: a Lit template, a string, or a DOM node.\n- A `<template>` child defines one row. `data-field=\"name\"` fills an element\n with the item's `name` as text, and `data-field-href=\"url\"` writes the\n item's `url` into `href`, the same for any attribute. A field may be a path\n of keys, such as `author.name`.\n\nRows are your markup in the light DOM, so your stylesheet styles them.\n\n**A grid.** `columns` lays the items out that many to a line, as a grid of\nequal columns, and windows the set a line at a time, as React Aria's grid\nlayout does. Each item draws one element, a cell of the grid, and\n`estimate` is the height of a line. `min-column-width` lays out as many\ncolumns as fit the box instead, as React Aria's grid layout does with its\nsmallest item size, and follows the box as it resizes. `gap` spaces the rows, and the columns\nof a grid, by a number of pixels the list counts in each row's place.\n\n**Across.** `orientation=\"horizontal\"` lays the items out along a line and\nscrolls it sideways, as a strip of cards does, windowed the same way:\n`estimate` is then an item's width, and the box needs an inline size.\n\n**Sections.** `section-field` names the field of each item that says which\nsection it is in, and the list draws a header before each section's items,\nas React Aria's list box sections do. Each section is an element with the\n`group` role, named by its header, which is its first child and carries\n`data-header`, so your stylesheet styles it, such as with `position:\nsticky`. A section starts wherever the field changes, so give the items in\norder. The window counts the headers with the items, and a section the\nwindow starts inside is drawn with its header. `renderSection` draws a\nheader of your own.\n\n**Keyboard.** The list is one tab stop.\n\n| Key | What it does |\n|---|---|\n| Down, Up | Moves to the next or the previous item, or in a grid to the item in the same column on the next or the previous line. |\n| Right, Left | In a grid, moves to the next or the previous item along the line, the other way round on a right-to-left page. In a list that runs across, moves to the next or the previous item the same way, and Down and Up are left to the row. |\n| Home, End | Moves to the first or the last item. |\n| Page Down, Page Up | Moves by the number of items, or of lines in a grid, on screen. |\n| A letter or a word | Moves to the next item whose text starts with what you typed. |\n| Escape | Lets go of every chosen item, in a list with `selection-mode`. |\n| Tab | Moves into the focused row's own links and buttons, then out of the list. |\n\nAn item that is not drawn is scrolled into view and focused, so a keyboard\nreaches the whole set. When the row holding the tab stop scrolls away, the\nstop moves to the first row on screen. Typing matches each item's\n`text-field`, which is the first `data-field` of the row template by default,\nor the item itself when it is a string or a number.\n\nEach row says its place in the whole set with `aria-posinset` and\n`aria-setsize`, so a screen reader says \"4,000 of 10,000\". The row holding\nfocus stays drawn when it scrolls out.\n\n**Selecting.** `selection-mode` makes the list a list box a reader chooses\nfrom: `single` selects one item and `multiple` any number. Space or a press\nselects an item, Shift with Space, an arrow, Home, End or a press takes in\nthe items from the last one chosen, and Control or Command with A selects\nthem all, in `multiple`. Escape lets go of every chosen item, and keeps the\nkey from the dialog or menu around the list while there was something to\nlet go of. `value` holds the chosen items' keys, separated by commas, in\nthe order of the set: `key-field` names each item's key, and a string or a\nnumber item is its own key. A change asks with `helix-before-change` and\ntells with `change`. Each row is an `option` marked `aria-selected`, so a\nrow's own links and buttons are not reached there: keep those to a list\nwithout `selection-mode`.\n\n`selection-behavior=\"replace\"` selects the item pressed alone, as a file\nlist does, and the arrow keys carry the selection with them. Control or\nCommand with a press or with Space adds or takes out one item, and with an\narrow moves without selecting.\n\n**Disabled items.** `disabled-keys` names items by their keys, separated by\ncommas. Their rows carry `aria-disabled`, the keys and typing pass over\nthem, and a press, Space, or Control with A does not select them.\n\n**Rows a stylesheet can tell apart.** A chosen row carries `data-selected`,\na disabled one `data-disabled`, as React Aria's rows do, and the row the\nkeys are on, which holds the tab stop, `data-active`, whether or not the\nlist has focus.\n\n**Loading more.** `helix-range-change` fires as the items on screen change,\nand `range` holds them. `helix-load-more` fires once a reader is within one\nscreen of the end, once for each length of the set: add to `items` and it can\nfire again. While you fetch, set `loading`: the list is busy, and a row after\nthe last item says \"Loading more\" (`loading-label`, or your own content in\nthe `loading` slot). `scrollToIndex` takes a reader to an item.\n\n**An empty list.** Set `empty-label`, or put your own content in the `empty`\nslot, for a row shown when there are no items and none are loading, such as\n\"No messages yet\" and a link to write one. Without either, an empty list\nshows nothing, since a list whose items have not arrived yet has not said\nit has none.\n\nThe list takes the `empty` state while it has read its items and holds none,\nand the `loading` state while `loading` is set, so your stylesheet can\nchange the box around it for either. A list with no name is reported once\nin a development build. Name it with `label` or `aria-label`.\n\n**Row height.** Rows are placed by `estimate` until they are drawn. Then the\nlist reads the drawn rows' height once and places every row by it, so a\nwrong estimate still reaches the last item. Set `measured` for rows of\ndifferent heights. Once the list draws rows of different heights, such as\nrows that wrap on a narrow screen, it places each row by its own height\nwhether `measured` is set or not.\n\nThe list follows its source. A new `items` array, an edit to the script's\ntext, or a script replaced under the same id draws the new rows. A row that\nstays in view keeps its element as the list scrolls.\n\nThe viewport scrolls on this tier. It carries `data-scroller`, and `core`\nships `[data-scroller] { overflow: auto }` into every shadow root, so\n`scrollTop` works with no stylesheet. You still need to set a block size.",
194515
195406
  "name": "HelixHeadlessVirtualList",
194516
195407
  "cssParts": [
194517
195408
  {
@@ -195490,7 +196381,7 @@
195490
196381
  }
195491
196382
  }
195492
196383
  ],
195493
- "description": "Writes a row's role, and whether it is chosen in a list that selects:\n`option` with `aria-selected` there, and `listitem` otherwise, and\n`aria-disabled` on an item `disabled-keys` names. Every pass,\nsince a choice changes no row's content."
196384
+ "description": "Writes a row's role, and whether it is chosen in a list that selects:\n`option` with `aria-selected` there, and `listitem` otherwise, and\n`aria-disabled` on an item `disabled-keys` names. Every pass,\nsince a choice changes no row's content.\n\n`data-selected` and `data-disabled` say the same to a stylesheet, as\nReact Aria's rows do, so one rule reads the same in either kind of list."
195494
196385
  },
195495
196386
  {
195496
196387
  "kind": "method",
@@ -196433,6 +197324,6 @@
196433
197324
  ],
196434
197325
  "helix": {
196435
197326
  "package": "@fusion.dev/helix-headless",
196436
- "version": "0.170.0"
197327
+ "version": "0.172.0"
196437
197328
  }
196438
197329
  }