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