@fusion.dev/helix-headless 0.44.0 → 0.46.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 +104 -19
- package/dist/carousel/root/carousel-root.d.ts +1 -1
- package/dist/carousel/root/carousel-root.js +13 -3
- package/dist/chat/message/chat-message.d.ts +14 -1
- package/dist/chat/message/chat-message.js +9 -2
- package/dist/checkbox-group/root/checkbox-group-root.js +3 -0
- package/dist/color-picker/root/color-picker-root.d.ts +19 -0
- package/dist/color-picker/root/color-picker-root.js +4 -0
- package/dist/drilldown/root/drilldown-root.js +5 -1
- package/dist/file-input/root/file-input-root.d.ts +45 -3
- package/dist/file-input/root/file-input-root.js +37 -25
- package/dist/filter-input/chip/filter-chip.d.ts +1 -1
- package/dist/filter-input/field/filter-field.d.ts +1 -1
- package/dist/filter-input/menu/filter-menu.d.ts +1 -1
- package/dist/filter-input/operator/filter-operator.d.ts +1 -1
- package/dist/filter-input/root/filter-input-root.d.ts +1 -1
- package/dist/listbox/root/listbox-root.js +3 -0
- package/dist/option/group/option-group.d.ts +24 -1
- package/dist/option/group/option-group.js +8 -0
- package/dist/option/root/option-root.d.ts +1 -1
- package/dist/radio/group/radio-group.d.ts +10 -1
- package/dist/radio/group/radio-group.js +7 -8
- package/dist/radio/root/radio-root.d.ts +18 -1
- package/dist/radio/root/radio-root.js +5 -1
- package/dist/skip-link/root/skip-link-root.d.ts +1 -1
- package/dist/steps/root/steps-root.js +3 -0
- package/dist/tabs/root/tabs-root.js +3 -0
- package/dist/toggle/group/toggle-group.js +3 -0
- package/dist/tree/root/tree-root.js +3 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +94 -14
- package/types/vue.d.ts +22 -4
package/custom-elements.json
CHANGED
|
@@ -10397,7 +10397,7 @@
|
|
|
10397
10397
|
"type": {
|
|
10398
10398
|
"text": "HelixSelectEvent"
|
|
10399
10399
|
},
|
|
10400
|
-
"description": "Fired
|
|
10400
|
+
"description": "Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`.",
|
|
10401
10401
|
"name": "helix-select",
|
|
10402
10402
|
"reactProp": "onhelix-select"
|
|
10403
10403
|
}
|
|
@@ -11388,7 +11388,7 @@
|
|
|
11388
11388
|
"type": {
|
|
11389
11389
|
"text": "'failed' | 'sending' | 'sent' | 'streaming' | undefined"
|
|
11390
11390
|
},
|
|
11391
|
-
"description": "What has happened to it.\n\nReflected, because every one of the four is a visual state and a stylesheet\nhas no other way to know. Absent is a settled message with nothing to\nreport, which is what most of a transcript is, and it is the absence of the\nattribute rather than a value of it: there is no initializer, so nothing is\nreflected until something happens.",
|
|
11391
|
+
"description": "What has happened to it.\n\nReflected, because every one of the four is a visual state and a stylesheet\nhas no other way to know. Absent is a settled message with nothing to\nreport, which is what most of a transcript is, and it is the absence of the\nattribute rather than a value of it: there is no initializer, so nothing is\nreflected until something happens.\n\n**The turn writes this once, on a retry nobody refused**, and `handleRetry`\nholds the reason. Everything else that moves it is the consumer's.",
|
|
11392
11392
|
"attribute": "state",
|
|
11393
11393
|
"reflects": true
|
|
11394
11394
|
},
|
|
@@ -11477,7 +11477,7 @@
|
|
|
11477
11477
|
"name": "handleRetry",
|
|
11478
11478
|
"privacy": "private",
|
|
11479
11479
|
"readonly": true,
|
|
11480
|
-
"description": "Retry is `helix-send` again, from the message rather than the composer.\n\n**Not an event of its own**, because a retry is the same request: the reader\nis offering what they wrote, and the only difference is which element is\noffering it. A listener that wants to tell them apart has `event.target`,\nwhich is the message on a retry and the composer on a first send, and that\nis a distinction the DOM already makes without a second name for it."
|
|
11480
|
+
"description": "Retry is `helix-send` again, from the message rather than the composer.\n\n**Not an event of its own**, because a retry is the same request: the reader\nis offering what they wrote, and the only difference is which element is\noffering it. A listener that wants to tell them apart has `event.target`,\nwhich is the message on a retry and the composer on a first send, and that\nis a distinction the DOM already makes without a second name for it.\n\n**Through `requestChange`, and the turn is what moves.** `helix-send` is\ncancelable because the composer clears only when nobody refused, and this\ndispatch had nothing behind it: the reader pressed the control, the turn sat\nat \"Not sent\" with the same control still on it, and `preventDefault()`\nrefused a decision nobody here was about to take. A retry that stands is a\nturn on its way, which is what `sending` means, so that is the default\naction and refusing it leaves the failure exactly where it was. A consumer\nwho wants something else refuses and assigns `state` themselves, which is\nhow control is taken from every other stateful element in the library."
|
|
11481
11481
|
},
|
|
11482
11482
|
{
|
|
11483
11483
|
"kind": "field",
|
|
@@ -11809,7 +11809,7 @@
|
|
|
11809
11809
|
"type": {
|
|
11810
11810
|
"text": "HelixSendEvent"
|
|
11811
11811
|
},
|
|
11812
|
-
"description": "Fired when the reader asks to send a failed message again, and
|
|
11812
|
+
"description": "Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were.",
|
|
11813
11813
|
"name": "helix-send",
|
|
11814
11814
|
"reactProp": "onhelix-send"
|
|
11815
11815
|
}
|
|
@@ -11870,7 +11870,7 @@
|
|
|
11870
11870
|
"type": {
|
|
11871
11871
|
"text": "'failed' | 'sending' | 'sent' | 'streaming' | undefined"
|
|
11872
11872
|
},
|
|
11873
|
-
"description": "What has happened to it.\n\nReflected, because every one of the four is a visual state and a stylesheet\nhas no other way to know. Absent is a settled message with nothing to\nreport, which is what most of a transcript is, and it is the absence of the\nattribute rather than a value of it: there is no initializer, so nothing is\nreflected until something happens.",
|
|
11873
|
+
"description": "What has happened to it.\n\nReflected, because every one of the four is a visual state and a stylesheet\nhas no other way to know. Absent is a settled message with nothing to\nreport, which is what most of a transcript is, and it is the absence of the\nattribute rather than a value of it: there is no initializer, so nothing is\nreflected until something happens.\n\n**The turn writes this once, on a retry nobody refused**, and `handleRetry`\nholds the reason. Everything else that moves it is the consumer's.",
|
|
11874
11874
|
"fieldName": "state"
|
|
11875
11875
|
}
|
|
11876
11876
|
],
|
|
@@ -19257,6 +19257,17 @@
|
|
|
19257
19257
|
"readonly": true,
|
|
19258
19258
|
"description": "Dragging in the square.\n\nThe two ranges are the state, so this reads a point and writes to them\nrather than keeping a position of its own. Pointer capture, so a drag that\nleaves the square keeps going: leaving and coming back is how a reader\nreaches a corner, and without it the handle stops wherever the pointer left."
|
|
19259
19259
|
},
|
|
19260
|
+
{
|
|
19261
|
+
"kind": "method",
|
|
19262
|
+
"name": "sync",
|
|
19263
|
+
"privacy": "private",
|
|
19264
|
+
"return": {
|
|
19265
|
+
"type": {
|
|
19266
|
+
"text": "void"
|
|
19267
|
+
}
|
|
19268
|
+
},
|
|
19269
|
+
"description": "Reports the color to the form.\n\n**The one thing a form-associated element owes, and this element owed it\nfrom the start without paying.** It extends `HelixFormElement`, takes\n`MixinControllerValue` and carries `name`, so a consumer writing\n`<helix-color-picker name=\"brand\">` inside a form has every reason to\nexpect an entry, and the form carried none: no error, nothing in the\nmarkup to look at, and a field silently missing from the submission. Its\ntwo siblings in the same composition position, Color Input and Slider,\nboth report.\n\n`null` rather than an empty string where there is no color, which is the\ndifference between a control that submits nothing and one that submits a\nblank. There is no `setValidity` beside it: this element does not take\n`MixinField`, and every validity member of `ElementInternals` is the\nconsumer's to reach through the field that wraps it."
|
|
19270
|
+
},
|
|
19260
19271
|
{
|
|
19261
19272
|
"kind": "method",
|
|
19262
19273
|
"name": "focus",
|
|
@@ -36529,9 +36540,63 @@
|
|
|
36529
36540
|
"text": "removeAt(at: number) => void"
|
|
36530
36541
|
}
|
|
36531
36542
|
},
|
|
36543
|
+
{
|
|
36544
|
+
"kind": "field",
|
|
36545
|
+
"name": "committed",
|
|
36546
|
+
"type": {
|
|
36547
|
+
"text": "File[]"
|
|
36548
|
+
},
|
|
36549
|
+
"privacy": "private",
|
|
36550
|
+
"default": "[]",
|
|
36551
|
+
"description": "The files this element last stood behind, so a refusal has somewhere to go.\n\nThe picker and a drop both write the input before anything here runs, so\nthe list being replaced is gone by the time the request is announced.\nNothing else records it: `files` reads the control, which is already the\nnew answer."
|
|
36552
|
+
},
|
|
36532
36553
|
{
|
|
36533
36554
|
"kind": "method",
|
|
36534
|
-
"name": "
|
|
36555
|
+
"name": "commit",
|
|
36556
|
+
"privacy": "private",
|
|
36557
|
+
"return": {
|
|
36558
|
+
"type": {
|
|
36559
|
+
"text": "void"
|
|
36560
|
+
}
|
|
36561
|
+
},
|
|
36562
|
+
"parameters": [
|
|
36563
|
+
{
|
|
36564
|
+
"name": "next",
|
|
36565
|
+
"type": {
|
|
36566
|
+
"text": "File[]"
|
|
36567
|
+
}
|
|
36568
|
+
}
|
|
36569
|
+
],
|
|
36570
|
+
"description": "Asks for a set of files, and puts them in unless a listener refuses.\n\n**The request was announced after the fact and nothing read it.** This\nelement dispatched `helix-before-change` from `announce`, after the input\nhad taken the files and after `setFormValue` had reported them, and\ndiscarded what the dispatch returned: an `@event` line promising that\nrefusing it left nothing moved, over a veto with no reader. The other\ndirection of that same fault is what `requestChange` exists to prevent, so\nthis goes through it like every other value control.\n\n**A refusal puts the input back rather than stopping an assignment**, which\nis the shape a file input forces. Two of the four paths in are the browser\nwriting `control.files` before this element hears anything, so there is no\nassignment left to skip and the honest answer is to restore the list the\nelement last stood behind."
|
|
36571
|
+
},
|
|
36572
|
+
{
|
|
36573
|
+
"kind": "method",
|
|
36574
|
+
"name": "put",
|
|
36575
|
+
"privacy": "private",
|
|
36576
|
+
"return": {
|
|
36577
|
+
"type": {
|
|
36578
|
+
"text": "void"
|
|
36579
|
+
}
|
|
36580
|
+
},
|
|
36581
|
+
"parameters": [
|
|
36582
|
+
{
|
|
36583
|
+
"name": "control",
|
|
36584
|
+
"type": {
|
|
36585
|
+
"text": "HTMLInputElement"
|
|
36586
|
+
}
|
|
36587
|
+
},
|
|
36588
|
+
{
|
|
36589
|
+
"name": "files",
|
|
36590
|
+
"type": {
|
|
36591
|
+
"text": "File[]"
|
|
36592
|
+
}
|
|
36593
|
+
}
|
|
36594
|
+
],
|
|
36595
|
+
"description": "Writes a list onto the input, which is the only way a `FileList` is built.\n\n`value = ''` for the empty case rather than an empty `DataTransfer`,\nbecause emptying is the one write a control accepts everywhere and a\nharness without `DataTransfer` still has to be able to clear."
|
|
36596
|
+
},
|
|
36597
|
+
{
|
|
36598
|
+
"kind": "method",
|
|
36599
|
+
"name": "settle",
|
|
36535
36600
|
"privacy": "private",
|
|
36536
36601
|
"return": {
|
|
36537
36602
|
"type": {
|
|
@@ -36611,7 +36676,7 @@
|
|
|
36611
36676
|
"text": "void"
|
|
36612
36677
|
}
|
|
36613
36678
|
},
|
|
36614
|
-
"description": "Empties it, which is what a form reset means for a file input.",
|
|
36679
|
+
"description": "Empties it, which is what a form reset means for a file input.\n\nNot through `commit`, and that is the point of the split: a reset is the\nform's decision and there is nothing here for a listener to refuse. What it\nowes is the report and the platform's own announcement, which is what\n`settle` is.",
|
|
36615
36680
|
"type": {
|
|
36616
36681
|
"text": "formResetCallback() => void"
|
|
36617
36682
|
}
|
|
@@ -37353,7 +37418,7 @@
|
|
|
37353
37418
|
"type": {
|
|
37354
37419
|
"text": "HelixBeforeChangeEvent"
|
|
37355
37420
|
},
|
|
37356
|
-
"description": "Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and
|
|
37421
|
+
"description": "Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included.",
|
|
37357
37422
|
"name": "helix-before-change",
|
|
37358
37423
|
"reactProp": "onhelix-before-change"
|
|
37359
37424
|
},
|
|
@@ -38128,7 +38193,7 @@
|
|
|
38128
38193
|
"helix": {
|
|
38129
38194
|
"category": "forms",
|
|
38130
38195
|
"since": "0.23.0",
|
|
38131
|
-
"status": "
|
|
38196
|
+
"status": "stable",
|
|
38132
38197
|
"documentation": "https://fusion.dev/helix/components/filter-input",
|
|
38133
38198
|
"aliases": [
|
|
38134
38199
|
"FilterToken",
|
|
@@ -38596,7 +38661,7 @@
|
|
|
38596
38661
|
"helix": {
|
|
38597
38662
|
"category": "forms",
|
|
38598
38663
|
"since": "0.23.0",
|
|
38599
|
-
"status": "
|
|
38664
|
+
"status": "stable",
|
|
38600
38665
|
"documentation": "https://fusion.dev/helix/components/filter-input",
|
|
38601
38666
|
"aliases": [
|
|
38602
38667
|
"Facet",
|
|
@@ -39325,7 +39390,7 @@
|
|
|
39325
39390
|
"helix": {
|
|
39326
39391
|
"category": "forms",
|
|
39327
39392
|
"since": "0.25.0",
|
|
39328
|
-
"status": "
|
|
39393
|
+
"status": "stable",
|
|
39329
39394
|
"documentation": "https://fusion.dev/helix/components/filter-input",
|
|
39330
39395
|
"aliases": [
|
|
39331
39396
|
"FacetPicker",
|
|
@@ -39789,7 +39854,7 @@
|
|
|
39789
39854
|
"helix": {
|
|
39790
39855
|
"category": "forms",
|
|
39791
39856
|
"since": "0.23.0",
|
|
39792
|
-
"status": "
|
|
39857
|
+
"status": "stable",
|
|
39793
39858
|
"documentation": "https://fusion.dev/helix/components/filter-input",
|
|
39794
39859
|
"aliases": [
|
|
39795
39860
|
"Comparator",
|
|
@@ -41812,7 +41877,7 @@
|
|
|
41812
41877
|
"helix": {
|
|
41813
41878
|
"category": "forms",
|
|
41814
41879
|
"since": "0.1.0",
|
|
41815
|
-
"status": "
|
|
41880
|
+
"status": "stable",
|
|
41816
41881
|
"documentation": "https://fusion.dev/helix/components/filter-input",
|
|
41817
41882
|
"aliases": [
|
|
41818
41883
|
"SearchField",
|
|
@@ -62513,6 +62578,17 @@
|
|
|
62513
62578
|
"description": "The heading over the run.",
|
|
62514
62579
|
"attribute": "label"
|
|
62515
62580
|
},
|
|
62581
|
+
{
|
|
62582
|
+
"kind": "method",
|
|
62583
|
+
"name": "nameFromHeading",
|
|
62584
|
+
"privacy": "private",
|
|
62585
|
+
"return": {
|
|
62586
|
+
"type": {
|
|
62587
|
+
"text": "void"
|
|
62588
|
+
}
|
|
62589
|
+
},
|
|
62590
|
+
"description": "Names the run after the heading it drew, whatever the heading is made of.\n\n**The string alone was not the name of the heading, it was the name of one\nway of writing it.** `label` reached `ariaLabel` and the slot reached the\nscreen, so a consumer who took the documented `label` slot for a heading\nricher than a string got a `role=\"group\"` with no accessible name at all,\nand the words they wrote were the one thing a reader could not hear: the\nheading carries `aria-hidden`, because the name was supposed to be on the\nhost already. Set both and they disagreed, the host saying the string and\nthe panel showing the slot.\n\nSo the host points at the heading, which is the one thing that is always\nwhat a reader sees. `ElementInternals` and not an IDREF, because the host\nis in the light DOM and the heading is in this shadow root, which is the\nboundary `aria-labelledby` cannot cross. `MixinField.linkHost` does the\nsame for a field's label and records the measurement.\n\nClient only. Element references need the nodes to exist, and the server has\nthe string."
|
|
62591
|
+
},
|
|
62516
62592
|
{
|
|
62517
62593
|
"kind": "field",
|
|
62518
62594
|
"name": "tier",
|
|
@@ -62856,7 +62932,7 @@
|
|
|
62856
62932
|
"summary": "A named run of options inside a listbox.",
|
|
62857
62933
|
"helix": {
|
|
62858
62934
|
"since": "0.1.0",
|
|
62859
|
-
"status": "
|
|
62935
|
+
"status": "stable",
|
|
62860
62936
|
"documentation": "https://fusion.dev/helix/components/option",
|
|
62861
62937
|
"aliases": [
|
|
62862
62938
|
"OptGroup",
|
|
@@ -63532,7 +63608,7 @@
|
|
|
63532
63608
|
"helix": {
|
|
63533
63609
|
"category": "forms",
|
|
63534
63610
|
"since": "0.1.0",
|
|
63535
|
-
"status": "
|
|
63611
|
+
"status": "stable",
|
|
63536
63612
|
"documentation": "https://fusion.dev/helix/components/option",
|
|
63537
63613
|
"aliases": [
|
|
63538
63614
|
"Item",
|
|
@@ -70345,7 +70421,7 @@
|
|
|
70345
70421
|
"name": "handleSelection",
|
|
70346
70422
|
"privacy": "private",
|
|
70347
70423
|
"readonly": true,
|
|
70348
|
-
"description": "Announces before it applies, so a listener can refuse the change.\n\nThe item has not moved by the time this runs, unlike a native input, so\nrefusing is simply not writing the value: there is nothing to put back."
|
|
70424
|
+
"description": "Announces before it applies, so a listener can refuse the change.\n\nThe item has not moved by the time this runs, unlike a native input, so\nrefusing is simply not writing the value: there is nothing to put back.\n\n**The radio's own `helix-select` is a request, and the argument that it was\na notification is the one this settles.** That argument ran: the group's\nveto is `helix-before-change` a step later, so the item could be read as\nannouncing a choice the group then decides about. What it leaves out is who\ncan reach which event. `helix-before-change` is dispatched on the group, so\nrefusing it needs a listener on the group, and a consumer ruling out one\nanswer binds to that answer. Two gates in two positions is the shape, and\nthe item's is the one that was doing nothing."
|
|
70349
70425
|
},
|
|
70350
70426
|
{
|
|
70351
70427
|
"kind": "method",
|
|
@@ -71390,7 +71466,7 @@
|
|
|
71390
71466
|
"summary": "A set of choices where exactly one is the answer.",
|
|
71391
71467
|
"helix": {
|
|
71392
71468
|
"since": "0.1.0",
|
|
71393
|
-
"status": "
|
|
71469
|
+
"status": "stable",
|
|
71394
71470
|
"documentation": "https://fusion.dev/helix/components/radio",
|
|
71395
71471
|
"aliases": [
|
|
71396
71472
|
"RadioButtons",
|
|
@@ -71504,6 +71580,15 @@
|
|
|
71504
71580
|
},
|
|
71505
71581
|
"description": "Whether the markup declared this one chosen, which is what a reset returns to.\n\n`<option selected>` reflects to `defaultSelected` and `option.selected` is\nlive state that does not reflect, and those are two different facts. This\nelement has one `selected` doing both jobs, so the declaration is the\nattribute and the state is the property.\n\n**The attribute, read when it is asked for, and never a snapshot taken in\nthe constructor.** A field initializer runs there, and an element the HTML\nparser created has none of its attributes yet at that moment: measured on\nthis element, on `Option` and on `Toggle`, `<helix-radio value=\"a\"\nselected>` reported `defaultSelected` as `false` both from parsed markup\nand from `createElement` followed by `setAttribute`, which is every way a\nconsumer writes one. The snapshot could only ever be true for an element\nalready in the document when `customElements.define` ran.\n\nNothing overwrites it either, which is what makes reading it late correct\nrather than merely later: `selected` does not reflect, for the reason\n`willUpdate` records, so the group's writes never reach the attribute and\nwhat it says is what the markup said.\n\n**The deeper half is not this.** State reflecting under the declaration's\nname is the fault `TreeItem` avoids by mirroring to `data-selected`.\nSplitting them means moving what every stylesheet selects on, which is a\nchange of its own rather than a property being added."
|
|
71506
71582
|
},
|
|
71583
|
+
{
|
|
71584
|
+
"kind": "field",
|
|
71585
|
+
"name": "focusable",
|
|
71586
|
+
"type": {
|
|
71587
|
+
"text": "boolean"
|
|
71588
|
+
},
|
|
71589
|
+
"default": "false",
|
|
71590
|
+
"description": "Whether this choice holds the group's tab stop while nothing is chosen.\n\nThe stop rides on the selection, and an unanswered group has no selection,\nso without this every choice is `tabindex=\"-1\"` and the keyboard cannot\nenter the set at all.\n\n**Written by the group, with no attribute, and it reaches the control\nrather than the host.** A `tabindex` on the host is two faults at once: it\nis markup a consumer rendered and their server never sent, which React\nreports as a mismatch and does not patch up, and it puts the tab stop on\nthe element rather than on the `<button role=\"radio\">` inside it, so a\nscreen reader meets an unnamed box where a native group would have given\nthem an unchecked radio. `Toggle.Root` carries the same property for the\nsame reason."
|
|
71591
|
+
},
|
|
71507
71592
|
{
|
|
71508
71593
|
"kind": "field",
|
|
71509
71594
|
"name": "selected",
|
|
@@ -71936,7 +72021,7 @@
|
|
|
71936
72021
|
"helix": {
|
|
71937
72022
|
"category": "forms",
|
|
71938
72023
|
"since": "0.1.0",
|
|
71939
|
-
"status": "
|
|
72024
|
+
"status": "stable",
|
|
71940
72025
|
"documentation": "https://fusion.dev/helix/components/radio",
|
|
71941
72026
|
"aliases": [
|
|
71942
72027
|
"Option",
|
|
@@ -77274,7 +77359,7 @@
|
|
|
77274
77359
|
"helix": {
|
|
77275
77360
|
"category": "navigation",
|
|
77276
77361
|
"since": "0.1.0",
|
|
77277
|
-
"status": "
|
|
77362
|
+
"status": "stable",
|
|
77278
77363
|
"documentation": "https://fusion.dev/helix/components/skip-link",
|
|
77279
77364
|
"aliases": [
|
|
77280
77365
|
"Skip navigation",
|
|
@@ -27,7 +27,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
27
27
|
*
|
|
28
28
|
* @slot - The slides.
|
|
29
29
|
*
|
|
30
|
-
* @event {HelixSelectEvent} helix-select - Fired
|
|
30
|
+
* @event {HelixSelectEvent} helix-select - Fired before the slide in view changes, with the index being asked for. Cancelable: refusing it takes the decision and nothing moves, including a drag, which is put back on the slide the index still holds. See `handleScroll`.
|
|
31
31
|
*
|
|
32
32
|
* @csspart controls - The row of controls.
|
|
33
33
|
* @csspart indicator - One of them, and a way to reach that slide.
|
|
@@ -60,9 +60,19 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
|
|
|
60
60
|
if (nearest === this.index) {
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const applied = requestChange({
|
|
64
|
+
apply: (value) => {
|
|
65
|
+
const previous = this.index;
|
|
66
|
+
this.indexState = value;
|
|
67
|
+
this.requestUpdate('index', previous);
|
|
68
|
+
},
|
|
69
|
+
event: (value) => new HelixSelectEvent({ value: String(value) }),
|
|
70
|
+
host: this,
|
|
71
|
+
next: nearest,
|
|
72
|
+
});
|
|
73
|
+
if (!applied) {
|
|
74
|
+
this.scrollToIndex();
|
|
75
|
+
}
|
|
66
76
|
};
|
|
67
77
|
this.scrollFrame = 0;
|
|
68
78
|
this.stopped = false;
|
|
@@ -35,7 +35,7 @@ export declare const FAILED_LABEL = "Not sent";
|
|
|
35
35
|
* indicator, so slotting nothing is what this has always done.
|
|
36
36
|
* @slot sources - What it was said from: citations, documents, a retrieved page.
|
|
37
37
|
*
|
|
38
|
-
* @event {HelixSendEvent} helix-send - Fired when the reader asks to send a failed message again, and
|
|
38
|
+
* @event {HelixSendEvent} helix-send - Fired when the reader asks to send a failed message again. Cancelable: a retry that stands puts the turn back on its way, and refusing it leaves the failure and the control offering it exactly where they were.
|
|
39
39
|
*
|
|
40
40
|
* @csspart actions - The controls offered on the turn, beside its time.
|
|
41
41
|
* @csspart avatar - The leading slot's box.
|
|
@@ -91,6 +91,9 @@ export declare class HelixHeadlessChatMessage extends HelixElement {
|
|
|
91
91
|
* report, which is what most of a transcript is, and it is the absence of the
|
|
92
92
|
* attribute rather than a value of it: there is no initializer, so nothing is
|
|
93
93
|
* reflected until something happens.
|
|
94
|
+
*
|
|
95
|
+
* **The turn writes this once, on a retry nobody refused**, and `handleRetry`
|
|
96
|
+
* holds the reason. Everything else that moves it is the consumer's.
|
|
94
97
|
*/
|
|
95
98
|
state?: 'failed' | 'sending' | 'sent' | 'streaming';
|
|
96
99
|
/**
|
|
@@ -180,6 +183,16 @@ export declare class HelixHeadlessChatMessage extends HelixElement {
|
|
|
180
183
|
* offering it. A listener that wants to tell them apart has `event.target`,
|
|
181
184
|
* which is the message on a retry and the composer on a first send, and that
|
|
182
185
|
* is a distinction the DOM already makes without a second name for it.
|
|
186
|
+
*
|
|
187
|
+
* **Through `requestChange`, and the turn is what moves.** `helix-send` is
|
|
188
|
+
* cancelable because the composer clears only when nobody refused, and this
|
|
189
|
+
* dispatch had nothing behind it: the reader pressed the control, the turn sat
|
|
190
|
+
* at "Not sent" with the same control still on it, and `preventDefault()`
|
|
191
|
+
* refused a decision nobody here was about to take. A retry that stands is a
|
|
192
|
+
* turn on its way, which is what `sending` means, so that is the default
|
|
193
|
+
* action and refusing it leaves the failure exactly where it was. A consumer
|
|
194
|
+
* who wants something else refuses and assigns `state` themselves, which is
|
|
195
|
+
* how control is taken from every other stateful element in the library.
|
|
183
196
|
*/
|
|
184
197
|
private readonly handleRetry;
|
|
185
198
|
}
|
|
@@ -4,7 +4,7 @@ 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 { customElement, HelixElement, HelixSendEvent, hostAria, relativeTime, slotFilled, TONE_MARKS, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, HelixElement, HelixSendEvent, hostAria, relativeTime, requestChange, slotFilled, TONE_MARKS, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
@@ -24,7 +24,14 @@ let HelixHeadlessChatMessage = class HelixHeadlessChatMessage extends HelixEleme
|
|
|
24
24
|
};
|
|
25
25
|
this.handleRetry = (event) => {
|
|
26
26
|
event.stopPropagation();
|
|
27
|
-
|
|
27
|
+
requestChange({
|
|
28
|
+
apply: (next) => {
|
|
29
|
+
this.state = next;
|
|
30
|
+
},
|
|
31
|
+
event: () => new HelixSendEvent({ value: this.textContent?.trim() ?? '' }),
|
|
32
|
+
host: this,
|
|
33
|
+
next: 'sending',
|
|
34
|
+
});
|
|
28
35
|
};
|
|
29
36
|
}
|
|
30
37
|
get moment() {
|
|
@@ -23,6 +23,9 @@ let HelixHeadlessCheckboxGroup = class HelixHeadlessCheckboxGroup extends MixinF
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
event.stopImmediatePropagation();
|
|
26
|
+
if (event.defaultPrevented) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
26
29
|
const wanted = event.detail.checked;
|
|
27
30
|
const next = this.boxes
|
|
28
31
|
.filter((box) => (box === changed ? wanted : box.checked))
|
|
@@ -223,6 +223,25 @@ export declare class HelixHeadlessColorPicker extends HelixHeadlessColorPicker_b
|
|
|
223
223
|
*/
|
|
224
224
|
private readonly handleGridPointer;
|
|
225
225
|
protected firstUpdated(changed: import('lit').PropertyValues): void;
|
|
226
|
+
/**
|
|
227
|
+
* Reports the color to the form.
|
|
228
|
+
*
|
|
229
|
+
* **The one thing a form-associated element owes, and this element owed it
|
|
230
|
+
* from the start without paying.** It extends `HelixFormElement`, takes
|
|
231
|
+
* `MixinControllerValue` and carries `name`, so a consumer writing
|
|
232
|
+
* `<helix-color-picker name="brand">` inside a form has every reason to
|
|
233
|
+
* expect an entry, and the form carried none: no error, nothing in the
|
|
234
|
+
* markup to look at, and a field silently missing from the submission. Its
|
|
235
|
+
* two siblings in the same composition position, Color Input and Slider,
|
|
236
|
+
* both report.
|
|
237
|
+
*
|
|
238
|
+
* `null` rather than an empty string where there is no color, which is the
|
|
239
|
+
* difference between a control that submits nothing and one that submits a
|
|
240
|
+
* blank. There is no `setValidity` beside it: this element does not take
|
|
241
|
+
* `MixinField`, and every validity member of `ElementInternals` is the
|
|
242
|
+
* consumer's to reach through the field that wraps it.
|
|
243
|
+
*/
|
|
244
|
+
private sync;
|
|
226
245
|
protected updated(changed: import('lit').PropertyValues): void;
|
|
227
246
|
/** Puts the keyboard on the square, which is the control a reader wants. */
|
|
228
247
|
focus(options?: FocusOptions): void;
|
|
@@ -106,8 +106,12 @@ let HelixHeadlessColorPicker = class HelixHeadlessColorPicker extends MixinContr
|
|
|
106
106
|
super.firstUpdated(changed);
|
|
107
107
|
this.finishHydration();
|
|
108
108
|
}
|
|
109
|
+
sync() {
|
|
110
|
+
this.internals.setFormValue(this.value === '' ? null : this.value);
|
|
111
|
+
}
|
|
109
112
|
updated(changed) {
|
|
110
113
|
super.updated(changed);
|
|
114
|
+
this.sync();
|
|
111
115
|
if (changed.has('value')) {
|
|
112
116
|
if (parseColor(this.value)) {
|
|
113
117
|
this.settled = this.value;
|
|
@@ -35,7 +35,11 @@ let HelixHeadlessDrilldown = class HelixHeadlessDrilldown extends MixinCollectio
|
|
|
35
35
|
}
|
|
36
36
|
if (this.drilldown.hasPage(item)) {
|
|
37
37
|
event.stopPropagation();
|
|
38
|
-
|
|
38
|
+
queueMicrotask(() => {
|
|
39
|
+
if (!event.defaultPrevented) {
|
|
40
|
+
this.enter(item);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
39
43
|
}
|
|
40
44
|
};
|
|
41
45
|
this.handleKeydown = (event) => {
|
|
@@ -28,7 +28,7 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
|
|
|
28
28
|
* @slot trigger - What the reader presses to open the picker.
|
|
29
29
|
*
|
|
30
30
|
* @event {Event} change - Announced once the value has moved.
|
|
31
|
-
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and
|
|
31
|
+
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before the chosen files change, with their names. Cancelable: refusing it takes the decision and the field keeps the files it had, a pick the browser has already written included.
|
|
32
32
|
* @event {Event} input - Announced once the value has moved.
|
|
33
33
|
*
|
|
34
34
|
* @csspart control - The file input itself, which carries the label and validity.
|
|
@@ -129,8 +129,43 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
|
|
|
129
129
|
* picker come back out the same way.
|
|
130
130
|
*/
|
|
131
131
|
removeAt(at: number): void;
|
|
132
|
+
/**
|
|
133
|
+
* The files this element last stood behind, so a refusal has somewhere to go.
|
|
134
|
+
*
|
|
135
|
+
* The picker and a drop both write the input before anything here runs, so
|
|
136
|
+
* the list being replaced is gone by the time the request is announced.
|
|
137
|
+
* Nothing else records it: `files` reads the control, which is already the
|
|
138
|
+
* new answer.
|
|
139
|
+
*/
|
|
140
|
+
private committed;
|
|
141
|
+
/**
|
|
142
|
+
* Asks for a set of files, and puts them in unless a listener refuses.
|
|
143
|
+
*
|
|
144
|
+
* **The request was announced after the fact and nothing read it.** This
|
|
145
|
+
* element dispatched `helix-before-change` from `announce`, after the input
|
|
146
|
+
* had taken the files and after `setFormValue` had reported them, and
|
|
147
|
+
* discarded what the dispatch returned: an `@event` line promising that
|
|
148
|
+
* refusing it left nothing moved, over a veto with no reader. The other
|
|
149
|
+
* direction of that same fault is what `requestChange` exists to prevent, so
|
|
150
|
+
* this goes through it like every other value control.
|
|
151
|
+
*
|
|
152
|
+
* **A refusal puts the input back rather than stopping an assignment**, which
|
|
153
|
+
* is the shape a file input forces. Two of the four paths in are the browser
|
|
154
|
+
* writing `control.files` before this element hears anything, so there is no
|
|
155
|
+
* assignment left to skip and the honest answer is to restore the list the
|
|
156
|
+
* element last stood behind.
|
|
157
|
+
*/
|
|
158
|
+
private commit;
|
|
159
|
+
/**
|
|
160
|
+
* Writes a list onto the input, which is the only way a `FileList` is built.
|
|
161
|
+
*
|
|
162
|
+
* `value = ''` for the empty case rather than an empty `DataTransfer`,
|
|
163
|
+
* because emptying is the one write a control accepts everywhere and a
|
|
164
|
+
* harness without `DataTransfer` still has to be able to clear.
|
|
165
|
+
*/
|
|
166
|
+
private put;
|
|
132
167
|
/** Says what is now chosen, by name, and puts it in the form. */
|
|
133
|
-
private
|
|
168
|
+
private settle;
|
|
134
169
|
/**
|
|
135
170
|
* Reports the files and the validity the host stands behind.
|
|
136
171
|
*
|
|
@@ -148,7 +183,14 @@ export declare class HelixHeadlessFileInput extends HelixHeadlessFileInput_base
|
|
|
148
183
|
protected firstUpdated(changed: PropertyValues): void;
|
|
149
184
|
/** Puts focus on the input, the way a label pointing at one would. */
|
|
150
185
|
focus(options?: FocusOptions): void;
|
|
151
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* Empties it, which is what a form reset means for a file input.
|
|
188
|
+
*
|
|
189
|
+
* Not through `commit`, and that is the point of the split: a reset is the
|
|
190
|
+
* form's decision and there is nothing here for a listener to refuse. What it
|
|
191
|
+
* owes is the report and the platform's own announcement, which is what
|
|
192
|
+
* `settle` is.
|
|
193
|
+
*/
|
|
152
194
|
formResetCallback(): void;
|
|
153
195
|
/**
|
|
154
196
|
* What is known about a chosen file, as tags, behind a seam the styled tier
|
|
@@ -4,7 +4,7 @@ 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, customElement, drawIcon, HelixBeforeChangeEvent, HelixFormElement, MixinField, restoreValue, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, customElement, drawIcon, HelixBeforeChangeEvent, HelixFormElement, MixinField, requestChange, restoreValue, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { trash } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
@@ -44,9 +44,10 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
44
44
|
this.removeLabel = 'Remove';
|
|
45
45
|
this.triggerLabel = 'Choose files';
|
|
46
46
|
this.dropping = false;
|
|
47
|
+
this.committed = [];
|
|
47
48
|
this.handleChange = (event) => {
|
|
48
49
|
event.stopPropagation();
|
|
49
|
-
this.
|
|
50
|
+
this.commit(this.files);
|
|
50
51
|
};
|
|
51
52
|
this.handleDragOver = (event) => {
|
|
52
53
|
if (this.isDisabled) {
|
|
@@ -69,12 +70,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
69
70
|
if (dropped.length === 0) {
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
for (const file of this.multiple ? [...this.files, ...dropped] : dropped.slice(-1)) {
|
|
74
|
-
keeping.items.add(file);
|
|
75
|
-
}
|
|
76
|
-
control.files = keeping.files;
|
|
77
|
-
this.announce();
|
|
73
|
+
this.commit(this.multiple ? [...this.files, ...dropped] : dropped.slice(-1));
|
|
78
74
|
};
|
|
79
75
|
this.handleRemove = (event) => {
|
|
80
76
|
const at = Number(event.currentTarget.dataset.at);
|
|
@@ -90,33 +86,48 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
90
86
|
return this.partNamed('control');
|
|
91
87
|
}
|
|
92
88
|
clear() {
|
|
89
|
+
this.commit([]);
|
|
90
|
+
}
|
|
91
|
+
removeAt(at) {
|
|
92
|
+
this.commit(this.files.filter((_, index) => index !== at));
|
|
93
|
+
}
|
|
94
|
+
commit(next) {
|
|
93
95
|
const control = this.control;
|
|
94
96
|
if (!control) {
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
const previous = this.committed;
|
|
100
|
+
const applied = requestChange({
|
|
101
|
+
apply: (files) => {
|
|
102
|
+
this.put(control, files);
|
|
103
|
+
this.settle();
|
|
104
|
+
},
|
|
105
|
+
event: (files) => new HelixBeforeChangeEvent({ value: files.map((file) => file.name).join(', ') }),
|
|
106
|
+
host: this,
|
|
107
|
+
next,
|
|
108
|
+
});
|
|
109
|
+
if (!applied) {
|
|
110
|
+
this.put(control, previous);
|
|
111
|
+
}
|
|
99
112
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
put(control, files) {
|
|
114
|
+
if (files.length === 0) {
|
|
115
|
+
control.value = '';
|
|
103
116
|
return;
|
|
104
117
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
if (typeof DataTransfer === 'undefined') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const holding = new DataTransfer();
|
|
122
|
+
for (const file of files) {
|
|
123
|
+
holding.items.add(file);
|
|
110
124
|
}
|
|
111
|
-
control.files =
|
|
112
|
-
this.announce();
|
|
125
|
+
control.files = holding.files;
|
|
113
126
|
}
|
|
114
|
-
|
|
127
|
+
settle() {
|
|
128
|
+
this.committed = this.files;
|
|
115
129
|
this.sync();
|
|
116
130
|
this.requestUpdate();
|
|
117
|
-
this.dispatchEvent(new HelixBeforeChangeEvent({
|
|
118
|
-
value: this.files.map((file) => file.name).join(', '),
|
|
119
|
-
}));
|
|
120
131
|
this.announceInput();
|
|
121
132
|
this.announceChange();
|
|
122
133
|
}
|
|
@@ -137,6 +148,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
137
148
|
}
|
|
138
149
|
firstUpdated(changed) {
|
|
139
150
|
super.firstUpdated(changed);
|
|
151
|
+
this.committed = this.files;
|
|
140
152
|
this.sync();
|
|
141
153
|
}
|
|
142
154
|
focus(options) {
|
|
@@ -146,7 +158,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
146
158
|
if (this.control) {
|
|
147
159
|
restoreValue(this.control, '');
|
|
148
160
|
}
|
|
149
|
-
this.
|
|
161
|
+
this.settle();
|
|
150
162
|
}
|
|
151
163
|
renderMeta(file) {
|
|
152
164
|
return html `<${this.tag('tag')} part="size"
|
|
@@ -37,7 +37,7 @@ declare const HelixHeadlessFilterChip_base: (new (...args: any[]) => import("@fu
|
|
|
37
37
|
* gives: what the words mean is the domain's.
|
|
38
38
|
*
|
|
39
39
|
* @since 0.23.0
|
|
40
|
-
* @status
|
|
40
|
+
* @status stable
|
|
41
41
|
* @category forms
|
|
42
42
|
* @aliases FilterToken, FacetChip
|
|
43
43
|
* @documentation https://fusion.dev/helix/components/filter-input
|