@fusion.dev/helix-headless 0.175.0 → 0.176.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.
- package/custom-elements.json +58 -17
- package/dist/filter-input/index.d.ts +0 -1
- package/dist/filter-input/index.js +0 -1
- package/dist/filter-input/menu/filter-menu.d.ts +4 -0
- package/dist/filter-input/menu/index.d.ts +1 -0
- package/dist/filter-input/menu/index.js +1 -0
- package/dist/filter-input/root/filter-input-root.d.ts +26 -2
- package/dist/filter-input/root/filter-input-root.js +39 -5
- package/dist/icon/all/index.d.ts +1 -0
- package/dist/icon/all/index.js +4 -0
- package/dist/icon/root/icon-root.d.ts +9 -8
- package/dist/icon/root/icon-root.js +4 -2
- package/dist/icon/set.js +16 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +52 -32
- package/types/vue.d.ts +7 -9
package/custom-elements.json
CHANGED
|
@@ -69284,14 +69284,6 @@
|
|
|
69284
69284
|
"module": "src/filter-input/grammar.js"
|
|
69285
69285
|
}
|
|
69286
69286
|
},
|
|
69287
|
-
{
|
|
69288
|
-
"kind": "js",
|
|
69289
|
-
"name": "*",
|
|
69290
|
-
"declaration": {
|
|
69291
|
-
"name": "*",
|
|
69292
|
-
"module": "src/filter-input/menu/filter-menu.js"
|
|
69293
|
-
}
|
|
69294
|
-
},
|
|
69295
69287
|
{
|
|
69296
69288
|
"kind": "js",
|
|
69297
69289
|
"name": "*",
|
|
@@ -69316,7 +69308,7 @@
|
|
|
69316
69308
|
"declarations": [
|
|
69317
69309
|
{
|
|
69318
69310
|
"kind": "class",
|
|
69319
|
-
"description": "Reads the `FilterField` declarations inside it and renders a two-level menu:\nthe fields, and under each field its operators and values. The menu does not\ninterpret what a field means.\n\n```html\n<helix-headless-filter-menu label=\"Filter\">\n <helix-headless-filter-field name=\"disposition\" label=\"Disposition\">\n <helix-headless-option value=\"refunded\">Refunded</helix-headless-option>\n </helix-headless-filter-field>\n</helix-headless-filter-menu>\n```\n\nThat field declares no operators, so it offers `is any of` and `is none of`,\nthe two the typed grammar supports. The menu and the typed text offer the\nsame filters because both read `FilterField.vocabulary`. Declare other\noperators on the field when you need them.\n\nTicking a value applies the filter immediately. There is no Apply button.\nEach tick fires `helix-facet` with the field's whole set of values, so\nresults narrow as the user works. Unticking the last value fires an empty\nset, which is your cue to remove the chip.\n\nThe operator is chosen in the menu. `helix-facet` carries an empty\n`operatorLabel` when the operator is the field's default, which is the value\na chip's `operator-label` expects, so the operator segment appears only for a\nnon-default choice. The menu decides this because it is the only element that\nreads both the field and its operators.\n\nAn operator's `arity` sets what appears under it:\n\n- `arity=\"none\"`, such as `is empty` or `is set`, offers no values and clears\n any that were ticked. The operator is the whole filter.\n- `arity=\"one\"` offers the values as a single choice. Pressing another value\n replaces the choice, and pressing the chosen value clears it.\n\n`helix-facet` includes `arity`, so you can tell an empty filter from no\nfilter.\n\nThe menu applies nothing. The chips are your markup and the rows are your\ndata, so the menu fires the event and leaves the change to you, as\n`Notification` does for a read. Call `preventDefault()` on `helix-facet` to\ncancel the change.",
|
|
69311
|
+
"description": "Reads the `FilterField` declarations inside it and renders a two-level menu:\nthe fields, and under each field its operators and values. The menu does not\ninterpret what a field means.\n\n```html\n<helix-headless-filter-menu label=\"Filter\">\n <helix-headless-filter-field name=\"disposition\" label=\"Disposition\">\n <helix-headless-option value=\"refunded\">Refunded</helix-headless-option>\n </helix-headless-filter-field>\n</helix-headless-filter-menu>\n```\n\nThat field declares no operators, so it offers `is any of` and `is none of`,\nthe two the typed grammar supports. The menu and the typed text offer the\nsame filters because both read `FilterField.vocabulary`. Declare other\noperators on the field when you need them.\n\nTicking a value applies the filter immediately. There is no Apply button.\nEach tick fires `helix-facet` with the field's whole set of values, so\nresults narrow as the user works. Unticking the last value fires an empty\nset, which is your cue to remove the chip.\n\nThe operator is chosen in the menu. `helix-facet` carries an empty\n`operatorLabel` when the operator is the field's default, which is the value\na chip's `operator-label` expects, so the operator segment appears only for a\nnon-default choice. The menu decides this because it is the only element that\nreads both the field and its operators.\n\nAn operator's `arity` sets what appears under it:\n\n- `arity=\"none\"`, such as `is empty` or `is set`, offers no values and clears\n any that were ticked. The operator is the whole filter.\n- `arity=\"one\"` offers the values as a single choice. Pressing another value\n replaces the choice, and pressing the chosen value clears it.\n\n`helix-facet` includes `arity`, so you can tell an empty filter from no\nfilter.\n\nThe menu applies nothing. The chips are your markup and the rows are your\ndata, so the menu fires the event and leaves the change to you, as\n`Notification` does for a read. Call `preventDefault()` on `helix-facet` to\ncancel the change.\n\nImported from its own entry, `@fusion.dev/helix-headless/filter-input/menu`, which registers\nit with the `Menu` it draws its pages with. The family's entry leaves it out,\nso a field without a picker does not carry `Menu`.",
|
|
69320
69312
|
"name": "HelixHeadlessFilterMenu",
|
|
69321
69313
|
"cssParts": [
|
|
69322
69314
|
{
|
|
@@ -69999,6 +69991,21 @@
|
|
|
69999
69991
|
}
|
|
70000
69992
|
]
|
|
70001
69993
|
},
|
|
69994
|
+
{
|
|
69995
|
+
"kind": "javascript-module",
|
|
69996
|
+
"path": "src/filter-input/menu/index.ts",
|
|
69997
|
+
"declarations": [],
|
|
69998
|
+
"exports": [
|
|
69999
|
+
{
|
|
70000
|
+
"kind": "js",
|
|
70001
|
+
"name": "*",
|
|
70002
|
+
"declaration": {
|
|
70003
|
+
"name": "*",
|
|
70004
|
+
"module": "src/filter-input/menu/filter-menu.js"
|
|
70005
|
+
}
|
|
70006
|
+
}
|
|
70007
|
+
]
|
|
70008
|
+
},
|
|
70002
70009
|
{
|
|
70003
70010
|
"kind": "javascript-module",
|
|
70004
70011
|
"path": "src/filter-input/operator/filter-operator.ts",
|
|
@@ -70484,7 +70491,7 @@
|
|
|
70484
70491
|
"declarations": [
|
|
70485
70492
|
{
|
|
70486
70493
|
"kind": "class",
|
|
70487
|
-
"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
|
|
70494
|
+
"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.",
|
|
70488
70495
|
"name": "HelixHeadlessFilterInput",
|
|
70489
70496
|
"cssParts": [
|
|
70490
70497
|
{
|
|
@@ -71011,7 +71018,7 @@
|
|
|
71011
71018
|
"privacy": "private",
|
|
71012
71019
|
"return": {
|
|
71013
71020
|
"type": {
|
|
71014
|
-
"text": "
|
|
71021
|
+
"text": "FieldPicker | null"
|
|
71015
71022
|
}
|
|
71016
71023
|
},
|
|
71017
71024
|
"parameters": [
|
|
@@ -71074,7 +71081,7 @@
|
|
|
71074
71081
|
"name": "handleDismiss",
|
|
71075
71082
|
"privacy": "private",
|
|
71076
71083
|
"readonly": true,
|
|
71077
|
-
"description": "A chip asking to go, answered as the filter going.\n\n**Taken rather than passed on, and only for a chip naming a declared\nfield.** Left alone, a dismissed chip removed itself and told the picker\nnothing, so the menu went on showing the value ticked and the reader's next\npress on it read as *unticking*: they pressed `Refunded` and nothing came\nback. Turning the request into `apply(field, [])` puts it down the one road\nevery other filter takes, so the chip goes because the filter went. A\nconsumer refusing that `helix-facet` keeps both, which is the answer a\nrefusal should give.\n\nEverything else in the row is untouched: a plain `Tag`, or a chip for\nsomething no `FilterField` declares, is the consumer's to answer."
|
|
71084
|
+
"description": "A chip asking to go, answered as the filter going.\n\n**Taken rather than passed on, and only for a chip naming a declared\nfield.** Left alone, a dismissed chip removed itself and told the picker\nnothing, so the menu went on showing the value ticked and the reader's next\npress on it read as *unticking*: they pressed `Refunded` and nothing came\nback. Turning the request into `apply(field, [])` puts it down the one road\nevery other filter takes, so the chip goes because the filter went. A\nconsumer refusing that `helix-facet` keeps both, which is the answer a\nrefusal should give.\n\nEverything else in the row is untouched: a plain `Tag`, or a chip for\nsomething no `FilterField` declares, is the consumer's to answer.\n\n**Heard on the way down, in the capture phase**, because the chip is a\nchild of this element and its request passes here before it reaches the\nchip's listeners or comes back up. Stopped there, no listener on the chip,\non this element or above it hears it. Heard on the way up, `stopPropagation`\nkept it from the ancestors and not from this element's other listeners, so a\nconsumer listening on the field heard `helix-dismiss` as well as the\n`helix-facet` that answered it."
|
|
71078
71085
|
},
|
|
71079
71086
|
{
|
|
71080
71087
|
"kind": "method",
|
|
@@ -71216,6 +71223,17 @@
|
|
|
71216
71223
|
"module": "src/mixins/mixin-tokens/mixin-tokens.ts"
|
|
71217
71224
|
}
|
|
71218
71225
|
},
|
|
71226
|
+
{
|
|
71227
|
+
"kind": "method",
|
|
71228
|
+
"name": "auditPicker",
|
|
71229
|
+
"privacy": "private",
|
|
71230
|
+
"return": {
|
|
71231
|
+
"type": {
|
|
71232
|
+
"text": "void"
|
|
71233
|
+
}
|
|
71234
|
+
},
|
|
71235
|
+
"description": "Says so when a picker is written in the field and never registered.\n\nThe picker has an entry of its own, so a page that imports only the field\ngets an inert element there: a trigger that opens nothing. Asked once the\npage has loaded and painted, so an entry a later module imports is not\ntaken for a missing one."
|
|
71236
|
+
},
|
|
71219
71237
|
{
|
|
71220
71238
|
"kind": "method",
|
|
71221
71239
|
"name": "markActive",
|
|
@@ -81564,6 +81582,21 @@
|
|
|
81564
81582
|
}
|
|
81565
81583
|
]
|
|
81566
81584
|
},
|
|
81585
|
+
{
|
|
81586
|
+
"kind": "javascript-module",
|
|
81587
|
+
"path": "src/icon/all/index.ts",
|
|
81588
|
+
"declarations": [],
|
|
81589
|
+
"exports": [
|
|
81590
|
+
{
|
|
81591
|
+
"kind": "js",
|
|
81592
|
+
"name": "*",
|
|
81593
|
+
"declaration": {
|
|
81594
|
+
"name": "*",
|
|
81595
|
+
"module": "src/icon/index.js"
|
|
81596
|
+
}
|
|
81597
|
+
}
|
|
81598
|
+
]
|
|
81599
|
+
},
|
|
81567
81600
|
{
|
|
81568
81601
|
"kind": "javascript-module",
|
|
81569
81602
|
"path": "src/icon/fetch.ts",
|
|
@@ -81870,7 +81903,7 @@
|
|
|
81870
81903
|
"declarations": [
|
|
81871
81904
|
{
|
|
81872
81905
|
"kind": "class",
|
|
81873
|
-
"description": "The icons are Phosphor, drawn on a 256 box at stroke-width 16. Each glyph\nrenders its full inner markup, because most icons are several elements, each\nwith its own stroke and fill.\n\nThe markup is generated at build time from SVG files in this repository and\nnever comes from user input, so rendering it with `unsafeSVG` is safe.\n\nThe `<svg>` carries `fill=\"currentColor\"`. The generator keeps only the inner\nmarkup, and Phosphor sets its fill on the root tag that the generator\ndiscards. Without it, the 65 solid glyphs fall back to black and disappear on\na dark background. Shapes that set their own `fill` or `stroke` are\nunaffected.\n\n**A name is looked up in a registry.** The family's entry registers the\nwhole set, so any name in it draws. The element's own entry,\n`@fusion.dev/helix-headless/icon/root`, registers none: a page that imports\nit and calls `registerIcons` with the glyphs it names ships those and no\nothers. `registerIconLibrary` resolves names under a prefix of your own,\nsuch as `lucide:camera`, at once or later, and the element fires `load` when\na glyph that came later is drawn. A glyph registered after the element\nrendered is drawn then.\n\n**Or point `src` at an SVG file**, such as `src=\"/icons/rocket.svg\"`, and\nthe element fetches it and draws its shapes in `currentColor` where the file\ndoes not color them. A file is fetched once a page, however many icons name\nit, and `load` fires when it is drawn. Scripts, event handlers and\n`javascript:` links in the file are taken out before it is drawn. A server\nfetches nothing, so a served icon with a `src` is drawn once the script\nruns.\n\nA name with no match, or a `src` that is no SVG, renders nothing, and the\nelement fires `error`, so a typo in a name can be heard. It waits until the page has loaded and painted\nfirst, so a library a later module registers is not taken for a typo.\n\nAn icon is decorative by default and hidden from assistive technology. Set\n`label` only when the icon is the only carrier of meaning, such as in a\nbutton with no text. An `aria-label` on the element works too: the element\nis then an image with that name.\n\n**A decorative icon that is all a control holds is reported.** In a\ndevelopment build, a button or a link whose only content is an icon with no\n`label`, and which has no name of its own, warns `missing-name` once,\nthrough the runtime channel every Helix warning uses.",
|
|
81906
|
+
"description": "The icons are Phosphor, drawn on a 256 box at stroke-width 16. Each glyph\nrenders its full inner markup, because most icons are several elements, each\nwith its own stroke and fill.\n\nThe markup is generated at build time from SVG files in this repository and\nnever comes from user input, so rendering it with `unsafeSVG` is safe.\n\nThe `<svg>` carries `fill=\"currentColor\"`. The generator keeps only the inner\nmarkup, and Phosphor sets its fill on the root tag that the generator\ndiscards. Without it, the 65 solid glyphs fall back to black and disappear on\na dark background. Shapes that set their own `fill` or `stroke` are\nunaffected.\n\n**A name is looked up in a registry.** The family's entry registers a small\nset: the four carets, `arrow-left`, `arrow-right`, `check`, `x`, `plus`,\n`minus`, `magnifying-glass`, `info` and `warning`. `icon/all` registers the\nwhole set, so any name in it draws, and a name that resolves to nothing\nsays to import it. The element's own entry,\n`@fusion.dev/helix-headless/icon/root`, registers none: a page that imports\nit and calls `registerIcons` with the glyphs it names ships those and no\nothers. `registerIconLibrary` resolves names under a prefix of your own,\nsuch as `lucide:camera`, at once or later, and the element fires `load` when\na glyph that came later is drawn. A glyph registered after the element\nrendered is drawn then.\n\n**Or point `src` at an SVG file**, such as `src=\"/icons/rocket.svg\"`, and\nthe element fetches it and draws its shapes in `currentColor` where the file\ndoes not color them. A file is fetched once a page, however many icons name\nit, and `load` fires when it is drawn. Scripts, event handlers and\n`javascript:` links in the file are taken out before it is drawn. A server\nfetches nothing, so a served icon with a `src` is drawn once the script\nruns.\n\nA name with no match, or a `src` that is no SVG, renders nothing, and the\nelement fires `error`, so a typo in a name can be heard. It waits until the page has loaded and painted\nfirst, so a library a later module registers is not taken for a typo.\n\nAn icon is decorative by default and hidden from assistive technology. Set\n`label` only when the icon is the only carrier of meaning, such as in a\nbutton with no text. An `aria-label` on the element works too: the element\nis then an image with that name.\n\n**A decorative icon that is all a control holds is reported.** In a\ndevelopment build, a button or a link whose only content is an icon with no\n`label`, and which has no name of its own, warns `missing-name` once,\nthrough the runtime channel every Helix warning uses.",
|
|
81874
81907
|
"name": "HelixHeadlessIcon",
|
|
81875
81908
|
"cssParts": [
|
|
81876
81909
|
{
|
|
@@ -81914,7 +81947,7 @@
|
|
|
81914
81947
|
"text": "string"
|
|
81915
81948
|
},
|
|
81916
81949
|
"default": "''",
|
|
81917
|
-
"description": "The name of the icon in the Helix set.\n\n**A lookup, and the reason both this and `icon` exist.** A name is a string,\nso it is what markup can carry, and resolving one means an index
|
|
81950
|
+
"description": "The name of the icon in the Helix set.\n\n**A lookup, and the reason both this and `icon` exist.** A name is a string,\nso it is what markup can carry, and resolving one means an index that no\nbundler can shake. The family's entry indexes a small set, `icon/all`\nindexes the whole set at 15 kB, and `icon/root` indexes nothing, for a\npage that registers the glyphs it names or hands over the value instead.\n\nA name written `prefix:name` asks the library registered under that\nprefix.",
|
|
81918
81951
|
"attribute": "name"
|
|
81919
81952
|
},
|
|
81920
81953
|
{
|
|
@@ -82465,7 +82498,7 @@
|
|
|
82465
82498
|
"text": "string"
|
|
82466
82499
|
},
|
|
82467
82500
|
"default": "''",
|
|
82468
|
-
"description": "The name of the icon in the Helix set.\n\n**A lookup, and the reason both this and `icon` exist.** A name is a string,\nso it is what markup can carry, and resolving one means an index
|
|
82501
|
+
"description": "The name of the icon in the Helix set.\n\n**A lookup, and the reason both this and `icon` exist.** A name is a string,\nso it is what markup can carry, and resolving one means an index that no\nbundler can shake. The family's entry indexes a small set, `icon/all`\nindexes the whole set at 15 kB, and `icon/root` indexes nothing, for a\npage that registers the glyphs it names or hands over the value instead.\n\nA name written `prefix:name` asks the library registered under that\nprefix.",
|
|
82469
82502
|
"fieldName": "name"
|
|
82470
82503
|
},
|
|
82471
82504
|
{
|
|
@@ -83776,6 +83809,14 @@
|
|
|
83776
83809
|
"module": "src/filter-input/index.js"
|
|
83777
83810
|
}
|
|
83778
83811
|
},
|
|
83812
|
+
{
|
|
83813
|
+
"kind": "js",
|
|
83814
|
+
"name": "*",
|
|
83815
|
+
"declaration": {
|
|
83816
|
+
"name": "*",
|
|
83817
|
+
"module": "src/filter-input/menu/index.js"
|
|
83818
|
+
}
|
|
83819
|
+
},
|
|
83779
83820
|
{
|
|
83780
83821
|
"kind": "js",
|
|
83781
83822
|
"name": "*",
|
|
@@ -83805,7 +83846,7 @@
|
|
|
83805
83846
|
"name": "*",
|
|
83806
83847
|
"declaration": {
|
|
83807
83848
|
"name": "*",
|
|
83808
|
-
"module": "src/icon/index.js"
|
|
83849
|
+
"module": "src/icon/all/index.js"
|
|
83809
83850
|
}
|
|
83810
83851
|
},
|
|
83811
83852
|
{
|
|
@@ -198274,6 +198315,6 @@
|
|
|
198274
198315
|
],
|
|
198275
198316
|
"helix": {
|
|
198276
198317
|
"package": "@fusion.dev/helix-headless",
|
|
198277
|
-
"version": "0.
|
|
198318
|
+
"version": "0.176.0"
|
|
198278
198319
|
}
|
|
198279
198320
|
}
|
|
@@ -51,6 +51,10 @@ import '../../menu/index.js';
|
|
|
51
51
|
* `Notification` does for a read. Call `preventDefault()` on `helix-facet` to
|
|
52
52
|
* cancel the change.
|
|
53
53
|
*
|
|
54
|
+
* Imported from its own entry, `@fusion.dev/helix-headless/filter-input/menu`, which registers
|
|
55
|
+
* it with the `Menu` it draws its pages with. The family's entry leaves it out,
|
|
56
|
+
* so a field without a picker does not carry `Menu`.
|
|
57
|
+
*
|
|
54
58
|
* @since 0.25.0
|
|
55
59
|
* @status stable
|
|
56
60
|
* @category forms
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './filter-menu.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './filter-menu.js';
|
|
@@ -42,7 +42,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
|
|
|
42
42
|
* | Enter | Presses the highlighted suggestion. With none highlighted, reads the typed filters into chips, or submits the form when there are none. |
|
|
43
43
|
* | Down, Up | Moves the highlight through the suggestions, wrapping, and opens the panel. |
|
|
44
44
|
* | Home, End | Highlights the first or the last suggestion while the panel is open. |
|
|
45
|
-
* | Escape | Closes the panel and stops there, so a dialog around the field stays open. |
|
|
45
|
+
* | 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. |
|
|
46
46
|
* | Backspace | In an empty field, asks the last token to go. |
|
|
47
47
|
* | 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. |
|
|
48
48
|
* | Left, Right on a token | Move between the tokens, and past the last back to the text. |
|
|
@@ -51,7 +51,8 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
|
|
|
51
51
|
* **Typed filters.** The field parses only the fields you declare. Write
|
|
52
52
|
* `FilterField` elements, usually inside the `FilterMenu` slotted into `start`,
|
|
53
53
|
* so the picker and the text share one set. On Enter, the field reads the text
|
|
54
|
-
* against them
|
|
54
|
+
* against them, and a paste is read the same way as soon as it lands.
|
|
55
|
+
* `disposition:refunded,settled` and `-disposition:refunded` fire
|
|
55
56
|
* `helix-facet` exactly as ticking those values in the picker would, and the
|
|
56
57
|
* parsed text is removed. Everything else stays, so `refund tuesday
|
|
57
58
|
* disposition:settled` is a filter and a search at once. Helix defines the
|
|
@@ -64,6 +65,12 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
|
|
|
64
65
|
* nothing draws it and the menu has no page for it, and the grammar still reads
|
|
65
66
|
* it.
|
|
66
67
|
*
|
|
68
|
+
* **The picker has an entry of its own.** `FilterMenu` draws its pages with
|
|
69
|
+
* `Menu`, which a field without a picker does not need, so this family's entry
|
|
70
|
+
* leaves it out. A page that writes one imports
|
|
71
|
+
* `@fusion.dev/helix/filter-input/menu` as well, and a picker written without it
|
|
72
|
+
* is reported once the page has loaded, with the entry to add.
|
|
73
|
+
*
|
|
67
74
|
* **Suggestions.** Slot options into `suggestions` and the field shows them,
|
|
68
75
|
* moves through them with the arrow keys, and presses one on Enter. The option
|
|
69
76
|
* fires its own `helix-select`, and you decide what choosing it means. Keys
|
|
@@ -408,6 +415,14 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
|
|
|
408
415
|
*
|
|
409
416
|
* Everything else in the row is untouched: a plain `Tag`, or a chip for
|
|
410
417
|
* something no `FilterField` declares, is the consumer's to answer.
|
|
418
|
+
*
|
|
419
|
+
* **Heard on the way down, in the capture phase**, because the chip is a
|
|
420
|
+
* child of this element and its request passes here before it reaches the
|
|
421
|
+
* chip's listeners or comes back up. Stopped there, no listener on the chip,
|
|
422
|
+
* on this element or above it hears it. Heard on the way up, `stopPropagation`
|
|
423
|
+
* kept it from the ancestors and not from this element's other listeners, so a
|
|
424
|
+
* consumer listening on the field heard `helix-dismiss` as well as the
|
|
425
|
+
* `helix-facet` that answered it.
|
|
411
426
|
*/
|
|
412
427
|
private readonly handleDismiss;
|
|
413
428
|
/**
|
|
@@ -512,6 +527,15 @@ export declare class HelixHeadlessFilterInput extends HelixHeadlessFilterInput_b
|
|
|
512
527
|
disconnectedCallback(): void;
|
|
513
528
|
protected willUpdate(changed: PropertyValues): void;
|
|
514
529
|
protected firstUpdated(changed: PropertyValues): void;
|
|
530
|
+
/**
|
|
531
|
+
* Says so when a picker is written in the field and never registered.
|
|
532
|
+
*
|
|
533
|
+
* The picker has an entry of its own, so a page that imports only the field
|
|
534
|
+
* gets an inert element there: a trigger that opens nothing. Asked once the
|
|
535
|
+
* page has loaded and painted, so an entry a later module imports is not
|
|
536
|
+
* taken for a missing one.
|
|
537
|
+
*/
|
|
538
|
+
private auditPicker;
|
|
515
539
|
protected updated(changed: PropertyValues): void;
|
|
516
540
|
/**
|
|
517
541
|
* Marks the highlighted suggestion, and tells the control which one it is.
|
|
@@ -4,14 +4,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, countLabel, customElement, drawIcon, drawStatus, formatWords, HelixFacetEvent, HelixFormElement, holdCaret, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, restoreValue, StatusController, scrollItemIntoView, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, countLabel, customElement, drawIcon, drawStatus, formatWords, HelixFacetEvent, HelixFormElement, holdCaret, MixinControllerOpen, MixinControllerValue, MixinField, MixinTokens, PopoverController, reportRuntime, restoreValue, StatusController, scrollItemIntoView, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { x } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
11
|
import { HelixHeadlessFilterField } from '../field/filter-field.js';
|
|
12
12
|
import { readFilterText } from '../grammar.js';
|
|
13
|
-
import { HelixHeadlessFilterMenu } from '../menu/filter-menu.js';
|
|
14
13
|
import { currentTerm, suggests, tokenEntry } from '../suggest.js';
|
|
14
|
+
const picks = (element) => typeof element.apply === 'function' &&
|
|
15
|
+
Array.isArray(element.fields);
|
|
15
16
|
const asksToGo = (node) => typeof node?.dismiss === 'function';
|
|
16
17
|
const isOption = (node) => 'active' in node && 'value' in node;
|
|
17
18
|
let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))) {
|
|
@@ -58,6 +59,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
58
59
|
restoreValue(control, this.value);
|
|
59
60
|
this.dismissed = false;
|
|
60
61
|
this.settleOpen();
|
|
62
|
+
if (event.inputType === 'insertFromPaste') {
|
|
63
|
+
this.readFilters();
|
|
64
|
+
}
|
|
61
65
|
};
|
|
62
66
|
this.handleChange = (event) => {
|
|
63
67
|
event.stopPropagation();
|
|
@@ -111,6 +115,12 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
111
115
|
}
|
|
112
116
|
return;
|
|
113
117
|
}
|
|
118
|
+
if (event.key === 'Escape' && !this.open && this.value !== '') {
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
event.stopPropagation();
|
|
121
|
+
this.handleClear();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
114
124
|
if (!this.suggesting) {
|
|
115
125
|
return;
|
|
116
126
|
}
|
|
@@ -281,7 +291,7 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
281
291
|
}
|
|
282
292
|
menuFor(field) {
|
|
283
293
|
for (const menu of this.querySelectorAll('*')) {
|
|
284
|
-
if (!(menu
|
|
294
|
+
if (!picks(menu)) {
|
|
285
295
|
continue;
|
|
286
296
|
}
|
|
287
297
|
if (menu.fields.some((declared) => (declared.name ?? '') === field)) {
|
|
@@ -387,12 +397,12 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
387
397
|
}
|
|
388
398
|
connectedCallback() {
|
|
389
399
|
super.connectedCallback();
|
|
390
|
-
this.addEventListener('helix-dismiss', this.handleDismiss);
|
|
400
|
+
this.addEventListener('helix-dismiss', this.handleDismiss, { capture: true });
|
|
391
401
|
this.addEventListener(HelixFacetEvent.eventName, this.handleFacet);
|
|
392
402
|
}
|
|
393
403
|
disconnectedCallback() {
|
|
394
404
|
super.disconnectedCallback();
|
|
395
|
-
this.removeEventListener('helix-dismiss', this.handleDismiss);
|
|
405
|
+
this.removeEventListener('helix-dismiss', this.handleDismiss, { capture: true });
|
|
396
406
|
this.removeEventListener(HelixFacetEvent.eventName, this.handleFacet);
|
|
397
407
|
}
|
|
398
408
|
willUpdate(changed) {
|
|
@@ -418,6 +428,30 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
418
428
|
this.finishHydration();
|
|
419
429
|
this.handleTokens();
|
|
420
430
|
this.refreshSuggesting();
|
|
431
|
+
this.auditPicker();
|
|
432
|
+
}
|
|
433
|
+
auditPicker() {
|
|
434
|
+
const { tier } = this.constructor;
|
|
435
|
+
const tag = `${tier}filter-menu`;
|
|
436
|
+
const check = () => {
|
|
437
|
+
if (this.isConnected && this.querySelector(tag) && !customElements.get(tag)) {
|
|
438
|
+
const entry = tier === 'helix-' ? '@fusion.dev/helix' : '@fusion.dev/helix-headless';
|
|
439
|
+
reportRuntime({
|
|
440
|
+
kind: 'unregistered-part',
|
|
441
|
+
message: `<${tag}> in <${this.localName}> is not registered, so its trigger opens nothing. Import '${entry}/filter-input/menu'.`,
|
|
442
|
+
tag: this.localName,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const frame = () => {
|
|
447
|
+
requestAnimationFrame(check);
|
|
448
|
+
};
|
|
449
|
+
if (document.readyState === 'complete') {
|
|
450
|
+
frame();
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
window.addEventListener('load', frame, { once: true });
|
|
454
|
+
}
|
|
421
455
|
}
|
|
422
456
|
updated(changed) {
|
|
423
457
|
super.updated(changed);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../index.js';
|
|
@@ -18,8 +18,11 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
18
18
|
* a dark background. Shapes that set their own `fill` or `stroke` are
|
|
19
19
|
* unaffected.
|
|
20
20
|
*
|
|
21
|
-
* **A name is looked up in a registry.** The family's entry registers
|
|
22
|
-
*
|
|
21
|
+
* **A name is looked up in a registry.** The family's entry registers a small
|
|
22
|
+
* set: the four carets, `arrow-left`, `arrow-right`, `check`, `x`, `plus`,
|
|
23
|
+
* `minus`, `magnifying-glass`, `info` and `warning`. `icon/all` registers the
|
|
24
|
+
* whole set, so any name in it draws, and a name that resolves to nothing
|
|
25
|
+
* says to import it. The element's own entry,
|
|
23
26
|
* `@fusion.dev/helix-headless/icon/root`, registers none: a page that imports
|
|
24
27
|
* it and calls `registerIcons` with the glyphs it names ships those and no
|
|
25
28
|
* others. `registerIconLibrary` resolves names under a prefix of your own,
|
|
@@ -94,12 +97,10 @@ export declare class HelixHeadlessIcon extends HelixElement {
|
|
|
94
97
|
* The name of the icon in the Helix set.
|
|
95
98
|
*
|
|
96
99
|
* **A lookup, and the reason both this and `icon` exist.** A name is a string,
|
|
97
|
-
* so it is what markup can carry, and resolving one means an index
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* element from `icon/root` and registers the glyphs it names, or hands over
|
|
102
|
-
* the value instead.
|
|
100
|
+
* so it is what markup can carry, and resolving one means an index that no
|
|
101
|
+
* bundler can shake. The family's entry indexes a small set, `icon/all`
|
|
102
|
+
* indexes the whole set at 15 kB, and `icon/root` indexes nothing, for a
|
|
103
|
+
* page that registers the glyphs it names or hands over the value instead.
|
|
103
104
|
*
|
|
104
105
|
* A name written `prefix:name` asks the library registered under that
|
|
105
106
|
* prefix.
|
|
@@ -131,8 +131,10 @@ let HelixHeadlessIcon = class HelixHeadlessIcon extends HelixElement {
|
|
|
131
131
|
kind: 'unknown-icon',
|
|
132
132
|
message: this.src === ''
|
|
133
133
|
? `<${this.localName} name="${asked}"> names no registered icon, so it draws ` +
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
`nothing. For a glyph of the Helix set, import '${this.localName.startsWith('helix-headless-')
|
|
135
|
+
? '@fusion.dev/helix-headless'
|
|
136
|
+
: '@fusion.dev/helix'}/icon/all', which registers every one. Otherwise check the name, or register ` +
|
|
137
|
+
'the glyph with `registerIcons`.'
|
|
136
138
|
: `<${this.localName} src="${asked}"> could not be fetched as an SVG file, so it ` +
|
|
137
139
|
'draws nothing. Check the URL, and that the file is an `<svg>`.',
|
|
138
140
|
tag: this.localName,
|
package/dist/icon/set.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { arrowLeft, arrowRight, caretDown, caretLeft, caretRight, caretUp, check, info, magnifyingGlass, minus, plus, warning, x, } from '@fusion.dev/helix-icons';
|
|
2
2
|
import { registerIcons } from './registry.js';
|
|
3
|
-
registerIcons(
|
|
3
|
+
registerIcons([
|
|
4
|
+
arrowLeft,
|
|
5
|
+
arrowRight,
|
|
6
|
+
caretDown,
|
|
7
|
+
caretLeft,
|
|
8
|
+
caretRight,
|
|
9
|
+
caretUp,
|
|
10
|
+
check,
|
|
11
|
+
info,
|
|
12
|
+
magnifyingGlass,
|
|
13
|
+
minus,
|
|
14
|
+
plus,
|
|
15
|
+
warning,
|
|
16
|
+
x,
|
|
17
|
+
]);
|
package/dist/index.d.ts
CHANGED
|
@@ -46,10 +46,11 @@ export * from './drilldown/index.js';
|
|
|
46
46
|
export * from './empty/index.js';
|
|
47
47
|
export * from './file-input/index.js';
|
|
48
48
|
export * from './filter-input/index.js';
|
|
49
|
+
export * from './filter-input/menu/index.js';
|
|
49
50
|
export * from './format-date/index.js';
|
|
50
51
|
export * from './graph/index.js';
|
|
51
52
|
export * from './heatmap/index.js';
|
|
52
|
-
export * from './icon/index.js';
|
|
53
|
+
export * from './icon/all/index.js';
|
|
53
54
|
export * from './icon-button/index.js';
|
|
54
55
|
export * from './image/index.js';
|
|
55
56
|
export * from './input/index.js';
|
package/dist/index.js
CHANGED
|
@@ -37,10 +37,11 @@ export * from './drilldown/index.js';
|
|
|
37
37
|
export * from './empty/index.js';
|
|
38
38
|
export * from './file-input/index.js';
|
|
39
39
|
export * from './filter-input/index.js';
|
|
40
|
+
export * from './filter-input/menu/index.js';
|
|
40
41
|
export * from './format-date/index.js';
|
|
41
42
|
export * from './graph/index.js';
|
|
42
43
|
export * from './heatmap/index.js';
|
|
43
|
-
export * from './icon/index.js';
|
|
44
|
+
export * from './icon/all/index.js';
|
|
44
45
|
export * from './icon-button/index.js';
|
|
45
46
|
export * from './image/index.js';
|
|
46
47
|
export * from './input/index.js';
|