@fusion.dev/helix-headless 0.177.2 → 0.178.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.
@@ -69668,6 +69668,30 @@
69668
69668
  "name": "*",
69669
69669
  "module": "src/filter-input/root/filter-input-root.js"
69670
69670
  }
69671
+ },
69672
+ {
69673
+ "kind": "js",
69674
+ "name": "FilterSource",
69675
+ "declaration": {
69676
+ "name": "FilterSource",
69677
+ "module": "./suggest.js"
69678
+ }
69679
+ },
69680
+ {
69681
+ "kind": "js",
69682
+ "name": "FilterSourceRequest",
69683
+ "declaration": {
69684
+ "name": "FilterSourceRequest",
69685
+ "module": "./suggest.js"
69686
+ }
69687
+ },
69688
+ {
69689
+ "kind": "js",
69690
+ "name": "FilterSuggestion",
69691
+ "declaration": {
69692
+ "name": "FilterSuggestion",
69693
+ "module": "./suggest.js"
69694
+ }
69671
69695
  }
69672
69696
  ]
69673
69697
  },
@@ -70860,7 +70884,7 @@
70860
70884
  "declarations": [
70861
70885
  {
70862
70886
  "kind": "class",
70863
- "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. With the panel closed, empties the text. In an empty field, does nothing, so the page hears it. |\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, and a paste is read the same way as soon as it lands.\n`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**The picker has an entry of its own.** `FilterMenu` draws its pages with\n`Menu`, which a field without a picker does not need, so this family's entry\nleaves it out. A page that writes one imports\n`@fusion.dev/helix/filter-input/menu` as well, and a picker written without it\nis reported once the page has loaded, with the entry to add.\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.",
70887
+ "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. With the panel closed, empties the text. In an empty field, does nothing, so the page hears it. |\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, and a paste is read the same way as soon as it lands.\n`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**The picker has an entry of its own.** `FilterMenu` draws its pages with\n`Menu`, which a field without a picker does not need, so this family's entry\nleaves it out. A page that writes a `FilterMenu` imports both entries:\n\n```js\nimport '@fusion.dev/helix/filter-input'\nimport '@fusion.dev/helix/filter-input/menu'\n```\n\nA picker written without the second is reported once the page has loaded,\nwith the entry to add.\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**Suggestions from a source.** Set `source` to a function, and the field asks\nit for suggestions as the text changes and draws them as options in the\npanel. It is given the term at the caret, the field the term is a value of,\nthe whole text, and an `AbortSignal`, and returns the suggestions or a\npromise of them. `filter-delay` waits for typing to rest before asking, and\na later keystroke aborts a request still out. While a promise is pending, the\npanel shows `loading-text` and a screen reader hears it, then the count.\nChoosing a suggestion writes its value over the term at the caret, and a\nfinished `field:value` is read into a chip as Enter would. Cancel its\n`helix-select` to write it yourself.\n\n```js\nfilter.source = async ({ field, term, signal }) => {\n const response = await fetch(`/values?field=${field}&q=${term}`, { signal })\n return response.json()\n}\n```\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.",
70864
70888
  "name": "HelixHeadlessFilterInput",
70865
70889
  "cssParts": [
70866
70890
  {
@@ -70900,7 +70924,7 @@
70900
70924
  "name": "live"
70901
70925
  },
70902
70926
  {
70903
- "description": "Shown in the panel while the suggestions load.",
70927
+ "description": "Shown in the panel while the suggestions load: while `loading` is set, or a request to `source` is out.",
70904
70928
  "name": "loading"
70905
70929
  },
70906
70930
  {
@@ -71028,6 +71052,16 @@
71028
71052
  "words": true
71029
71053
  }
71030
71054
  },
71055
+ {
71056
+ "kind": "field",
71057
+ "name": "filterDelay",
71058
+ "type": {
71059
+ "text": "number"
71060
+ },
71061
+ "default": "0",
71062
+ "description": "How long typing rests before `source` is asked, in milliseconds. A delay\nsends one request for a word in place of one for each letter. The arrow\nkeys and Enter ask at once. `0` asks on every keystroke.",
71063
+ "attribute": "filter-delay"
71064
+ },
71031
71065
  {
71032
71066
  "kind": "field",
71033
71067
  "name": "filterSuggestions",
@@ -71082,6 +71116,14 @@
71082
71116
  "description": "The text shown while the field is empty.\n\nNot a label: it goes when typing starts.",
71083
71117
  "attribute": "placeholder"
71084
71118
  },
71119
+ {
71120
+ "kind": "field",
71121
+ "name": "source",
71122
+ "type": {
71123
+ "text": "FilterSource | undefined | undefined"
71124
+ },
71125
+ "description": "Where the suggestions come from: a function given the term at the caret,\nthe field it is a value of, the whole text, and an `AbortSignal`, which\nreturns the suggestions or a promise of them. Each is a string, or a\n`value` with a `label` and `disabled`. The field draws them as options in\nthe panel, beside any you slot. A property only, there being no way to\nwrite a function in markup. See the class description."
71126
+ },
71085
71127
  {
71086
71128
  "kind": "field",
71087
71129
  "name": "suggestionLabel",
@@ -71481,6 +71523,130 @@
71481
71523
  },
71482
71524
  "description": "Opens the panel when there is something in it, and closes it when there is\nnot.\n\n**Derived rather than commanded, which is the whole of how two levels\nwork.** A reader who picks `disposition` wants its values next, and the\nconsumer answers by rendering those values; if picking closed the panel it\nwould shut and reopen between two renders, which a reader sees as a flicker\nand a screen reader hears as a list going away. Nothing here closes on a\nchoice: the panel goes when the consumer stops offering, which is exactly\nwhen a value has been completed and there is nothing left to say.\n\nThrough `requestOpen` for both directions, so `helix-before-open` and\n`helix-before-close` still gate it and a consumer who refuses is obeyed.\n**No guard of its own**, because that method already returns early when the\nanswer has not moved: this runs after every render, and a second guard here\nwould only be a copy of that one going stale."
71483
71525
  },
71526
+ {
71527
+ "kind": "field",
71528
+ "name": "fetching",
71529
+ "type": {
71530
+ "text": "boolean"
71531
+ },
71532
+ "privacy": "private",
71533
+ "default": "false",
71534
+ "description": "Whether a request to `source` is out, which the panel shows as loading."
71535
+ },
71536
+ {
71537
+ "kind": "field",
71538
+ "name": "busy",
71539
+ "type": {
71540
+ "text": "boolean"
71541
+ },
71542
+ "privacy": "private",
71543
+ "description": "Whether the suggestions are loading: `loading`, or a request to `source` out.",
71544
+ "readonly": true
71545
+ },
71546
+ {
71547
+ "kind": "field",
71548
+ "name": "request",
71549
+ "type": {
71550
+ "text": "AbortController | undefined"
71551
+ },
71552
+ "privacy": "private",
71553
+ "description": "The request to `source` still out, which a later one aborts."
71554
+ },
71555
+ {
71556
+ "kind": "field",
71557
+ "name": "asked",
71558
+ "type": {
71559
+ "text": "string | undefined"
71560
+ },
71561
+ "privacy": "private",
71562
+ "description": "The text `source` was last asked about, so focus alone does not ask again."
71563
+ },
71564
+ {
71565
+ "kind": "field",
71566
+ "name": "used",
71567
+ "type": {
71568
+ "text": "FilterSource | undefined"
71569
+ },
71570
+ "privacy": "private",
71571
+ "description": "The source the suggestions drawn now came from."
71572
+ },
71573
+ {
71574
+ "kind": "field",
71575
+ "name": "sourced",
71576
+ "type": {
71577
+ "text": "SuggestionOption[]"
71578
+ },
71579
+ "privacy": "private",
71580
+ "default": "[]",
71581
+ "description": "The options this element drew from what `source` gave, which the next answer replaces."
71582
+ },
71583
+ {
71584
+ "kind": "field",
71585
+ "name": "asking",
71586
+ "privacy": "private",
71587
+ "readonly": true,
71588
+ "default": "new DelayController(this, { renders: false })",
71589
+ "description": "The request waiting out `filterDelay`, if any."
71590
+ },
71591
+ {
71592
+ "kind": "method",
71593
+ "name": "resetSource",
71594
+ "privacy": "private",
71595
+ "return": {
71596
+ "type": {
71597
+ "text": "void"
71598
+ }
71599
+ },
71600
+ "description": "A new source, or none: what the old one gave goes, and the new one is\nasked where the reader is in the field."
71601
+ },
71602
+ {
71603
+ "kind": "method",
71604
+ "name": "askSoon",
71605
+ "privacy": "private",
71606
+ "return": {
71607
+ "type": {
71608
+ "text": "void"
71609
+ }
71610
+ },
71611
+ "description": "Asks `source` once typing rests for `filterDelay`, or at once without one."
71612
+ },
71613
+ {
71614
+ "kind": "method",
71615
+ "name": "ask",
71616
+ "privacy": "private",
71617
+ "return": {
71618
+ "type": {
71619
+ "text": "void"
71620
+ }
71621
+ },
71622
+ "description": "Asks `source` for suggestions for the term at the caret, and draws what\nit gives.\n\n**A later request aborts this one, and an answer that arrives late is\ndropped**, whether or not the source heeds the signal: the panel shows\nwhat the text says now."
71623
+ },
71624
+ {
71625
+ "kind": "method",
71626
+ "name": "drawSourced",
71627
+ "privacy": "private",
71628
+ "return": {
71629
+ "type": {
71630
+ "text": "void"
71631
+ }
71632
+ },
71633
+ "parameters": [
71634
+ {
71635
+ "name": "suggestions",
71636
+ "type": {
71637
+ "text": "readonly FilterSuggestion[]"
71638
+ }
71639
+ }
71640
+ ],
71641
+ "description": "Draws what `source` gave as options in the `suggestions` slot, in place of\nthe ones it gave before, and leaves the ones you slotted alone. The\noptions are this tier's `Option`, which a page on the headless tier\nimports; one not registered is reported."
71642
+ },
71643
+ {
71644
+ "kind": "field",
71645
+ "name": "handleSourcedSelect",
71646
+ "privacy": "private",
71647
+ "readonly": true,
71648
+ "description": "A suggestion `source` gave, chosen: its value written over the term at the\ncaret, and a finished `field:value` read into a chip, as Enter reads it.\n\n**After every listener has had its turn**, as a chip under `chips` is\ndrawn, so a `preventDefault()` on the `helix-select` is read and leaves the\ntext to you."
71649
+ },
71484
71650
  {
71485
71651
  "kind": "method",
71486
71652
  "name": "move",
@@ -73410,7 +73576,7 @@
73410
73576
  "type": {
73411
73577
  "text": "HelixSelectEvent"
73412
73578
  },
73413
- "description": "Fired by an option in `suggestions` when it is chosen, and bubbles out of the field. Cancelable: call `preventDefault()` to stop other listeners acting on it. The field takes no action on a choice; you decide what it does.",
73579
+ "description": "Fired by an option in `suggestions` when it is chosen, and bubbles out of the field. Cancelable: call `preventDefault()` to stop other listeners acting on it. The field takes no action on an option you slotted, and you decide what it does. A suggestion from `source` is written over the term at the caret unless you cancel it.",
73414
73580
  "name": "helix-select",
73415
73581
  "reactProp": "onhelix-select"
73416
73582
  },
@@ -73463,6 +73629,15 @@
73463
73629
  "description": "The name of the clear button, read by screen readers. The button shows only\nan icon.",
73464
73630
  "fieldName": "clearLabel"
73465
73631
  },
73632
+ {
73633
+ "name": "filter-delay",
73634
+ "type": {
73635
+ "text": "number"
73636
+ },
73637
+ "default": "0",
73638
+ "description": "How long typing rests before `source` is asked, in milliseconds. A delay\nsends one request for a word in place of one for each letter. The arrow\nkeys and Enter ask at once. `0` asks on every keystroke.",
73639
+ "fieldName": "filterDelay"
73640
+ },
73466
73641
  {
73467
73642
  "name": "filter-suggestions",
73468
73643
  "type": {
@@ -73750,7 +73925,7 @@
73750
73925
  "name": "invalid"
73751
73926
  },
73752
73927
  {
73753
- "description": "While `loading` is set.",
73928
+ "description": "While `loading` is set, or a request to `source` is out.",
73754
73929
  "name": "loading"
73755
73930
  },
73756
73931
  {
@@ -73868,6 +74043,61 @@
73868
74043
  ],
73869
74044
  "description": "The term being typed at the caret: the run of text since the last space,\nand after its colon where it has one, so `status:ope` narrows by `ope`."
73870
74045
  },
74046
+ {
74047
+ "kind": "function",
74048
+ "name": "currentField",
74049
+ "return": {
74050
+ "type": {
74051
+ "text": "string"
74052
+ }
74053
+ },
74054
+ "parameters": [
74055
+ {
74056
+ "name": "text",
74057
+ "type": {
74058
+ "text": "string"
74059
+ }
74060
+ },
74061
+ {
74062
+ "name": "caret",
74063
+ "default": "text.length",
74064
+ "type": {
74065
+ "text": "number"
74066
+ }
74067
+ }
74068
+ ],
74069
+ "description": "The field the term at the caret is a value of: the word before its colon,\nwithout a leading `-`, so `-status:ope` is a value of `status`. Empty where\nthe term has no colon, which is a key being typed."
74070
+ },
74071
+ {
74072
+ "kind": "function",
74073
+ "name": "completeTerm",
74074
+ "return": {
74075
+ "type": {
74076
+ "text": "{ caret: number; text: string }"
74077
+ }
74078
+ },
74079
+ "parameters": [
74080
+ {
74081
+ "name": "text",
74082
+ "type": {
74083
+ "text": "string"
74084
+ }
74085
+ },
74086
+ {
74087
+ "name": "caret",
74088
+ "type": {
74089
+ "text": "number"
74090
+ }
74091
+ },
74092
+ {
74093
+ "name": "value",
74094
+ "type": {
74095
+ "text": "string"
74096
+ }
74097
+ }
74098
+ ],
74099
+ "description": "The text with a suggestion written over the term at the caret, and where\nthe caret goes after it.\n\nThe word's field and its colon stay, and a leading `-`, so a value chosen\nfor `-status:op` gives `-status:open`. A space follows, ready for the next\nterm, unless the suggestion ends with a colon, which is a key whose value\ncomes next."
74100
+ },
73871
74101
  {
73872
74102
  "kind": "function",
73873
74103
  "name": "suggests",
@@ -73938,6 +74168,22 @@
73938
74168
  "module": "src/filter-input/suggest.ts"
73939
74169
  }
73940
74170
  },
74171
+ {
74172
+ "kind": "js",
74173
+ "name": "currentField",
74174
+ "declaration": {
74175
+ "name": "currentField",
74176
+ "module": "src/filter-input/suggest.ts"
74177
+ }
74178
+ },
74179
+ {
74180
+ "kind": "js",
74181
+ "name": "completeTerm",
74182
+ "declaration": {
74183
+ "name": "completeTerm",
74184
+ "module": "src/filter-input/suggest.ts"
74185
+ }
74186
+ },
73941
74187
  {
73942
74188
  "kind": "js",
73943
74189
  "name": "suggests",
@@ -75477,7 +75723,7 @@
75477
75723
  "declarations": [
75478
75724
  {
75479
75725
  "kind": "class",
75480
- "description": "Helix draws no graph. A force layout of thousands of notes is physics and\nWebGL, which a renderer such as Sigma.js does. Put the renderer's box in the\nframe, and `connectGraph` from `@fusion.dev/helix-graph` draws `nodes` and\n`links` in it and keeps it in step with the frame.\n\nWhat the frame adds is what a drawing of a graph cannot do: a list of every\nnote beside it, which is the graph for a screen reader and a quick way to\nfind a note for anyone. The selected note comes first, then the notes it\nlinks to and from, then every other note, and the search field narrows it.\nSelecting a note in the list or in the drawing is the same selection.\n\nSet `depth` to show the local graph: only the notes within that many links\nof the selected one.\n\nSet `legend` to name the groups over the drawing, each a button that shows\nor hides its notes. `hiddenGroups` holds the groups turned off, and the\nlist holds only what the drawing shows. The legend is a toolbar and one tab\nstop: the arrow keys, Home, End and a group's first letters move between\nits buttons, and Space or Enter presses one.\n\n`nodes` and `links` are properties, never markup: a graph of ten thousand\nnotes as ten thousand elements would cost what the renderer exists to save.\nThe list draws only the rows in view.\n\nThe list scrolls on this tier. It carries `data-scroller`, and `core` ships\n`[data-scroller] { overflow: auto }` into every shadow root. You still need\nto set a block size on the list, or it grows to hold every row.",
75726
+ "description": "Helix draws no graph. A force layout of thousands of notes is physics and\nWebGL, which a renderer such as Sigma.js does. Put the renderer's box in the\nframe, and `connectGraph` from `@fusion.dev/helix-graph` draws `nodes` and\n`links` in it and keeps it in step with the frame.\n\nWhat the frame adds is what a drawing of a graph cannot do: a list of every\nnote beside it, which is the graph for a screen reader and a quick way to\nfind a note for anyone. The selected note comes first, then the notes it\nlinks to and from, then every other note, and the search field narrows it.\nSelecting a note in the list or in the drawing is the same selection.\n\nSet `depth` to show the local graph: only the notes within that many links\nof the selected one.\n\nThe search field is a combobox over the list. Down and Up move through the\nnotes while the focus stays in the field, and Enter selects one.\n\n**Walk the links with Alt and the arrow keys**, in the list or in the\nsearch field once its keys are on a note. The note the keys are on is\nmarked in the drawing.\n\n| Key | What it does |\n|---|---|\n| Alt+Right | Follows a link, to the first note the one the keys are on links to or from. |\n| Alt+Down, Alt+Up | Moves to the next or the previous note linked to the one the step came from, wrapping. |\n| Alt+Left | Goes back along the links walked. |\n\nRight and Left swap on a right-to-left page. Each step says the note, how it\nstands to the note it came from, and its place among that note's links,\nsuch as \"Hiring plan. Linked from Roadmap. 2 of 3\". Enter selects the note.\n\nSet `legend` to name the groups over the drawing, each a button that shows\nor hides its notes. `hiddenGroups` holds the groups turned off, and the\nlist holds only what the drawing shows. The legend is a toolbar and one tab\nstop: the arrow keys, Home, End and a group's first letters move between\nits buttons, and Space or Enter presses one.\n\n`nodes` and `links` are properties, never markup: a graph of ten thousand\nnotes as ten thousand elements would cost what the renderer exists to save.\nThe list draws only the rows in view.\n\nThe list scrolls on this tier. It carries `data-scroller`, and `core` ships\n`[data-scroller] { overflow: auto }` into every shadow root. You still need\nto set a block size on the list, or it grows to hold every row.",
75481
75727
  "name": "HelixHeadlessGraph",
75482
75728
  "cssParts": [
75483
75729
  {
@@ -75830,6 +76076,19 @@
75830
76076
  "words": true
75831
76077
  }
75832
76078
  },
76079
+ {
76080
+ "kind": "field",
76081
+ "name": "linkCountLabel",
76082
+ "type": {
76083
+ "text": "string"
76084
+ },
76085
+ "default": "'{note}. {count, plural, =0 {No links} one {# link} other {# links}}'",
76086
+ "description": "What screen readers hear when a walk along the links reaches a note that\nhas no link to follow, or goes back to a note: `{note}` for its name and\n`{count}` for how many notes the list holds that it links to or from.",
76087
+ "attribute": "link-count-label",
76088
+ "helix": {
76089
+ "words": true
76090
+ }
76091
+ },
75833
76092
  {
75834
76093
  "kind": "field",
75835
76094
  "name": "linkedBothLabel",
@@ -76081,6 +76340,19 @@
76081
76340
  "module": "src/mixins/mixin-viewport/mixin-viewport.ts"
76082
76341
  }
76083
76342
  },
76343
+ {
76344
+ "kind": "field",
76345
+ "name": "walkLabel",
76346
+ "type": {
76347
+ "text": "string"
76348
+ },
76349
+ "default": "'{note}. {relation}. {index} of {count}'",
76350
+ "description": "What screen readers hear when a walk along the links moves to a note:\n`{note}` for its name, `{relation}` for how it stands to the note the\nstep came from, such as \"Linked from Roadmap\", and `{index}` and `{count}`\nfor its place among that note's links.",
76351
+ "attribute": "walk-label",
76352
+ "helix": {
76353
+ "words": true
76354
+ }
76355
+ },
76084
76356
  {
76085
76357
  "kind": "field",
76086
76358
  "name": "wheelHintLabel",
@@ -76154,6 +76426,45 @@
76154
76426
  "default": "false",
76155
76427
  "description": "Whether the search and the list are open over the drawing, with `no-panel`."
76156
76428
  },
76429
+ {
76430
+ "kind": "field",
76431
+ "name": "fieldKeys",
76432
+ "type": {
76433
+ "text": "boolean"
76434
+ },
76435
+ "privacy": "private",
76436
+ "default": "false",
76437
+ "description": "Whether the search field's arrow keys have moved onto a note, which the\nfield then names as its `aria-activedescendant` while the focus stays in\nit. Typing, Left, Right, Escape, and leaving the field let it go."
76438
+ },
76439
+ {
76440
+ "kind": "field",
76441
+ "name": "listKeys",
76442
+ "type": {
76443
+ "text": "boolean"
76444
+ },
76445
+ "privacy": "private",
76446
+ "default": "false",
76447
+ "description": "Whether the list has the focus, so the drawing marks the note its keys are on."
76448
+ },
76449
+ {
76450
+ "kind": "field",
76451
+ "name": "trail",
76452
+ "type": {
76453
+ "text": "string[]"
76454
+ },
76455
+ "privacy": "private",
76456
+ "default": "[]",
76457
+ "description": "The notes a walk along the links stepped from, by `id`, the last the one\nthe note the keys are on was reached from. Empty when the keys moved some\nother way."
76458
+ },
76459
+ {
76460
+ "kind": "field",
76461
+ "name": "keyedSent",
76462
+ "type": {
76463
+ "text": "string | undefined"
76464
+ },
76465
+ "privacy": "private",
76466
+ "description": "The note the drawing last heard the keys are on, so it hears each move once."
76467
+ },
76157
76468
  {
76158
76469
  "kind": "field",
76159
76470
  "name": "adjacency",
@@ -76276,6 +76587,17 @@
76276
76587
  "readonly": true,
76277
76588
  "default": "new VirtualizeController(this, { container: () => this.partNamed('list'), count: () => this.entries.length, estimate: () => 32, rows: () => [...(this.renderRoot.querySelectorAll?.('[data-index]') ?? [])].map((element) => ({ element: element as HTMLElement, index: Number((element as HTMLElement).dataset.index), })), })"
76278
76589
  },
76590
+ {
76591
+ "kind": "method",
76592
+ "name": "keyedId",
76593
+ "privacy": "private",
76594
+ "return": {
76595
+ "type": {
76596
+ "text": "string | undefined"
76597
+ }
76598
+ },
76599
+ "description": "The `id` of the note the keys are on, or `undefined` where neither the list nor the field has them."
76600
+ },
76279
76601
  {
76280
76602
  "kind": "field",
76281
76603
  "name": "ready",
@@ -76592,6 +76914,10 @@
76592
76914
  "type": {
76593
76915
  "text": "number"
76594
76916
  }
76917
+ },
76918
+ {
76919
+ "name": "walking",
76920
+ "default": "false"
76595
76921
  }
76596
76922
  ],
76597
76923
  "description": "Moves the keyboard to a row, and the list to show it.\n\n**The row the keys are on is always drawn**, beside the window where it is\noutside it, so `aria-activedescendant` names a row that exists. The list\nthen scrolls to it, see `revealCursor`."
@@ -76657,6 +76983,88 @@
76657
76983
  ],
76658
76984
  "description": "Moves to the next note whose name starts with what was typed, through\ncore's `Typeahead`, as a listbox does: letters within half a second run\ntogether, and a repeated letter steps through the names that start with it."
76659
76985
  },
76986
+ {
76987
+ "kind": "method",
76988
+ "name": "linkedRows",
76989
+ "privacy": "private",
76990
+ "return": {
76991
+ "type": {
76992
+ "text": "number[]"
76993
+ }
76994
+ },
76995
+ "parameters": [
76996
+ {
76997
+ "name": "id",
76998
+ "type": {
76999
+ "text": "string"
77000
+ }
77001
+ }
77002
+ ],
77003
+ "description": "The rows of the notes `id` links to or is linked from, in the list's\norder, which is the order of their names. Only the notes the list holds:\na search, the legend, or `depth` leaves the others out."
77004
+ },
77005
+ {
77006
+ "kind": "method",
77007
+ "name": "relationFrom",
77008
+ "privacy": "private",
77009
+ "return": {
77010
+ "type": {
77011
+ "text": "string"
77012
+ }
77013
+ },
77014
+ "parameters": [
77015
+ {
77016
+ "name": "from",
77017
+ "type": {
77018
+ "text": "string"
77019
+ }
77020
+ },
77021
+ {
77022
+ "name": "id",
77023
+ "type": {
77024
+ "text": "string"
77025
+ }
77026
+ }
77027
+ ],
77028
+ "description": "How the note `id` stands to the note `from`, in the words the list uses for the selection."
77029
+ },
77030
+ {
77031
+ "kind": "method",
77032
+ "name": "walk",
77033
+ "privacy": "private",
77034
+ "return": {
77035
+ "type": {
77036
+ "text": "boolean"
77037
+ }
77038
+ },
77039
+ "parameters": [
77040
+ {
77041
+ "name": "event",
77042
+ "type": {
77043
+ "text": "KeyboardEvent"
77044
+ }
77045
+ }
77046
+ ],
77047
+ "description": "A walk along the links from the note the keys are on, with Alt and the\narrow keys, so a reader follows the lines of the drawing and not only\nthe order of the list. **The same four keys a tree takes**, with Alt,\nsince the list's own arrows already move through it:\n\n- Alt+Right follows a link, to the first note the one the keys are on\n links to or from.\n- Alt+Down and Alt+Up move to the next and the previous note linked to\n the one the step came from, wrapping.\n- Alt+Left goes back along the links walked.\n\nRight and Left swap on a right-to-left page. Each step says the note, how\nit stands to the note it was reached from, and its place among that\nnote's links. Returns whether the key was one of the walk's."
77048
+ },
77049
+ {
77050
+ "kind": "method",
77051
+ "name": "sayLinks",
77052
+ "privacy": "private",
77053
+ "return": {
77054
+ "type": {
77055
+ "text": "void"
77056
+ }
77057
+ },
77058
+ "parameters": [
77059
+ {
77060
+ "name": "at",
77061
+ "type": {
77062
+ "text": "number"
77063
+ }
77064
+ }
77065
+ ],
77066
+ "description": "Says the note at row `at` and how many notes in the list it links to or from."
77067
+ },
76660
77068
  {
76661
77069
  "kind": "method",
76662
77070
  "name": "pageRows",
@@ -76673,7 +77081,14 @@
76673
77081
  "name": "handleSearchKeydown",
76674
77082
  "privacy": "private",
76675
77083
  "readonly": true,
76676
- "description": "The search field's keys, which belong to the field and never pan the\ngraph. Escape empties a search that has words in it, and passes through\nan empty one to whatever the page does with it."
77084
+ "description": "The search field's keys, which belong to the field and never pan the\ngraph.\n\n**The field is a combobox over the list**, as React Aria's Autocomplete\nand the APG combobox are: the focus stays in the field, and Down and Up\nmove the keys through the notes, which the field names as its\n`aria-activedescendant`. Down starts on the note the keys were last on,\nthe first after a search, and Up on the last. Page Down and Page Up move\na page, Enter selects, Alt and the arrows walk the links as in the list,\nand Left, Right, Home and End go back to the text.\n\nEscape lets the note go, then empties a search that has words in it, and\npasses through an empty one to whatever the page does with it."
77085
+ },
77086
+ {
77087
+ "kind": "field",
77088
+ "name": "handleSearchBlur",
77089
+ "privacy": "private",
77090
+ "readonly": true,
77091
+ "description": "Leaving the field lets go of the note its keys were on."
76677
77092
  },
76678
77093
  {
76679
77094
  "kind": "method",
@@ -76718,7 +77133,8 @@
76718
77133
  "kind": "field",
76719
77134
  "name": "handleSearch",
76720
77135
  "privacy": "private",
76721
- "readonly": true
77136
+ "readonly": true,
77137
+ "description": "Typing is a new search, read from its top, with no note under the keys until Down."
76722
77138
  },
76723
77139
  {
76724
77140
  "kind": "method",
@@ -77905,7 +78321,7 @@
77905
78321
  "type": {
77906
78322
  "text": "HelixGraphPartChangeEvent"
77907
78323
  },
77908
- "description": "The notes, the links, the selection, the depth or the gestures changed. Used by `connectGraph`; pages do not need to listen for it.",
78324
+ "description": "The notes, the links, the selection, the depth, the note the keys are on, or the gestures changed. Used by `connectGraph`; pages do not need to listen for it.",
77909
78325
  "name": "helix-graph-part-change",
77910
78326
  "reactProp": "onhelix-graph-part-change"
77911
78327
  },
@@ -78015,6 +78431,15 @@
78015
78431
  "description": "The legend's name, read by screen readers.",
78016
78432
  "fieldName": "legendLabel"
78017
78433
  },
78434
+ {
78435
+ "name": "link-count-label",
78436
+ "type": {
78437
+ "text": "string"
78438
+ },
78439
+ "default": "'{note}. {count, plural, =0 {No links} one {# link} other {# links}}'",
78440
+ "description": "What screen readers hear when a walk along the links reaches a note that\nhas no link to follow, or goes back to a note: `{note}` for its name and\n`{count}` for how many notes the list holds that it links to or from.",
78441
+ "fieldName": "linkCountLabel"
78442
+ },
78018
78443
  {
78019
78444
  "name": "linked-both-label",
78020
78445
  "type": {
@@ -78168,6 +78593,15 @@
78168
78593
  "description": "The line shown while the graph, with `gestures=\"activate\"`, has the gestures.",
78169
78594
  "fieldName": "usingLabel"
78170
78595
  },
78596
+ {
78597
+ "name": "walk-label",
78598
+ "type": {
78599
+ "text": "string"
78600
+ },
78601
+ "default": "'{note}. {relation}. {index} of {count}'",
78602
+ "description": "What screen readers hear when a walk along the links moves to a note:\n`{note}` for its name, `{relation}` for how it stands to the note the\nstep came from, such as \"Linked from Roadmap\", and `{index}` and `{count}`\nfor its place among that note's links.",
78603
+ "fieldName": "walkLabel"
78604
+ },
78171
78605
  {
78172
78606
  "name": "wheel-hint-label",
78173
78607
  "type": {
@@ -199002,6 +199436,6 @@
199002
199436
  ],
199003
199437
  "helix": {
199004
199438
  "package": "@fusion.dev/helix-headless",
199005
- "version": "0.177.2"
199439
+ "version": "0.178.0"
199006
199440
  }
199007
199441
  }
@@ -3,3 +3,4 @@ export * from './field/filter-field.js';
3
3
  export * from './grammar.js';
4
4
  export * from './operator/filter-operator.js';
5
5
  export * from './root/filter-input-root.js';
6
+ export type { FilterSource, FilterSourceRequest, FilterSuggestion } from './suggest.js';