@fusion.dev/helix-headless 0.12.3 → 0.13.1

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/types/jsx.d.ts CHANGED
@@ -55,6 +55,7 @@ import type {
55
55
  HelixHeadlessDrilldownItem,
56
56
  HelixHeadlessEmpty,
57
57
  HelixHeadlessFileInput,
58
+ HelixHeadlessFilterInput,
58
59
  HelixHeadlessFormatDate,
59
60
  HelixHeadlessHeatmap,
60
61
  HelixHeadlessIcon,
@@ -2985,6 +2986,9 @@ export type HelixHeadlessComboboxHelixBeforeOpenElementEvent =
2985
2986
  /** `helix-create` event type */
2986
2987
  export type HelixHeadlessComboboxHelixCreateElementEvent =
2987
2988
  HelixHeadlessComboboxElementEvent<HelixCreateEvent>;
2989
+ /** `helix-filter` event type */
2990
+ export type HelixHeadlessComboboxHelixFilterElementEvent =
2991
+ HelixHeadlessComboboxElementEvent<HelixFilterEvent>;
2988
2992
 
2989
2993
  export type HelixHeadlessComboboxProps = {
2990
2994
  /** What the browser may autofill in the text field, as a platform token.
@@ -3134,6 +3138,8 @@ differently: they are one chooser to a consumer. */
3134
3138
  ) => void) | undefined;
3135
3139
  /** Fired when the reader asks for something the list does not hold, and cancelable. */
3136
3140
  "onhelix-create"?: ((e: HelixHeadlessComboboxHelixCreateElementEvent) => void) | undefined;
3141
+ /** Fired as the reader types, carrying the text the list is being narrowed to. Cancelable: refusing it leaves the list whole, and is how a consumer takes the filtering for themselves. */
3142
+ "onhelix-filter"?: ((e: HelixHeadlessComboboxHelixFilterElementEvent) => void) | undefined;
3137
3143
  /** Announced once the value has moved. */
3138
3144
  oninput?: ((e: HelixHeadlessComboboxElementEvent) => void) | undefined;
3139
3145
  /** Says the state moved, carrying `newState` and `oldState`. */
@@ -3287,6 +3293,8 @@ differently: they are one chooser to a consumer. */
3287
3293
  ) => void) | undefined;
3288
3294
  /** Fired when the reader asks for something the list does not hold, and cancelable. */
3289
3295
  "on:helix-create"?: ((e: HelixHeadlessComboboxHelixCreateElementEvent) => void) | undefined;
3296
+ /** Fired as the reader types, carrying the text the list is being narrowed to. Cancelable: refusing it leaves the list whole, and is how a consumer takes the filtering for themselves. */
3297
+ "on:helix-filter"?: ((e: HelixHeadlessComboboxHelixFilterElementEvent) => void) | undefined;
3290
3298
  /** Announced once the value has moved. */
3291
3299
  "on:input"?: ((e: HelixHeadlessComboboxElementEvent) => void) | undefined;
3292
3300
  /** Says the state moved, carrying `newState` and `oldState`. */
@@ -5764,6 +5772,171 @@ Here rather than on each control. */
5764
5772
  textContent?: (string | number) | undefined;
5765
5773
  };
5766
5774
 
5775
+ /** `HelixHeadlessFilterInput` component event */
5776
+ export type HelixHeadlessFilterInputElementEvent<E = Event> = TypedEvent<
5777
+ HelixHeadlessFilterInput,
5778
+ E
5779
+ >;
5780
+ /** `helix-before-change` event type */
5781
+ export type HelixHeadlessFilterInputHelixBeforeChangeElementEvent =
5782
+ HelixHeadlessFilterInputElementEvent<HelixBeforeChangeEvent>;
5783
+
5784
+ export type HelixHeadlessFilterInputProps = {
5785
+ /** Whether the field offers a button that empties it.
5786
+
5787
+ The text only. The tokens are the consumer's markup and this element is not
5788
+ entitled to take them out of the document, which is the same reason
5789
+ `hideOverflow` marks the ones over the cap `hidden` rather than removing
5790
+ them. */
5791
+ clearable?: (HelixHeadlessFilterInput["clearable"]) | undefined;
5792
+ /** What the clear button is called, since an icon has no name of its own. */
5793
+ "clear-label"?: (HelixHeadlessFilterInput["clearLabel"]) | undefined;
5794
+ /** What the clear button is called, since an icon has no name of its own. */
5795
+ clearLabel?: (HelixHeadlessFilterInput["clearLabel"]) | undefined;
5796
+ /** Shown while the field is empty.
5797
+
5798
+ Not a label: it goes when typing starts. */
5799
+ placeholder?: (HelixHeadlessFilterInput["placeholder"]) | undefined;
5800
+ /** How many tokens to show before the rest become a count. */
5801
+ "max-tags"?: (HelixHeadlessFilterInput["maxTags"]) | undefined;
5802
+ /** How many tokens to show before the rest become a count. */
5803
+ maxTags?: (HelixHeadlessFilterInput["maxTags"]) | undefined;
5804
+ /** The value the markup declared, which is where a reset returns to. */
5805
+ "default-value"?: (HelixHeadlessFilterInput["defaultValue"]) | undefined;
5806
+ /** The value the markup declared, which is where a reset returns to. */
5807
+ defaultValue?: (HelixHeadlessFilterInput["defaultValue"]) | undefined;
5808
+ /** What the control holds now, and what its form submits.
5809
+
5810
+ One property, not two, so `input.value` answers the question anyone
5811
+ asking it meant. Writing always applies; `<helix-input value="x">` is the
5812
+ same statement in markup, and `default-value` is how markup seeds a value
5813
+ a reset should return to. */
5814
+ value?: (HelixHeadlessFilterInput["value"]) | undefined;
5815
+ /** What is wrong, in the reader's words rather than the browser's. */
5816
+ feedback?: (HelixHeadlessFilterInput["feedback"]) | undefined;
5817
+ /** Help under the control, shown while there is nothing wrong. */
5818
+ hint?: (HelixHeadlessFilterInput["hint"]) | undefined;
5819
+ /** The accessible name, and the fallback for the `label` slot. */
5820
+ label?: (HelixHeadlessFilterInput["label"]) | undefined;
5821
+ /** Whether the form refuses to submit without it. */
5822
+ required?: (HelixHeadlessFilterInput["required"]) | undefined;
5823
+ /** Whether the control is non-interactive.
5824
+
5825
+ Reflected, because the styled tier selects on it, and mirrored to
5826
+ `aria-disabled` rather than removing the element from the accessibility
5827
+ tree, so a screen reader can still announce that the control exists.
5828
+
5829
+ The mirror itself is in `willUpdate` below, here rather than in each
5830
+ control, because here is where `disabled` is. */
5831
+ disabled?: (HelixHeadlessFilterInput["disabled"]) | undefined;
5832
+ /** Name submitted with the form.
5833
+
5834
+ **Reflected, and it is the exception to "reflect only what CSS selects
5835
+ on".** The platform builds a form's entry list from the `name` *content
5836
+ attribute*, not from the property, so a control named through the property
5837
+ submitted under no name at all and was silently dropped. Measured: two
5838
+ inputs in one form, one named by attribute and one by property, and the
5839
+ form carried a single entry.
5840
+
5841
+ Safe for a React consumer, which is what the rule is protecting: React
5842
+ renders a custom element's props as attributes on the server, so the name
5843
+ is already in the markup being hydrated and reflecting it writes what is
5844
+ there. It is only a change to the markup when a consumer assigns it
5845
+ imperatively later, and nothing is hydrating by then.
5846
+
5847
+ Here rather than on each control. */
5848
+ name?: (HelixHeadlessFilterInput["name"]) | undefined;
5849
+
5850
+ /** Relayed from the input once the reader has finished. */
5851
+ onchange?: ((e: HelixHeadlessFilterInputElementEvent) => void) | undefined;
5852
+ /** Fired as the text changes, and cancelable. */
5853
+ "onhelix-before-change"?: ((
5854
+ e: HelixHeadlessFilterInputHelixBeforeChangeElementEvent,
5855
+ ) => void) | undefined;
5856
+ /** Announced once the text has moved. */
5857
+ oninput?: ((e: HelixHeadlessFilterInputElementEvent) => void) | undefined;
5858
+ };
5859
+
5860
+ export type HelixHeadlessFilterInputSolidJsProps = {
5861
+ /** Whether the field offers a button that empties it.
5862
+
5863
+ The text only. The tokens are the consumer's markup and this element is not
5864
+ entitled to take them out of the document, which is the same reason
5865
+ `hideOverflow` marks the ones over the cap `hidden` rather than removing
5866
+ them. */
5867
+ "prop:clearable"?: (HelixHeadlessFilterInput["clearable"]) | undefined;
5868
+ /** What the clear button is called, since an icon has no name of its own. */
5869
+ "attr:clear-label"?: (HelixHeadlessFilterInput["clearLabel"]) | undefined;
5870
+ /** What the clear button is called, since an icon has no name of its own. */
5871
+ "prop:clearLabel"?: (HelixHeadlessFilterInput["clearLabel"]) | undefined;
5872
+ /** Shown while the field is empty.
5873
+
5874
+ Not a label: it goes when typing starts. */
5875
+ "prop:placeholder"?: (HelixHeadlessFilterInput["placeholder"]) | undefined;
5876
+ /** How many tokens to show before the rest become a count. */
5877
+ "attr:max-tags"?: (HelixHeadlessFilterInput["maxTags"]) | undefined;
5878
+ /** How many tokens to show before the rest become a count. */
5879
+ "prop:maxTags"?: (HelixHeadlessFilterInput["maxTags"]) | undefined;
5880
+ /** The value the markup declared, which is where a reset returns to. */
5881
+ "attr:default-value"?: (HelixHeadlessFilterInput["defaultValue"]) | undefined;
5882
+ /** The value the markup declared, which is where a reset returns to. */
5883
+ "prop:defaultValue"?: (HelixHeadlessFilterInput["defaultValue"]) | undefined;
5884
+ /** What the control holds now, and what its form submits.
5885
+
5886
+ One property, not two, so `input.value` answers the question anyone
5887
+ asking it meant. Writing always applies; `<helix-input value="x">` is the
5888
+ same statement in markup, and `default-value` is how markup seeds a value
5889
+ a reset should return to. */
5890
+ "prop:value"?: (HelixHeadlessFilterInput["value"]) | undefined;
5891
+ /** What is wrong, in the reader's words rather than the browser's. */
5892
+ "prop:feedback"?: (HelixHeadlessFilterInput["feedback"]) | undefined;
5893
+ /** Help under the control, shown while there is nothing wrong. */
5894
+ "prop:hint"?: (HelixHeadlessFilterInput["hint"]) | undefined;
5895
+ /** The accessible name, and the fallback for the `label` slot. */
5896
+ "prop:label"?: (HelixHeadlessFilterInput["label"]) | undefined;
5897
+ /** Whether the form refuses to submit without it. */
5898
+ "prop:required"?: (HelixHeadlessFilterInput["required"]) | undefined;
5899
+ /** Whether the control is non-interactive.
5900
+
5901
+ Reflected, because the styled tier selects on it, and mirrored to
5902
+ `aria-disabled` rather than removing the element from the accessibility
5903
+ tree, so a screen reader can still announce that the control exists.
5904
+
5905
+ The mirror itself is in `willUpdate` below, here rather than in each
5906
+ control, because here is where `disabled` is. */
5907
+ "prop:disabled"?: (HelixHeadlessFilterInput["disabled"]) | undefined;
5908
+ /** Name submitted with the form.
5909
+
5910
+ **Reflected, and it is the exception to "reflect only what CSS selects
5911
+ on".** The platform builds a form's entry list from the `name` *content
5912
+ attribute*, not from the property, so a control named through the property
5913
+ submitted under no name at all and was silently dropped. Measured: two
5914
+ inputs in one form, one named by attribute and one by property, and the
5915
+ form carried a single entry.
5916
+
5917
+ Safe for a React consumer, which is what the rule is protecting: React
5918
+ renders a custom element's props as attributes on the server, so the name
5919
+ is already in the markup being hydrated and reflecting it writes what is
5920
+ there. It is only a change to the markup when a consumer assigns it
5921
+ imperatively later, and nothing is hydrating by then.
5922
+
5923
+ Here rather than on each control. */
5924
+ "prop:name"?: (HelixHeadlessFilterInput["name"]) | undefined;
5925
+ /** Relayed from the input once the reader has finished. */
5926
+ "on:change"?: ((e: HelixHeadlessFilterInputElementEvent) => void) | undefined;
5927
+ /** Fired as the text changes, and cancelable. */
5928
+ "on:helix-before-change"?: ((
5929
+ e: HelixHeadlessFilterInputHelixBeforeChangeElementEvent,
5930
+ ) => void) | undefined;
5931
+ /** Announced once the text has moved. */
5932
+ "on:input"?: ((e: HelixHeadlessFilterInputElementEvent) => void) | undefined;
5933
+
5934
+ /** Set the innerHTML of the element */
5935
+ innerHTML?: (string) | undefined;
5936
+ /** Set the textContent of the element */
5937
+ textContent?: (string | number) | undefined;
5938
+ };
5939
+
5767
5940
  export type HelixHeadlessFormatDateProps = {
5768
5941
  /** The timestamp, as anything `Date` parses.
5769
5942
 
@@ -18274,6 +18447,7 @@ export type CustomElements = {
18274
18447
  * - `helix-before-close`: Fired before it closes. Cancelable: refusing it takes the decision and nothing moves.
18275
18448
  * - `helix-before-open`: Fired before the panel opens. Cancelable: refusing it takes the decision and nothing moves.
18276
18449
  * - `helix-create`: Fired when the reader asks for something the list does not hold, and cancelable.
18450
+ * - `helix-filter`: Fired as the reader types, carrying the text the list is being narrowed to. Cancelable: refusing it leaves the list whole, and is how a consumer takes the filtering for themselves.
18277
18451
  * - `input`: Announced once the value has moved.
18278
18452
  * - `toggle`: Says the state moved, carrying `newState` and `oldState`.
18279
18453
  *
@@ -20332,6 +20506,186 @@ export type CustomElements = {
20332
20506
  HelixHeadlessFileInputProps & BaseProps<HelixHeadlessFileInput> & BaseEvents<HelixHeadlessFileInput>
20333
20507
  >;
20334
20508
 
20509
+ /**
20510
+ * **The tokens are the consumer's own elements, slotted.** What a filter narrows
20511
+ * by belongs to the consumer's model rather than to this element: a facet with a
20512
+ * prefix, a saved query, a range. So they are slotted rather than rendered from
20513
+ * a list of strings, and removing one is a request this element makes rather
20514
+ * than a change it applies. Combobox is the other way round, and that is the
20515
+ * difference between the two: its tokens are a view of a selection it owns,
20516
+ * drawn from options it was handed.
20517
+ *
20518
+ * **`value` is the text being typed, not the tokens**, which is what makes this
20519
+ * a field a consumer can listen to. Every keystroke announces `input` with the
20520
+ * text on the element, so a table narrows as the reader types. The tokens need
20521
+ * no reporting back, being markup the consumer already holds.
20522
+ *
20523
+ * Backspace on an empty field asks the last token to go, through the same
20524
+ * `helix-dismiss` its own remove control fires, so a consumer who handled the
20525
+ * pointer has already handled the keyboard.
20526
+ *
20527
+ * ## Attributes & Properties
20528
+ *
20529
+ * Component attributes and properties that can be applied to the element or by using JavaScript.
20530
+ *
20531
+ * - `clearable`: Whether the field offers a button that empties it.
20532
+ *
20533
+ * The text only. The tokens are the consumer's markup and this element is not
20534
+ * entitled to take them out of the document, which is the same reason
20535
+ * `hideOverflow` marks the ones over the cap `hidden` rather than removing
20536
+ * them.
20537
+ * - `clear-label`/`clearLabel`: What the clear button is called, since an icon has no name of its own.
20538
+ * - `placeholder`: Shown while the field is empty.
20539
+ *
20540
+ * Not a label: it goes when typing starts.
20541
+ * - `max-tags`/`maxTags`: How many tokens to show before the rest become a count.
20542
+ * - `default-value`/`defaultValue`: The value the markup declared, which is where a reset returns to.
20543
+ * - `value`: What the control holds now, and what its form submits.
20544
+ *
20545
+ * One property, not two, so `input.value` answers the question anyone
20546
+ * asking it meant. Writing always applies; `<helix-input value="x">` is the
20547
+ * same statement in markup, and `default-value` is how markup seeds a value
20548
+ * a reset should return to.
20549
+ * - `feedback`: What is wrong, in the reader's words rather than the browser's.
20550
+ * - `hint`: Help under the control, shown while there is nothing wrong.
20551
+ * - `label`: The accessible name, and the fallback for the `label` slot.
20552
+ * - `required`: Whether the form refuses to submit without it.
20553
+ * - `disabled`: Whether the control is non-interactive.
20554
+ *
20555
+ * Reflected, because the styled tier selects on it, and mirrored to
20556
+ * `aria-disabled` rather than removing the element from the accessibility
20557
+ * tree, so a screen reader can still announce that the control exists.
20558
+ *
20559
+ * The mirror itself is in `willUpdate` below, here rather than in each
20560
+ * control, because here is where `disabled` is.
20561
+ * - `name`: Name submitted with the form.
20562
+ *
20563
+ * **Reflected, and it is the exception to "reflect only what CSS selects
20564
+ * on".** The platform builds a form's entry list from the `name` *content
20565
+ * attribute*, not from the property, so a control named through the property
20566
+ * submitted under no name at all and was silently dropped. Measured: two
20567
+ * inputs in one form, one named by attribute and one by property, and the
20568
+ * form carried a single entry.
20569
+ *
20570
+ * Safe for a React consumer, which is what the rule is protecting: React
20571
+ * renders a custom element's props as attributes on the server, so the name
20572
+ * is already in the markup being hydrated and reflecting it writes what is
20573
+ * there. It is only a change to the markup when a consumer assigns it
20574
+ * imperatively later, and nothing is hydrating by then.
20575
+ *
20576
+ * Here rather than on each control.
20577
+ * - `form`: The form this control belongs to, if any. (property only) (readonly)
20578
+ * - `labels`: Labels associated with this control. (property only) (readonly)
20579
+ * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
20580
+ *
20581
+ * **Named `whenSettled` because `settled` is taken four times over**, by
20582
+ * Image, Splitter and both color controls, none of them about transitions. A
20583
+ * base class takes the name every subclass then cannot use.
20584
+ *
20585
+ * **The state it reports cannot be watched, which is why this exists.** A
20586
+ * custom state is deliberately not in the DOM, so there is no
20587
+ * `MutationObserver` equivalent for `ElementInternals.states`. Await this
20588
+ * instead.
20589
+ *
20590
+ * Separate from `updateComplete`, which resolves in the microtask checkpoint
20591
+ * the suppression is still active in. That is the distinction `readyToMove`
20592
+ * in `@fusion.dev/helix-testing` is written around.
20593
+ *
20594
+ * It never resolves where `requestAnimationFrame` never fires, which is a
20595
+ * server, a hidden tab, and happy-dom. Callers that must not hang race it
20596
+ * against a timeout; `readyToMove` does exactly that. (property only) (readonly)
20597
+ *
20598
+ * ## Events
20599
+ *
20600
+ * Events that will be emitted by the component.
20601
+ *
20602
+ * - `change`: Relayed from the input once the reader has finished.
20603
+ * - `helix-before-change`: Fired as the text changes, and cancelable.
20604
+ * - `input`: Announced once the text has moved.
20605
+ *
20606
+ * ## Slots
20607
+ *
20608
+ * Areas where markup can be added to the component.
20609
+ *
20610
+ * - `end`: After the text, for a count or a control.
20611
+ * - `start`: Before the text, for a search icon.
20612
+ * - `tags`: What is already narrowing the results, as `Tag`s.
20613
+ *
20614
+ * ## Methods
20615
+ *
20616
+ * Methods that can be called to access component functionality.
20617
+ *
20618
+ * - `focus(options?: FocusOptions) => void`: Puts the caret in the field, the way a label pointing at one would.
20619
+ * - `requestValue(next: string) => boolean`: Requests a new value, and reports whether the request stood.
20620
+ *
20621
+ * Announces before it acts, so `preventDefault()` on the `helix-before-change` is
20622
+ * a refusal and nothing moves. Nobody refusing means the control updates
20623
+ * itself, whether or not a consumer declared `value`: a declared property
20624
+ * is a seed and a correction, the way `<input value>` is, and a consumer
20625
+ * who wants the change for themselves refuses it and assigns their own.
20626
+ * - `formResetCallback() => void`: Returns the control to the value its markup declared.
20627
+ *
20628
+ * Both halves are dropped, the consumer's and the control's, because a
20629
+ * reset means the form is as it was served. Clearing only the internal one
20630
+ * would leave a control that had been written to stuck at that value.
20631
+ * - `formStateRestoreCallback(state: string | null) => void`: Restores the value when the browser brings a form back.
20632
+ *
20633
+ * Falling back to `defaultValue` rather than to empty, because a restore
20634
+ * with no state is a form the browser has nothing recorded for, which is
20635
+ * the same situation as a fresh one.
20636
+ * - `formDisabledCallback(disabled: boolean) => void`: The platform's notice that an ancestor fieldset changed.
20637
+ *
20638
+ * **The argument is the effective state, and taking it whole is what latched
20639
+ * every control in the library.** The platform calls this for the element's
20640
+ * own `disabled` as well as for a fieldset, and our own arrives here at the
20641
+ * worst possible moment: reflecting `disabled` to the attribute happens
20642
+ * inside Lit's `update()`, the UA fires this synchronously from that write,
20643
+ * and a reactive write during an update is a change-in-update Lit does not
20644
+ * re-render for. So `formDisabled` ran exactly one update behind forever.
20645
+ *
20646
+ * Measured on Slider, logging what `render` saw:
20647
+ *
20648
+ * ```
20649
+ * set true disabled true formDisabled false isDisabled true
20650
+ * set false disabled false formDisabled true isDisabled true <- stale
20651
+ * (no third render)
20652
+ * ```
20653
+ *
20654
+ * The host came out right every time, `disabled` false, `:disabled` not
20655
+ * matching, and the inner control kept the attribute, so a control disabled
20656
+ * once could never be re-enabled. It only shows when something re-enables,
20657
+ * which is the same shape of bug `inheritDisabled` was written for.
20658
+ *
20659
+ * Our own half is already in `this.disabled` and `isDisabled` is an or, so
20660
+ * this records only what the fieldset adds. That also removes the write
20661
+ * entirely on the own-disabled path rather than moving it.
20662
+ *
20663
+ * The one case this cannot see is a control that is disabled itself *and*
20664
+ * inside a disabled fieldset, whose own `disabled` is then removed: the
20665
+ * effective state does not change, so the platform does not call again, and
20666
+ * the fieldset's hold is not recorded. There is no callback to learn it from
20667
+ * and reading `:disabled` here would answer about the attribute we are in the
20668
+ * middle of removing.
20669
+ *
20670
+ * ## CSS Parts
20671
+ *
20672
+ * Custom selectors for styling elements within the component.
20673
+ *
20674
+ * - `clear`: The button that empties the field.
20675
+ * - `control`: The input itself.
20676
+ * - `feedback`: What is wrong with it.
20677
+ * - `field`: The box holding everything.
20678
+ * - `hint`: Help under the field, while nothing is wrong.
20679
+ * - `label`: The question, over the field.
20680
+ * - `rest`: How many tokens did not fit.
20681
+ * - `tags`: The row the tokens sit in.
20682
+ */
20683
+ "helix-headless-filter-input": Partial<
20684
+ HelixHeadlessFilterInputProps &
20685
+ BaseProps<HelixHeadlessFilterInput> &
20686
+ BaseEvents<HelixHeadlessFilterInput>
20687
+ >;
20688
+
20335
20689
  /**
20336
20690
  * A date in a page is almost never wanted as an ISO string, and formatting one
20337
20691
  * in the consumer's own code means every consumer ships a copy of the same
@@ -32493,6 +32847,7 @@ export type CustomElementsSolidJs = {
32493
32847
  * - `helix-before-close`: Fired before it closes. Cancelable: refusing it takes the decision and nothing moves.
32494
32848
  * - `helix-before-open`: Fired before the panel opens. Cancelable: refusing it takes the decision and nothing moves.
32495
32849
  * - `helix-create`: Fired when the reader asks for something the list does not hold, and cancelable.
32850
+ * - `helix-filter`: Fired as the reader types, carrying the text the list is being narrowed to. Cancelable: refusing it leaves the list whole, and is how a consumer takes the filtering for themselves.
32496
32851
  * - `input`: Announced once the value has moved.
32497
32852
  * - `toggle`: Says the state moved, carrying `newState` and `oldState`.
32498
32853
  *
@@ -34585,6 +34940,187 @@ export type CustomElementsSolidJs = {
34585
34940
  BaseEvents<HelixHeadlessFileInput>
34586
34941
  >;
34587
34942
 
34943
+ /**
34944
+ * **The tokens are the consumer's own elements, slotted.** What a filter narrows
34945
+ * by belongs to the consumer's model rather than to this element: a facet with a
34946
+ * prefix, a saved query, a range. So they are slotted rather than rendered from
34947
+ * a list of strings, and removing one is a request this element makes rather
34948
+ * than a change it applies. Combobox is the other way round, and that is the
34949
+ * difference between the two: its tokens are a view of a selection it owns,
34950
+ * drawn from options it was handed.
34951
+ *
34952
+ * **`value` is the text being typed, not the tokens**, which is what makes this
34953
+ * a field a consumer can listen to. Every keystroke announces `input` with the
34954
+ * text on the element, so a table narrows as the reader types. The tokens need
34955
+ * no reporting back, being markup the consumer already holds.
34956
+ *
34957
+ * Backspace on an empty field asks the last token to go, through the same
34958
+ * `helix-dismiss` its own remove control fires, so a consumer who handled the
34959
+ * pointer has already handled the keyboard.
34960
+ *
34961
+ * ## Attributes & Properties
34962
+ *
34963
+ * Component attributes and properties that can be applied to the element or by using JavaScript.
34964
+ *
34965
+ * - `clearable`: Whether the field offers a button that empties it.
34966
+ *
34967
+ * The text only. The tokens are the consumer's markup and this element is not
34968
+ * entitled to take them out of the document, which is the same reason
34969
+ * `hideOverflow` marks the ones over the cap `hidden` rather than removing
34970
+ * them.
34971
+ * - `clear-label`/`clearLabel`: What the clear button is called, since an icon has no name of its own.
34972
+ * - `placeholder`: Shown while the field is empty.
34973
+ *
34974
+ * Not a label: it goes when typing starts.
34975
+ * - `max-tags`/`maxTags`: How many tokens to show before the rest become a count.
34976
+ * - `default-value`/`defaultValue`: The value the markup declared, which is where a reset returns to.
34977
+ * - `value`: What the control holds now, and what its form submits.
34978
+ *
34979
+ * One property, not two, so `input.value` answers the question anyone
34980
+ * asking it meant. Writing always applies; `<helix-input value="x">` is the
34981
+ * same statement in markup, and `default-value` is how markup seeds a value
34982
+ * a reset should return to.
34983
+ * - `feedback`: What is wrong, in the reader's words rather than the browser's.
34984
+ * - `hint`: Help under the control, shown while there is nothing wrong.
34985
+ * - `label`: The accessible name, and the fallback for the `label` slot.
34986
+ * - `required`: Whether the form refuses to submit without it.
34987
+ * - `disabled`: Whether the control is non-interactive.
34988
+ *
34989
+ * Reflected, because the styled tier selects on it, and mirrored to
34990
+ * `aria-disabled` rather than removing the element from the accessibility
34991
+ * tree, so a screen reader can still announce that the control exists.
34992
+ *
34993
+ * The mirror itself is in `willUpdate` below, here rather than in each
34994
+ * control, because here is where `disabled` is.
34995
+ * - `name`: Name submitted with the form.
34996
+ *
34997
+ * **Reflected, and it is the exception to "reflect only what CSS selects
34998
+ * on".** The platform builds a form's entry list from the `name` *content
34999
+ * attribute*, not from the property, so a control named through the property
35000
+ * submitted under no name at all and was silently dropped. Measured: two
35001
+ * inputs in one form, one named by attribute and one by property, and the
35002
+ * form carried a single entry.
35003
+ *
35004
+ * Safe for a React consumer, which is what the rule is protecting: React
35005
+ * renders a custom element's props as attributes on the server, so the name
35006
+ * is already in the markup being hydrated and reflecting it writes what is
35007
+ * there. It is only a change to the markup when a consumer assigns it
35008
+ * imperatively later, and nothing is hydrating by then.
35009
+ *
35010
+ * Here rather than on each control.
35011
+ * - `form`: The form this control belongs to, if any. (property only) (readonly)
35012
+ * - `labels`: Labels associated with this control. (property only) (readonly)
35013
+ * - `whenSettled`: Resolves a frame after the first update, when transitions are switched on.
35014
+ *
35015
+ * **Named `whenSettled` because `settled` is taken four times over**, by
35016
+ * Image, Splitter and both color controls, none of them about transitions. A
35017
+ * base class takes the name every subclass then cannot use.
35018
+ *
35019
+ * **The state it reports cannot be watched, which is why this exists.** A
35020
+ * custom state is deliberately not in the DOM, so there is no
35021
+ * `MutationObserver` equivalent for `ElementInternals.states`. Await this
35022
+ * instead.
35023
+ *
35024
+ * Separate from `updateComplete`, which resolves in the microtask checkpoint
35025
+ * the suppression is still active in. That is the distinction `readyToMove`
35026
+ * in `@fusion.dev/helix-testing` is written around.
35027
+ *
35028
+ * It never resolves where `requestAnimationFrame` never fires, which is a
35029
+ * server, a hidden tab, and happy-dom. Callers that must not hang race it
35030
+ * against a timeout; `readyToMove` does exactly that. (property only) (readonly)
35031
+ *
35032
+ * ## Events
35033
+ *
35034
+ * Events that will be emitted by the component.
35035
+ *
35036
+ * - `change`: Relayed from the input once the reader has finished.
35037
+ * - `helix-before-change`: Fired as the text changes, and cancelable.
35038
+ * - `input`: Announced once the text has moved.
35039
+ *
35040
+ * ## Slots
35041
+ *
35042
+ * Areas where markup can be added to the component.
35043
+ *
35044
+ * - `end`: After the text, for a count or a control.
35045
+ * - `start`: Before the text, for a search icon.
35046
+ * - `tags`: What is already narrowing the results, as `Tag`s.
35047
+ *
35048
+ * ## Methods
35049
+ *
35050
+ * Methods that can be called to access component functionality.
35051
+ *
35052
+ * - `focus(options?: FocusOptions) => void`: Puts the caret in the field, the way a label pointing at one would.
35053
+ * - `requestValue(next: string) => boolean`: Requests a new value, and reports whether the request stood.
35054
+ *
35055
+ * Announces before it acts, so `preventDefault()` on the `helix-before-change` is
35056
+ * a refusal and nothing moves. Nobody refusing means the control updates
35057
+ * itself, whether or not a consumer declared `value`: a declared property
35058
+ * is a seed and a correction, the way `<input value>` is, and a consumer
35059
+ * who wants the change for themselves refuses it and assigns their own.
35060
+ * - `formResetCallback() => void`: Returns the control to the value its markup declared.
35061
+ *
35062
+ * Both halves are dropped, the consumer's and the control's, because a
35063
+ * reset means the form is as it was served. Clearing only the internal one
35064
+ * would leave a control that had been written to stuck at that value.
35065
+ * - `formStateRestoreCallback(state: string | null) => void`: Restores the value when the browser brings a form back.
35066
+ *
35067
+ * Falling back to `defaultValue` rather than to empty, because a restore
35068
+ * with no state is a form the browser has nothing recorded for, which is
35069
+ * the same situation as a fresh one.
35070
+ * - `formDisabledCallback(disabled: boolean) => void`: The platform's notice that an ancestor fieldset changed.
35071
+ *
35072
+ * **The argument is the effective state, and taking it whole is what latched
35073
+ * every control in the library.** The platform calls this for the element's
35074
+ * own `disabled` as well as for a fieldset, and our own arrives here at the
35075
+ * worst possible moment: reflecting `disabled` to the attribute happens
35076
+ * inside Lit's `update()`, the UA fires this synchronously from that write,
35077
+ * and a reactive write during an update is a change-in-update Lit does not
35078
+ * re-render for. So `formDisabled` ran exactly one update behind forever.
35079
+ *
35080
+ * Measured on Slider, logging what `render` saw:
35081
+ *
35082
+ * ```
35083
+ * set true disabled true formDisabled false isDisabled true
35084
+ * set false disabled false formDisabled true isDisabled true <- stale
35085
+ * (no third render)
35086
+ * ```
35087
+ *
35088
+ * The host came out right every time, `disabled` false, `:disabled` not
35089
+ * matching, and the inner control kept the attribute, so a control disabled
35090
+ * once could never be re-enabled. It only shows when something re-enables,
35091
+ * which is the same shape of bug `inheritDisabled` was written for.
35092
+ *
35093
+ * Our own half is already in `this.disabled` and `isDisabled` is an or, so
35094
+ * this records only what the fieldset adds. That also removes the write
35095
+ * entirely on the own-disabled path rather than moving it.
35096
+ *
35097
+ * The one case this cannot see is a control that is disabled itself *and*
35098
+ * inside a disabled fieldset, whose own `disabled` is then removed: the
35099
+ * effective state does not change, so the platform does not call again, and
35100
+ * the fieldset's hold is not recorded. There is no callback to learn it from
35101
+ * and reading `:disabled` here would answer about the attribute we are in the
35102
+ * middle of removing.
35103
+ *
35104
+ * ## CSS Parts
35105
+ *
35106
+ * Custom selectors for styling elements within the component.
35107
+ *
35108
+ * - `clear`: The button that empties the field.
35109
+ * - `control`: The input itself.
35110
+ * - `feedback`: What is wrong with it.
35111
+ * - `field`: The box holding everything.
35112
+ * - `hint`: Help under the field, while nothing is wrong.
35113
+ * - `label`: The question, over the field.
35114
+ * - `rest`: How many tokens did not fit.
35115
+ * - `tags`: The row the tokens sit in.
35116
+ */
35117
+ "helix-headless-filter-input": Partial<
35118
+ HelixHeadlessFilterInputProps &
35119
+ HelixHeadlessFilterInputSolidJsProps &
35120
+ BaseProps<HelixHeadlessFilterInput> &
35121
+ BaseEvents<HelixHeadlessFilterInput>
35122
+ >;
35123
+
34588
35124
  /**
34589
35125
  * A date in a page is almost never wanted as an ISO string, and formatting one
34590
35126
  * in the consumer's own code means every consumer ships a copy of the same
package/types/svelte.d.ts CHANGED
@@ -46,6 +46,7 @@ declare module 'svelte/elements' {
46
46
  'helix-headless-drilldown': CustomElements['helix-headless-drilldown']
47
47
  'helix-headless-empty': CustomElements['helix-headless-empty']
48
48
  'helix-headless-file-input': CustomElements['helix-headless-file-input']
49
+ 'helix-headless-filter-input': CustomElements['helix-headless-filter-input']
49
50
  'helix-headless-format-date': CustomElements['helix-headless-format-date']
50
51
  'helix-headless-heatmap': CustomElements['helix-headless-heatmap']
51
52
  'helix-headless-icon-button': CustomElements['helix-headless-icon-button']