@reliability-design/web 1.4.8 → 1.4.10
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/CHANGELOG.md +214 -0
- package/custom-elements.json +18600 -0
- package/dist/components/rel-accordion/README.md +9 -2
- package/dist/components/rel-alert/README.md +5 -8
- package/dist/components/rel-autocomplete/README.md +209 -208
- package/dist/components/rel-avatar/README.md +228 -118
- package/dist/components/rel-breadcrumb/README.md +1 -0
- package/dist/components/rel-button/README.md +9 -9
- package/dist/components/rel-calendar/README.md +111 -109
- package/dist/components/rel-cardmedia/README.md +1 -1
- package/dist/components/rel-carousel/README.md +187 -186
- package/dist/components/rel-checkbox/README.md +159 -146
- package/dist/components/rel-chip/README.md +141 -138
- package/dist/components/rel-container/README.md +1 -1
- package/dist/components/rel-date-picker/README.md +221 -200
- package/dist/components/rel-drawer/README.md +4 -4
- package/dist/components/rel-kbd/README.md +188 -141
- package/dist/components/rel-layout/README.md +34 -34
- package/dist/components/rel-menu/README.md +17 -10
- package/dist/components/rel-menubar/README.md +218 -218
- package/dist/components/rel-otp/README.md +4 -8
- package/dist/components/rel-pagination/README.md +141 -100
- package/dist/components/rel-popover/README.md +220 -203
- package/dist/components/rel-radio/README.md +142 -131
- package/dist/components/rel-range-picker/README.md +194 -165
- package/dist/components/rel-resizable/README.md +220 -199
- package/dist/components/rel-segmented-button/README.md +158 -156
- package/dist/components/rel-select/README.md +22 -16
- package/dist/components/rel-sheet/README.md +3 -2
- package/dist/components/rel-signature-pad/README.md +161 -0
- package/dist/components/rel-slider/README.md +140 -131
- package/dist/components/rel-snackbar/README.md +170 -168
- package/dist/components/rel-split-button/README.md +129 -0
- package/dist/components/rel-stepper/README.md +171 -162
- package/dist/components/rel-switch/README.md +122 -117
- package/dist/components/rel-tab/README.md +184 -173
- package/dist/components/rel-textfield/README.md +23 -30
- package/dist/components/rel-time-picker/README.md +188 -164
- package/dist/components/rel-toast/README.md +20 -11
- package/dist/components/rel-tooltip/README.md +5 -4
- package/dist/components/rel-tree/README.md +157 -0
- package/dist/components/rel-upload/README.md +8 -5
- package/dist/index.d.mts +1698 -607
- package/dist/index.d.ts +1698 -607
- package/dist/index.js +12276 -6347
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12218 -6296
- package/dist/index.mjs.map +1 -1
- package/dist/styles/motion.css +31 -0
- package/dist/styles/motion.tokens.json +38 -0
- package/dist/styles/vars.css +88 -64
- package/dist/{vars-DXX6QOAG.css → vars-E22LYFMV.css} +88 -64
- package/package.json +60 -37
- package/dist/styles/focus-ring.ts +0 -84
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
<rel-icon slot="trailing-icon" name="plus"></rel-icon>
|
|
40
40
|
<p>Extra content…</p>
|
|
41
41
|
</rel-accordion>
|
|
42
|
+
|
|
43
|
+
<!-- content-type="custom": your own layout. No body padding, no body type or colour, 8px radius -->
|
|
44
|
+
<rel-accordion title="Line items" content-type="custom">
|
|
45
|
+
<table>…</table>
|
|
46
|
+
</rel-accordion>
|
|
42
47
|
```
|
|
43
48
|
|
|
44
49
|
```js
|
|
@@ -76,7 +81,8 @@ accordion.open = true;
|
|
|
76
81
|
| `title` | `title` | `string` | `"Accordion"` | Header text. Note it is also the native HTML `title` attribute, so browsers show it as a tooltip on hover. |
|
|
77
82
|
| `open` | `open` | `boolean` | `false` | Whether the body is expanded; toggled by the header and reported via `open-change`. Setting it from code does not fire the event. Reflected. |
|
|
78
83
|
| `size` | `size` | `"md" \| "sm" \| "large" \| "medium" \| "small"` | `"md"` | Header scale (Figma "Size"): `large` = 56px header / heading-md title, `medium` = 48px / heading-sm, `small` = 44px / heading-xs (new in 1.4.7). The historical spellings stay supported as aliases: `md` = large (the default), `sm` = medium. Reflected. |
|
|
79
|
-
| `type` | `type` | `"default" \| "action"` | `"default"` | Header layout (Figma "Type"): `default` = title + trailing rotating chevron; `action` = leading icon slot (rotating chevron by default) + static trailing arrow-expansion icon, with the body indented under the title.
|
|
84
|
+
| `type` | `type` | `"default" \| "action"` | `"default"` | Header layout (Figma "Type"): `default` = title + trailing rotating chevron; `action` = leading icon slot (rotating chevron by default) + static trailing arrow-expansion icon, with the body indented under the title. Reflected. |
|
|
85
|
+
| `contentType` | `content-type` | `"text" \| "custom"` | `"text"` | Body treatment (Figma "Content Type"). `text` (default) styles the slotted body as copy: 0 / 16 / 16 / 16px padding, body-md type and the `--rel-text-body` colour. `custom` is for your own layout (a table, a form, a nested card): the body wrapper has no padding, sets no type or colour of its own (the slotted content inherits from the accordion's context), and has an 8px (`--rel-border-radius-sm`) corner radius. With `custom`, `type="action"` does not indent the body. Unknown values fall back to `text`. Reflected. |
|
|
80
86
|
| `appearance` | `appearance` | `"shadow" \| "plain"` | `"shadow"` | Figma "Style" axis — named appearance because `style` collides with the HTML style attribute. `shadow` = 4px-radius card with the shadow-sm effect and no border; `plain` = square corners with a 1px border-subtle stroke and no elevation. Reflected. |
|
|
81
87
|
| `disabled` | `disabled` | `boolean` | `false` | Figma "State=Disabled": dims the whole component to 40% and blocks toggling by pointer and keyboard. The header keeps its fill and stroke — the dulling is opacity alone, as Figma specifies. Reflected. |
|
|
82
88
|
|
|
@@ -125,7 +131,8 @@ accordion.open = true;
|
|
|
125
131
|
- `size` defaults to the legacy spelling `"md"`, which renders the Figma Large (56px) header; `"sm"` renders Medium (48px). The full-word values are the canonical ones — see Deprecated aliases.
|
|
126
132
|
- `title` is also the element's native `title` attribute, so browsers show it as a hover tooltip on the whole component.
|
|
127
133
|
- `open-change` fires only from user toggles (click, Enter, Space). Setting `open` from code updates the UI silently.
|
|
128
|
-
- The body stays in the DOM when closed; it is collapsed with `max-height: 0` / `overflow: hidden` and `inert`. When open
|
|
134
|
+
- The body stays in the DOM when closed; it is collapsed with `max-height: 0` / `overflow: hidden` and `inert`. When open there is no height cap (`max-height: none`): a body of any height is shown in full, and content that grows while open is not clipped. The expand / collapse animation measures the body's `scrollHeight`, animates an inline `max-height` to it, and removes the inline value when the transition ends. Earlier versions capped the open body at 1000px and clipped anything taller.
|
|
135
|
+
- `content-type` defaults to `"text"` (body-md copy, `--rel-text-body`, 0 / 16 / 16 / 16px padding, indented under the title for `type="action"`). `"custom"` (Figma Content Type=Custom) removes the padding and the action indent, sets no type or colour so your content inherits from the accordion's context, and gives the body wrapper (part `content`) an 8px radius. Unknown values are normalised to `"text"`.
|
|
129
136
|
- Rotation on open applies only to the built-in fallback icons (`chevron-down` in the trailing slot for `type="default"`, in the leading slot for `type="action"`). Anything you slot into `icon` or `trailing-icon` stays static, and the Action type's trailing `arrow-expansion` never rotates (Figma audit 1b).
|
|
130
137
|
- `type="action"` indents the open body under the title: 56px at `large`, 48px at `medium` / `small`. The leading icon wrapper is 40px / 18px icon at `large` and 32px / 16px at the other sizes; the trailing icon wrapper follows the same tiers.
|
|
131
138
|
- Header hover / pressed use the `--rel-action-overlay-hover` / `--rel-action-overlay-pressed` tokens. `appearance="shadow"` uses `--rel-shadows-sm` with a transparent border; `appearance="plain"` uses `--rel-border-subtle` with no shadow and zero radius.
|
|
@@ -27,17 +27,14 @@
|
|
|
27
27
|
url-href="https://example.com/release-notes"
|
|
28
28
|
></rel-alert>
|
|
29
29
|
|
|
30
|
-
<!-- Non-dismissible
|
|
31
|
-
<rel-alert id="locked" tone="warning" message1="Action required"></rel-alert>
|
|
30
|
+
<!-- Non-dismissible ("false" switches a default-true boolean off, since 1.5.0) -->
|
|
31
|
+
<rel-alert id="locked" tone="warning" message1="Action required" dismissible="false"></rel-alert>
|
|
32
32
|
|
|
33
33
|
<!-- Listen for dismissal -->
|
|
34
34
|
<rel-alert id="saved" tone="success" message1="Saved"></rel-alert>
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
```js
|
|
38
|
-
// A Lit boolean attribute is true whenever present, so dismissible="false" does NOT work.
|
|
39
|
-
document.getElementById("locked").dismissible = false;
|
|
40
|
-
|
|
41
38
|
const saved = document.getElementById("saved");
|
|
42
39
|
saved.addEventListener("dismiss", () => {
|
|
43
40
|
saved.dismissed; // true — the host is now display: none
|
|
@@ -78,7 +75,7 @@ saved.dismissed = false;
|
|
|
78
75
|
| `message2` | `message2` | `string` | `undefined` | Optional secondary message rendered under the title in body-md at 90% opacity; omitted when unset. |
|
|
79
76
|
| `urlText` | `url-text` | `string` | `undefined` | Text of the inline link rendered under the messages. The link appears only when both `urlText` and `urlHref` are set. |
|
|
80
77
|
| `urlHref` | `url-href` | `string` | `undefined` | `href` of the inline link. The link appears only when both `urlText` and `urlHref` are set. |
|
|
81
|
-
| `dismissible` | `dismissible` | `boolean` | `true` | Shows the dismiss (✕) button. Defaults to true;
|
|
78
|
+
| `dismissible` | `dismissible` | `boolean` | `true` | Shows the dismiss (✕) button. Defaults to true; `dismissible="false"` (or the property set to `false`) hides the button. |
|
|
82
79
|
| `dismissed` | `dismissed` | `boolean` | `false` | True once the alert has been dismissed; hides the host via `:host([dismissed]) { display: none }`. Set it back to `false` to show the alert again. Reflected. |
|
|
83
80
|
|
|
84
81
|
#### Deprecated (still supported — removal targeted for 3.0)
|
|
@@ -138,8 +135,8 @@ saved.dismissed = false;
|
|
|
138
135
|
|
|
139
136
|
- `tone` and `appearance` are independent axes; every tone renders in every appearance. Solid uses inverse text and carries the `--rel-shadows-md` elevation; subtle and outline have no shadow.
|
|
140
137
|
- The status icon is fixed per tone (`info` for neutral and info, `check-circle`, `alert-circle`, `error`) and cannot be replaced or removed — there are no slots.
|
|
141
|
-
- The alert has `min-width: 360px
|
|
138
|
+
- The alert stretches to its container (`display: block`) and has **no minimum width**. It carried a `min-width: 360px` until 1.4.9; that floor was code-invented (Figma's Alert is a fixed 400 with `minWidth` unset on all twelve variants) and stopped the alert fitting a narrower column. Set a floor on the host if you want one.
|
|
142
139
|
- The link renders only when both `url-text` and `url-href` are set; it opens in the same tab (no `target`).
|
|
143
|
-
- `dismissible` and `dismissed` are
|
|
140
|
+
- `dismissible` and `dismissed` are booleans: presence or `""` means true, and since 1.5.0 the value `"false"` means false, so `dismissible="false"` works in markup.
|
|
144
141
|
- `dismissed` only hides the host; the element stays in the DOM and keeps its state. Resetting `dismissed = false` shows it again without re-firing `dismiss`.
|
|
145
142
|
- `resolvedTone` is the tone actually rendered after alias resolution; use it (not `tone` / `type`) if you branch on tone in script.
|
|
@@ -1,208 +1,209 @@
|
|
|
1
|
-
# rel-autocomplete
|
|
2
|
-
|
|
3
|
-
`<rel-autocomplete>` is a searchable single-select type-ahead field. It composes `rel-textfield` for all field chrome (label, sizes, variants, states, prefix icon, helper / error text) and adds a suggestion panel with keyboard navigation, loading and no-results rows, and an optional clear affordance. Two data modes: **local** — set `items` and typing filters them client-side (`filter`); **remote** — set `searchFn` and input is debounced (`debounce-ms`), queries are latest-wins and local filtering is bypassed. Use it for searchable dropdowns and search bars over lists too long for `rel-select`; for a short fixed list use `rel-select`, and for plain free text use `rel-textfield`.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```html
|
|
8
|
-
<!-- Local items (strings) -->
|
|
9
|
-
<rel-autocomplete id="carrier" label="Carrier" prefix-icon="search" placeholder="Start typing" clearable></rel-autocomplete>
|
|
10
|
-
<script>
|
|
11
|
-
const ac = document.getElementById("carrier");
|
|
12
|
-
ac.items = ["FedEx Freight", "UPS Freight", "XPO Logistics", "Estes Express"];
|
|
13
|
-
ac.addEventListener("changed", (e) => console.log(e.detail.value, e.detail.item));
|
|
14
|
-
ac.addEventListener("rel-change", (e) => console.log("typed:", e.detail));
|
|
15
|
-
</script>
|
|
16
|
-
|
|
17
|
-
<!-- Object items with icons, read through display-field / value-field -->
|
|
18
|
-
<rel-autocomplete id="mode" label="Mode" display-field="name" value-field="id"></rel-autocomplete>
|
|
19
|
-
<script>
|
|
20
|
-
document.getElementById("mode").items = [
|
|
21
|
-
{ id: "air", name: "Air", icon: "air" },
|
|
22
|
-
{ id: "sea", name: "Sea", icon: "ship" },
|
|
23
|
-
// dulled, skipped by arrow keys, ignores clicks
|
|
24
|
-
{ id: "rail", name: "Rail", icon: "intermodal", disabled: true },
|
|
25
|
-
];
|
|
26
|
-
</script>
|
|
27
|
-
|
|
28
|
-
<!-- Filtering and limits -->
|
|
29
|
-
<rel-autocomplete label="City" filter="starts-with" max-suggestions="8" min-search-length="2"></rel-autocomplete>
|
|
30
|
-
|
|
31
|
-
<!-- Remote data: debounced, latest-wins -->
|
|
32
|
-
<rel-autocomplete id="remote" label="Customer" debounce-ms="250" loading-text="Searching…" no-results-text="No customers match"></rel-autocomplete>
|
|
33
|
-
<script>
|
|
34
|
-
const remote = document.getElementById("remote");
|
|
35
|
-
remote.searchFn = async (query) => {
|
|
36
|
-
const res = await fetch(`/api/customers?q=${encodeURIComponent(query)}`);
|
|
37
|
-
return res.json(); // (string | object)[]
|
|
38
|
-
};
|
|
39
|
-
remote.addEventListener("rel-search-error", (e) => console.warn(e.detail.query, e.detail.error));
|
|
40
|
-
</script>
|
|
41
|
-
|
|
42
|
-
<!-- Field chrome passthroughs -->
|
|
43
|
-
<rel-autocomplete label="Small underline" size="small" variant="underline"></rel-autocomplete>
|
|
44
|
-
<rel-autocomplete label="Floating label" label-style="floating" helper-text="Pick a carrier"></rel-autocomplete>
|
|
45
|
-
<rel-autocomplete label-style="none" placeholder="Search orders" field-aria-label="Search orders"></rel-autocomplete>
|
|
46
|
-
<rel-autocomplete label="Required" required error error-message="Choose a carrier from the list"></rel-autocomplete>
|
|
47
|
-
<rel-autocomplete label="Read only" value="UPS Freight" readonly></rel-autocomplete>
|
|
48
|
-
<rel-autocomplete label="Disabled" disabled></rel-autocomplete>
|
|
49
|
-
<rel-autocomplete label="Inline width" full-width name="carrier"></rel-autocomplete>
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Deprecated aliases
|
|
53
|
-
|
|
54
|
-
```html
|
|
55
|
-
<!-- floating-label → label-style="floating" (kept permanently as an alias; no console notice) -->
|
|
56
|
-
<rel-autocomplete label="Old spelling" floating-label></rel-autocomplete>
|
|
57
|
-
<rel-autocomplete label="Canonical" label-style="floating"></rel-autocomplete>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
<!-- readme-gen:start -->
|
|
61
|
-
|
|
62
|
-
<!-- Generated from rel-autocomplete.ts — do not edit; run readme-generate.mjs -->
|
|
63
|
-
|
|
64
|
-
## API
|
|
65
|
-
|
|
66
|
-
### Attributes / Properties
|
|
67
|
-
|
|
68
|
-
| Property | Attribute | Type | Default | Description |
|
|
69
|
-
| --- | --- | --- | --- | --- |
|
|
70
|
-
| `items` | `items` | `(string \| Record<string, unknown>)[]` | `[]` | Local (or fallback initial) suggestion list. Ignored while searchFn drives results. Object items are read through `displayField` / `valueField` plus the fixed keys `icon`, `disabled` and `supportingText` (the last renamable via `supporting-field`); `supportingText` renders as a second line in the row and is matched by the filter alongside the label. |
|
|
71
|
-
| `value` | `value` | `string` | `""` | Committed value — the selected item's valueField string ("" = none). |
|
|
72
|
-
| `displayField` | `display-field` | `string` | `"label"` | Object key read for the row label when items are objects. |
|
|
73
|
-
| `valueField` | `value-field` | `string` | `"value"` | Object key read for the committed value when items are objects. |
|
|
74
|
-
| `supportingField` | `supporting-field` | `string` | `"supportingText"` | Object key read for the row's optional second line. Defaults to `supportingText`, the fixed key `rel-select` and `rel-radio` already use; point it elsewhere when your records cannot be reshaped. |
|
|
75
|
-
| `filter` | `filter` | `"contains" \| "starts-with" \| "none"` | `"contains"` | Local filtering mode. Ignored when searchFn is set (results arrive pre-filtered). |
|
|
76
|
-
| `searchFn` | — | `(query: string) => Promise<AutocompleteItem[]>` | `undefined` | Async data source. When set, local filtering is bypassed entirely. Property only. |
|
|
77
|
-
| `debounceMs` | `debounce-ms` | `number` | `300` | Debounce for searchFn calls, in ms. |
|
|
78
|
-
| `minSearchLength` | `min-search-length` | `number` | `1` | Minimum typed characters before the panel opens / searchFn fires. 0 = open on focus. |
|
|
79
|
-
| `maxSuggestions` | `max-suggestions` | `number` | `Infinity` | Optional cap on rendered suggestions (panel scrolls regardless). |
|
|
80
|
-
| `multiple` | `multiple` | `boolean` | `false` | Multi-select mode: rows become checkboxes with a Select-all row, and the committed values live in `selectedValues` rather than `value`. Mirrors `rel-select`'s `multiple` rather than introducing a second pattern. THE TEXT INPUT STAYS A SEARCH BOX. In single mode the input shows the selected label, which is the point of a type-ahead; in multi mode there is no single label to show, and writing a value list into the field would destroy the query the user is typing. `rel-select` can
|
|
81
|
-
| `selectedValues` | `selected-values` | `string[]` | `[]` | Committed values in `multiple` mode (ignored otherwise). Mirrors `rel-select.selectedValues`. |
|
|
82
|
-
| `selectAllLabel` | `select-all-label` | `string` | `"Select all"` | Label of the Select-all row shown at the top of the panel in `multiple` mode. |
|
|
83
|
-
| `clearable` | `clearable` | `boolean` | `false` | Shows a trailing ✕ that clears the text and the committed value. |
|
|
84
|
-
| `loading` | `loading` | `boolean` | `false` | Externally-controlled loading state (internal searchFn loading is automatic). |
|
|
85
|
-
| `noResultsText` | `no-results-text` | `string` | `"No results found"` | Row shown when a query yields nothing. |
|
|
86
|
-
| `loadingText` | `loading-text` | `string` | `"Loading…"` | Text shown next to the spinner while suggestions load. |
|
|
87
|
-
| `label` | `label` | `string` | `undefined` | Visible label, forwarded to the inner `rel-textfield`. Also names the suggestion panel. |
|
|
88
|
-
| `placeholder` | `placeholder` | `string` | `""` | Placeholder of the input, forwarded to the inner `rel-textfield`. |
|
|
89
|
-
| `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Field height — `small` 32px, `medium` 40px, `large` 48px — forwarded to the inner `rel-textfield`. Also drives the suggestion panel's row ramp. Reflected. |
|
|
90
|
-
| `variant` | `variant` | `"default" \| "underline" \| "legend"` | `"default"` | Field chrome (`default` outlined, `underline`, `legend` notched outline), forwarded to the inner `rel-textfield`. |
|
|
91
|
-
| `labelStyle` | `label-style` | `"default" \| "floating" \| "none"` | `undefined` | Label presentation (`default` / `floating` / `none`), forwarded to the inner field. Left `undefined`, the deprecated `floatingLabel` decides. Reflected. |
|
|
92
|
-
| `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the input. Required when `labelStyle="none"`. |
|
|
93
|
-
| `fieldAriaLabelledby` | `field-aria-labelledby` | `string` | `undefined` | IDREF list of external element(s) naming the input. Wins over `fieldAriaLabel`. |
|
|
94
|
-
| `prefixIcon` | `prefix-icon` | `string` | `undefined` | Leading `rel-icon` name in the field's prefix block, e.g. `search`. |
|
|
95
|
-
| `disabled` | `disabled` | `boolean` | `false` | Disables the field; the panel never opens and the clear affordance is hidden. |
|
|
96
|
-
| `readonly` | `readonly` | `boolean` | `false` | Read-only field: focusable but not editable; the panel never opens and the clear affordance is hidden. |
|
|
97
|
-
| `required` | `required` | `boolean` | `false` | Marks the input required (native `required` / `aria-required`, asterisk on the label). |
|
|
98
|
-
| `error` | `error` | `boolean` | `false` | Puts the field in the error state (danger border, `aria-invalid`); pair with `errorMessage`. |
|
|
99
|
-
| `errorMessage` | `error-message` | `string` | `undefined` | Text shown below the field instead of `helperText` while `error` is set. |
|
|
100
|
-
| `helperText` | `helper-text` | `string` | `undefined` | Helper line below the field, linked to the input via `aria-describedby`. |
|
|
101
|
-
| `name` | `name` | `string` | `undefined` | Form field name forwarded to the native input. |
|
|
102
|
-
| `fullWidth` | `full-width` | `boolean` | `true` | Fills the parent width as a block element (default). Unset it to render inline at the content's natural width. |
|
|
103
|
-
|
|
104
|
-
#### Deprecated (still supported — removal targeted for 3.0)
|
|
105
|
-
|
|
106
|
-
| Property | Attribute | Type | Use instead |
|
|
107
|
-
| --- | --- | --- | --- |
|
|
108
|
-
| `floatingLabel` | `floating-label` | `boolean` | Use `labelStyle="floating"`. Kept permanently as an alias. |
|
|
109
|
-
|
|
110
|
-
### Methods
|
|
111
|
-
|
|
112
|
-
| Method | Signature | Description |
|
|
113
|
-
| --- | --- | --- |
|
|
114
|
-
| `focus` | `focus()` | Delegates focus to the inner textfield. |
|
|
115
|
-
|
|
116
|
-
## Events
|
|
117
|
-
|
|
118
|
-
| Event | Detail | Bubbles / composed | Description |
|
|
119
|
-
| --- | --- | --- | --- |
|
|
120
|
-
| `changed` | `{ value: this.selectedValues, item: null } / { value: this.value, item: item ? item.item : null }` | bubbles / composed | Fired when the committed value changes — an item was selected (click, Enter, or an exact text match on Enter / Tab / blur) or the field was cleared. Detail: `{ value, item }`, where `item` is the original string or object, or `null` after a clear. |
|
|
121
|
-
| `rel-change` | `inputText / ""` | bubbles / composed | Fired on every keystroke with the raw input text as the detail (and with `""` after a clear). The committed `value` does not change until an item is selected. |
|
|
122
|
-
| `rel-clear` | — | bubbles / composed | Fired when the clear icon empties a non-empty field (followed by `changed` and `rel-change`). No detail. |
|
|
123
|
-
| `rel-search-error` | `{ query, error: err }` | bubbles / composed | Fired when `searchFn` rejects; the panel then shows the no-results row. Detail: `{ query, error }`. |
|
|
124
|
-
|
|
125
|
-
## Slots
|
|
126
|
-
|
|
127
|
-
| Slot | Description |
|
|
128
|
-
| --- | --- |
|
|
129
|
-
| — | This element has no slots. |
|
|
130
|
-
|
|
131
|
-
## CSS Parts
|
|
132
|
-
|
|
133
|
-
| Part | Description |
|
|
134
|
-
| --- | --- |
|
|
135
|
-
| `chips` | — |
|
|
136
|
-
| `chip` | — |
|
|
137
|
-
| `panel` | The suggestion listbox container (same node as `list`) |
|
|
138
|
-
| `list` | Alias of `panel` |
|
|
139
|
-
| `select-all` | — |
|
|
140
|
-
| `option` | A suggestion row. Emitted in BOTH single and `multiple` mode, unlike the two parts below |
|
|
141
|
-
| `option-label` | The row's first line (the matched label). SINGLE-SELECT ROWS ONLY |
|
|
142
|
-
| `option-supporting-text` | The row's optional second line, rendered only when the item supplies one. SINGLE-SELECT ROWS ONLY — under `multiple` the row is a nested `rel-checkbox` and the second line is that component's own `helper` part, inside its shadow root with nothing forwarding it out, so it cannot be reached with `::part()` at all. Style both modes with the `--rel-*` tokens instead |
|
|
143
|
-
| `label` | The inner field's label (re-exported from `rel-textfield`) |
|
|
144
|
-
| `control` | The inner field's bordered control box (re-exported from `rel-textfield`) |
|
|
145
|
-
| `input` | The inner native input (re-exported from `rel-textfield`) |
|
|
146
|
-
| `helper` | The inner field's helper / error line (re-exported from `rel-textfield`) |
|
|
147
|
-
| `right-icon` | The clear icon shown when `clearable` (re-exported from `rel-textfield`) |
|
|
148
|
-
|
|
149
|
-
## Tokens / CSS Variables
|
|
150
|
-
|
|
151
|
-
### Component hooks
|
|
152
|
-
|
|
153
|
-
Set these on the host (or an ancestor) to customise the element without touching design tokens.
|
|
154
|
-
|
|
155
|
-
| Custom property | Default | Description |
|
|
156
|
-
| --- | --- | --- |
|
|
157
|
-
| `--rel-autocomplete-bg` | `var(--rel-bg-neutral)` | Panel and row background |
|
|
158
|
-
| `--rel-autocomplete-divider` | `transparent` | Hairline between rows |
|
|
159
|
-
| `--rel-autocomplete-icon-color` | `var(--rel-text-default)` | Colour of a row's leading `icon` |
|
|
160
|
-
| `--rel-autocomplete-menu-border` | `var(--rel-border-
|
|
161
|
-
| `--rel-autocomplete-menu-max-height` | `360px` | Scroll ceiling of the suggestion panel; default `360px`. Code-owned rather than Figma-derived — every Figma Menu variant hugs its content with no ceiling, which is not shippable for an unbounded result list. |
|
|
162
|
-
| `--rel-autocomplete-menu-radius` | `var(--rel-border-radius-xs)` | Panel corner radius |
|
|
163
|
-
| `--rel-autocomplete-menu-shadow` | `var(--rel-shadows-md)` | Panel box-shadow |
|
|
164
|
-
| `--rel-autocomplete-option-hover` | `var(--rel-action-overlay-hover)` | Background of the hovered / keyboard-highlighted / selected row |
|
|
165
|
-
| `--rel-autocomplete-option-pressed` | `var(--rel-action-overlay-pressed)` | Background of a row while it is being pressed |
|
|
166
|
-
| `--rel-autocomplete-status-color` | `var(--rel-text-body)` | Colour of the loading / no-results status rows |
|
|
167
|
-
| `--rel-autocomplete-supporting-color` | `var(--rel-text-caption)` | — |
|
|
168
|
-
| `--rel-autocomplete-value-color` | `var(--rel-text-default)` | Suggestion label colour |
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
- The second line
|
|
196
|
-
-
|
|
197
|
-
- `
|
|
198
|
-
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
201
|
-
- `
|
|
202
|
-
- `
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
1
|
+
# rel-autocomplete
|
|
2
|
+
|
|
3
|
+
`<rel-autocomplete>` is a searchable single-select type-ahead field. It composes `rel-textfield` for all field chrome (label, sizes, variants, states, prefix icon, helper / error text) and adds a suggestion panel with keyboard navigation, loading and no-results rows, and an optional clear affordance. Two data modes: **local** — set `items` and typing filters them client-side (`filter`); **remote** — set `searchFn` and input is debounced (`debounce-ms`), queries are latest-wins and local filtering is bypassed. Use it for searchable dropdowns and search bars over lists too long for `rel-select`; for a short fixed list use `rel-select`, and for plain free text use `rel-textfield`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<!-- Local items (strings) -->
|
|
9
|
+
<rel-autocomplete id="carrier" label="Carrier" prefix-icon="search" placeholder="Start typing" clearable></rel-autocomplete>
|
|
10
|
+
<script>
|
|
11
|
+
const ac = document.getElementById("carrier");
|
|
12
|
+
ac.items = ["FedEx Freight", "UPS Freight", "XPO Logistics", "Estes Express"];
|
|
13
|
+
ac.addEventListener("changed", (e) => console.log(e.detail.value, e.detail.item));
|
|
14
|
+
ac.addEventListener("rel-change", (e) => console.log("typed:", e.detail));
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- Object items with icons, read through display-field / value-field -->
|
|
18
|
+
<rel-autocomplete id="mode" label="Mode" display-field="name" value-field="id"></rel-autocomplete>
|
|
19
|
+
<script>
|
|
20
|
+
document.getElementById("mode").items = [
|
|
21
|
+
{ id: "air", name: "Air", icon: "air" },
|
|
22
|
+
{ id: "sea", name: "Sea", icon: "ship" },
|
|
23
|
+
// dulled, skipped by arrow keys, ignores clicks
|
|
24
|
+
{ id: "rail", name: "Rail", icon: "intermodal", disabled: true },
|
|
25
|
+
];
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<!-- Filtering and limits -->
|
|
29
|
+
<rel-autocomplete label="City" filter="starts-with" max-suggestions="8" min-search-length="2"></rel-autocomplete>
|
|
30
|
+
|
|
31
|
+
<!-- Remote data: debounced, latest-wins -->
|
|
32
|
+
<rel-autocomplete id="remote" label="Customer" debounce-ms="250" loading-text="Searching…" no-results-text="No customers match"></rel-autocomplete>
|
|
33
|
+
<script>
|
|
34
|
+
const remote = document.getElementById("remote");
|
|
35
|
+
remote.searchFn = async (query) => {
|
|
36
|
+
const res = await fetch(`/api/customers?q=${encodeURIComponent(query)}`);
|
|
37
|
+
return res.json(); // (string | object)[]
|
|
38
|
+
};
|
|
39
|
+
remote.addEventListener("rel-search-error", (e) => console.warn(e.detail.query, e.detail.error));
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<!-- Field chrome passthroughs -->
|
|
43
|
+
<rel-autocomplete label="Small underline" size="small" variant="underline"></rel-autocomplete>
|
|
44
|
+
<rel-autocomplete label="Floating label" label-style="floating" helper-text="Pick a carrier"></rel-autocomplete>
|
|
45
|
+
<rel-autocomplete label-style="none" placeholder="Search orders" field-aria-label="Search orders"></rel-autocomplete>
|
|
46
|
+
<rel-autocomplete label="Required" required error error-message="Choose a carrier from the list"></rel-autocomplete>
|
|
47
|
+
<rel-autocomplete label="Read only" value="UPS Freight" readonly></rel-autocomplete>
|
|
48
|
+
<rel-autocomplete label="Disabled" disabled></rel-autocomplete>
|
|
49
|
+
<rel-autocomplete label="Inline width" full-width name="carrier"></rel-autocomplete>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Deprecated aliases
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!-- floating-label → label-style="floating" (kept permanently as an alias; no console notice) -->
|
|
56
|
+
<rel-autocomplete label="Old spelling" floating-label></rel-autocomplete>
|
|
57
|
+
<rel-autocomplete label="Canonical" label-style="floating"></rel-autocomplete>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
<!-- readme-gen:start -->
|
|
61
|
+
|
|
62
|
+
<!-- Generated from rel-autocomplete.ts — do not edit; run readme-generate.mjs -->
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### Attributes / Properties
|
|
67
|
+
|
|
68
|
+
| Property | Attribute | Type | Default | Description |
|
|
69
|
+
| --- | --- | --- | --- | --- |
|
|
70
|
+
| `items` | `items` | `(string \| Record<string, unknown>)[]` | `[]` | Local (or fallback initial) suggestion list. Ignored while searchFn drives results. Object items are read through `displayField` / `valueField` plus the fixed keys `icon`, `disabled` and `supportingText` (the last renamable via `supporting-field`); `supportingText` renders as a second line in the row and is matched by the filter alongside the label. |
|
|
71
|
+
| `value` | `value` | `string` | `""` | Committed value — the selected item's valueField string ("" = none). |
|
|
72
|
+
| `displayField` | `display-field` | `string` | `"label"` | Object key read for the row label when items are objects. |
|
|
73
|
+
| `valueField` | `value-field` | `string` | `"value"` | Object key read for the committed value when items are objects. |
|
|
74
|
+
| `supportingField` | `supporting-field` | `string` | `"supportingText"` | Object key read for the row's optional second line. Defaults to `supportingText`, the fixed key `rel-select` and `rel-radio` already use; point it elsewhere when your records cannot be reshaped. |
|
|
75
|
+
| `filter` | `filter` | `"contains" \| "starts-with" \| "none"` | `"contains"` | Local filtering mode. Ignored when searchFn is set (results arrive pre-filtered). |
|
|
76
|
+
| `searchFn` | — | `(query: string) => Promise<AutocompleteItem[]>` | `undefined` | Async data source. When set, local filtering is bypassed entirely. Property only. |
|
|
77
|
+
| `debounceMs` | `debounce-ms` | `number` | `300` | Debounce for searchFn calls, in ms. |
|
|
78
|
+
| `minSearchLength` | `min-search-length` | `number` | `1` | Minimum typed characters before the panel opens / searchFn fires. 0 = open on focus. |
|
|
79
|
+
| `maxSuggestions` | `max-suggestions` | `number` | `Infinity` | Optional cap on rendered suggestions (panel scrolls regardless). |
|
|
80
|
+
| `multiple` | `multiple` | `boolean` | `false` | Multi-select mode: rows become checkboxes with a Select-all row, and the committed values live in `selectedValues` rather than `value`. Mirrors `rel-select`'s `multiple` rather than introducing a second pattern. THE TEXT INPUT STAYS A SEARCH BOX. In single mode the input shows the selected label, which is the point of a type-ahead; in multi mode there is no single label to show, and writing a value list into the field would destroy the query the user is typing. `rel-select` can lay chips over its trigger because a trigger is not an editable surface — an input is. So the chips render beneath the field, the query clears after each toggle so the next search starts fresh, and the panel stays open. Reflected. |
|
|
81
|
+
| `selectedValues` | `selected-values` | `string[]` | `[]` | Committed values in `multiple` mode (ignored otherwise). Mirrors `rel-select.selectedValues`. |
|
|
82
|
+
| `selectAllLabel` | `select-all-label` | `string` | `"Select all"` | Label of the Select-all row shown at the top of the panel in `multiple` mode. |
|
|
83
|
+
| `clearable` | `clearable` | `boolean` | `false` | Shows a trailing ✕ that clears the text and the committed value. |
|
|
84
|
+
| `loading` | `loading` | `boolean` | `false` | Externally-controlled loading state (internal searchFn loading is automatic). |
|
|
85
|
+
| `noResultsText` | `no-results-text` | `string` | `"No results found"` | Row shown when a query yields nothing. |
|
|
86
|
+
| `loadingText` | `loading-text` | `string` | `"Loading…"` | Text shown next to the spinner while suggestions load. |
|
|
87
|
+
| `label` | `label` | `string` | `undefined` | Visible label, forwarded to the inner `rel-textfield`. Also names the suggestion panel. |
|
|
88
|
+
| `placeholder` | `placeholder` | `string` | `""` | Placeholder of the input, forwarded to the inner `rel-textfield`. |
|
|
89
|
+
| `size` | `size` | `"small" \| "medium" \| "large"` | `"medium"` | Field height — `small` 32px, `medium` 40px, `large` 48px — forwarded to the inner `rel-textfield`. Also drives the suggestion panel's row ramp. Reflected. |
|
|
90
|
+
| `variant` | `variant` | `"default" \| "underline" \| "legend"` | `"default"` | Field chrome (`default` outlined, `underline`, `legend` notched outline), forwarded to the inner `rel-textfield`. |
|
|
91
|
+
| `labelStyle` | `label-style` | `"default" \| "floating" \| "none"` | `undefined` | Label presentation (`default` / `floating` / `none`), forwarded to the inner field. Left `undefined`, the deprecated `floatingLabel` decides. Reflected. |
|
|
92
|
+
| `fieldAriaLabel` | `field-aria-label` | `string` | `undefined` | Accessible name for the input. Required when `labelStyle="none"`. |
|
|
93
|
+
| `fieldAriaLabelledby` | `field-aria-labelledby` | `string` | `undefined` | IDREF list of external element(s) naming the input. Wins over `fieldAriaLabel`. |
|
|
94
|
+
| `prefixIcon` | `prefix-icon` | `string` | `undefined` | Leading `rel-icon` name in the field's prefix block, e.g. `search`. |
|
|
95
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables the field; the panel never opens and the clear affordance is hidden. |
|
|
96
|
+
| `readonly` | `readonly` | `boolean` | `false` | Read-only field: focusable but not editable; the panel never opens and the clear affordance is hidden. |
|
|
97
|
+
| `required` | `required` | `boolean` | `false` | Marks the input required (native `required` / `aria-required`, asterisk on the label). |
|
|
98
|
+
| `error` | `error` | `boolean` | `false` | Puts the field in the error state (danger border, `aria-invalid`); pair with `errorMessage`. |
|
|
99
|
+
| `errorMessage` | `error-message` | `string` | `undefined` | Text shown below the field instead of `helperText` while `error` is set. |
|
|
100
|
+
| `helperText` | `helper-text` | `string` | `undefined` | Helper line below the field, linked to the input via `aria-describedby`. |
|
|
101
|
+
| `name` | `name` | `string` | `undefined` | Form field name forwarded to the native input. |
|
|
102
|
+
| `fullWidth` | `full-width` | `boolean` | `true` | Fills the parent width as a block element (default). Unset it to render inline at the content's natural width. |
|
|
103
|
+
|
|
104
|
+
#### Deprecated (still supported — removal targeted for 3.0)
|
|
105
|
+
|
|
106
|
+
| Property | Attribute | Type | Use instead |
|
|
107
|
+
| --- | --- | --- | --- |
|
|
108
|
+
| `floatingLabel` | `floating-label` | `boolean` | Use `labelStyle="floating"`. Kept permanently as an alias. |
|
|
109
|
+
|
|
110
|
+
### Methods
|
|
111
|
+
|
|
112
|
+
| Method | Signature | Description |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `focus` | `focus()` | Delegates focus to the inner textfield. |
|
|
115
|
+
|
|
116
|
+
## Events
|
|
117
|
+
|
|
118
|
+
| Event | Detail | Bubbles / composed | Description |
|
|
119
|
+
| --- | --- | --- | --- |
|
|
120
|
+
| `changed` | `{ value: this.selectedValues, item: null } / { value: this.value, item: item ? item.item : null }` | bubbles / composed | Fired when the committed value changes — an item was selected (click, Enter, or an exact text match on Enter / Tab / blur) or the field was cleared. Detail: `{ value, item }`, where `item` is the original string or object, or `null` after a clear. |
|
|
121
|
+
| `rel-change` | `inputText / ""` | bubbles / composed | Fired on every keystroke with the raw input text as the detail (and with `""` after a clear). The committed `value` does not change until an item is selected. |
|
|
122
|
+
| `rel-clear` | — | bubbles / composed | Fired when the clear icon empties a non-empty field (followed by `changed` and `rel-change`). No detail. |
|
|
123
|
+
| `rel-search-error` | `{ query, error: err }` | bubbles / composed | Fired when `searchFn` rejects; the panel then shows the no-results row. Detail: `{ query, error }`. |
|
|
124
|
+
|
|
125
|
+
## Slots
|
|
126
|
+
|
|
127
|
+
| Slot | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| — | This element has no slots. |
|
|
130
|
+
|
|
131
|
+
## CSS Parts
|
|
132
|
+
|
|
133
|
+
| Part | Description |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `chips` | — |
|
|
136
|
+
| `chip` | — |
|
|
137
|
+
| `panel` | The suggestion listbox container (same node as `list`) |
|
|
138
|
+
| `list` | Alias of `panel` |
|
|
139
|
+
| `select-all` | — |
|
|
140
|
+
| `option` | A suggestion row. Emitted in BOTH single and `multiple` mode, unlike the two parts below |
|
|
141
|
+
| `option-label` | The row's first line (the matched label). SINGLE-SELECT ROWS ONLY |
|
|
142
|
+
| `option-supporting-text` | The row's optional second line, rendered only when the item supplies one. SINGLE-SELECT ROWS ONLY — under `multiple` the row is a nested `rel-checkbox` and the second line is that component's own `helper` part, inside its shadow root with nothing forwarding it out, so it cannot be reached with `::part()` at all. Style both modes with the `--rel-*` tokens instead |
|
|
143
|
+
| `label` | The inner field's label (re-exported from `rel-textfield`) |
|
|
144
|
+
| `control` | The inner field's bordered control box (re-exported from `rel-textfield`) |
|
|
145
|
+
| `input` | The inner native input (re-exported from `rel-textfield`) |
|
|
146
|
+
| `helper` | The inner field's helper / error line (re-exported from `rel-textfield`) |
|
|
147
|
+
| `right-icon` | The clear icon shown when `clearable` (re-exported from `rel-textfield`) |
|
|
148
|
+
|
|
149
|
+
## Tokens / CSS Variables
|
|
150
|
+
|
|
151
|
+
### Component hooks
|
|
152
|
+
|
|
153
|
+
Set these on the host (or an ancestor) to customise the element without touching design tokens.
|
|
154
|
+
|
|
155
|
+
| Custom property | Default | Description |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| `--rel-autocomplete-bg` | `var(--rel-bg-neutral)` | Panel and row background |
|
|
158
|
+
| `--rel-autocomplete-divider` | `transparent` | Hairline between rows |
|
|
159
|
+
| `--rel-autocomplete-icon-color` | `var(--rel-text-default)` | Colour of a row's leading `icon` |
|
|
160
|
+
| `--rel-autocomplete-menu-border` | `var(--rel-border-subtle)` | Panel border colour. The panel is borderless by design and only draws this on dark surfaces, where its shadow alone can't separate it from the page. |
|
|
161
|
+
| `--rel-autocomplete-menu-max-height` | `360px` | Scroll ceiling of the suggestion panel; default `360px`. Code-owned rather than Figma-derived — every Figma Menu variant hugs its content with no ceiling, which is not shippable for an unbounded result list. |
|
|
162
|
+
| `--rel-autocomplete-menu-radius` | `var(--rel-border-radius-xs)` | Panel corner radius |
|
|
163
|
+
| `--rel-autocomplete-menu-shadow` | `var(--rel-shadows-md)` | Panel box-shadow |
|
|
164
|
+
| `--rel-autocomplete-option-hover` | `var(--rel-action-overlay-hover)` | Background of the hovered / keyboard-highlighted / selected row |
|
|
165
|
+
| `--rel-autocomplete-option-pressed` | `var(--rel-action-overlay-pressed)` | Background of a row while it is being pressed |
|
|
166
|
+
| `--rel-autocomplete-status-color` | `var(--rel-text-body)` | Colour of the loading / no-results status rows |
|
|
167
|
+
| `--rel-autocomplete-supporting-color` | `var(--rel-text-caption)` | — |
|
|
168
|
+
| `--rel-autocomplete-value-color` | `var(--rel-text-default)` | Suggestion label colour |
|
|
169
|
+
| `--rel-overlay-fallback-z` | `1001` | z-index of the panel on the **no-Popover-API fallback path only**. Wherever the browser top layer is available the panel is promoted into it and this value is inert, because top-layer elements are not in the page stacking order at all. Shared with the other overlays (select, autocomplete, the three pickers) so the whole family can be moved from one place. |
|
|
170
|
+
|
|
171
|
+
<!-- readme-gen:end -->
|
|
172
|
+
|
|
173
|
+
## Accessibility
|
|
174
|
+
|
|
175
|
+
- Combobox pattern: a wrapper around the inner field carries `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded`, `aria-controls` and `aria-activedescendant` (the highlighted row). The role sits on the wrapper rather than the `<input>` because the input lives in `rel-textfield`'s nested shadow root, which `aria-activedescendant` cannot reference across — the pragmatic pattern for composed shadow components.
|
|
176
|
+
- The panel is `role="listbox"` named by the visible `label` (or `field-aria-label` under `label-style="none"`, falling back to "Suggestions"); rows are `role="option"` with `aria-selected` on the committed value. The loading row is `role="status"`.
|
|
177
|
+
- Field-level semantics come from `rel-textfield`: `<label for>`, `aria-required`, `aria-invalid`, `aria-describedby` on the helper / error line. With `label-style="none"` the inner field logs a console warning once if neither `field-aria-label` nor `field-aria-labelledby` is set.
|
|
178
|
+
- The clear affordance (`clearable`) is the inner field's clickable trailing icon: `role="button"`, focusable, labelled "Clear".
|
|
179
|
+
- Screen-reader announcements via the shared `announce()` helper: `"Loading suggestions"` when the loading row appears, `"{label}, i of n"` while arrowing through rows, `"N results available"` when results change, and the `no-results-text` when a query yields nothing.
|
|
180
|
+
- Disabled and read-only fields never open the panel and hide the clear icon.
|
|
181
|
+
|
|
182
|
+
| Key | Action |
|
|
183
|
+
|-----|--------|
|
|
184
|
+
| Typing | Filters local items or queries `searchFn`, and opens the panel once the query reaches `min-search-length` |
|
|
185
|
+
| `ArrowDown` | Open the panel (if the query is long enough) or move the highlight down |
|
|
186
|
+
| `ArrowUp` | Move the highlight up |
|
|
187
|
+
| `Enter` | Select the highlighted row; with no highlight, commit an exact label / value match, otherwise keep the free text and close |
|
|
188
|
+
| `Escape` | Close the panel without touching the text |
|
|
189
|
+
| `Tab` | Commit an exact match (if any), close, and move focus on |
|
|
190
|
+
|
|
191
|
+
Focus: `focus()` on the host delegates to the inner field. Focusing the field with `min-search-length="0"` (or with a query already long enough and matching items) opens the panel. Focus leaving the component commits an exact match and closes the panel; the same happens on an outside pointerdown. After a clear, focus returns to the field.
|
|
192
|
+
|
|
193
|
+
## Notes / Edge Cases
|
|
194
|
+
|
|
195
|
+
- **The second line is searchable, and that changes which rows appear.** `supportingText` is matched by the filter alongside the label, because the identifiers it usually carries (a DOT or MC number, a code) are the reason to show it at all. An item whose label does not match but whose supporting text does will now appear in the list, and the matched characters are emboldened on whichever line they were found. Set `filter="none"` or drop `supportingText` if you need the previous label-only behaviour.
|
|
196
|
+
- The second line truncates with an ellipsis rather than wrapping — the panel is pinned to the field's width, and a suggestion row is a fixed-height scanning target. Rows grow from 32 / 40 / 48 to roughly 46 / 58 / 70 when an item supplies one; a row without `supportingText` is unchanged. This holds in `multiple` mode too (the nested checkbox rows used to wrap, and an unbroken identifier could push the panel sideways). Nothing is hyphenated. A row that carries a second line also gets a native `title` with its full label and supporting text, so hovering a truncated row reveals what was cut.
|
|
197
|
+
- **In `multiple` mode the text input stays a search box.** It never displays the committed values: in single mode the input shows the selected label, but in multi there is no single label to show, and writing a value list into the field would overwrite the query being typed. The chips therefore render *beneath* the field (`rel-select` can carry them inside its trigger because a trigger is not editable), the query clears after each toggle so the next search starts fresh, and the panel stays open. Read the selection from `selectedValues`, not `value`; `changed` carries the array.
|
|
198
|
+
- `value` and the visible text are separate: typing only emits `rel-change`; `value` (and `changed`) update when a row is picked, or when the text exactly matches an item's label or value (case-insensitive) on Enter, Tab, blur or outside click. Free text that matches nothing stays in the field but never becomes `value`.
|
|
199
|
+
- Setting `value` externally (attribute, property, Angular `writeValue`) shows the matching item's label, or the raw string when no item matches (common with remote data on init) — provide the item in `items` to show its label.
|
|
200
|
+
- Clearing emits, in order, `rel-clear`, `changed` (`value: ""`, `item: null`) and `rel-change` (`""`), and only when the field actually had text or a value.
|
|
201
|
+
- Remote mode: `items` is ignored while `searchFn` has produced results; a shorter-than-minimum query cancels any in-flight request and closes the panel; stale responses are discarded (latest-wins); a rejected `searchFn` is treated as an empty result plus `rel-search-error` — the component never throws into the app.
|
|
202
|
+
- `loading` is an external flag layered on top of the automatic `searchFn` loading state; the loading row is shown while either is true.
|
|
203
|
+
- `max-suggestions` caps rendered rows only; the panel scrolls (360px max height) regardless. The matched substring in each row renders bold.
|
|
204
|
+
- Validation is displayed (`error`, `error-message`, `required`) but not performed; validity logic belongs to the form layer.
|
|
205
|
+
- `full-width` layout is keyed off the **attribute** (`:host(:not([full-width]))` renders inline-block) even though the property defaults to `true` and is not reflected — add the `full-width` attribute in markup when you need the block layout. This differs from `rel-textfield`, whose `fullWidth` property is reflected (as `fullwidth` in 1.x).
|
|
206
|
+
- The suggestion panel mirrors `rel-select`'s listbox (same tokens, radius, shadow, row styling) and is promoted to the browser top layer (native Popover API, `popover="manual"`), so it renders correctly inside transformed or `overflow: hidden` containers such as a centered `rel-modal` or a `rel-drawer`; engines without the Popover API fall back to `position: fixed` with a measure-and-cancel correction. It matches the field width, flips above when short on space, and closes on outside pointerdown. On an outside scroll (since 1.5.0) it **stays open and moves with the field while the field is still visible**, and closes once the field scrolls out of the viewport or its scroll container, or under a sticky header — so the top-layer panel never paints over page furniture (the reason 1.4.9 closed it on every scroll). Only the window and the field's own scroll containers count, including ones inside shadow roots such as `rel-modal`'s body; scrolls of unrelated elements and of the panel itself are ignored, and a wheel or swipe over the panel never scrolls the page (`overscroll-behavior: contain`).
|
|
207
|
+
- Only `label`, `control`, `input`, `helper` and `right-icon` are re-exported from the inner `rel-textfield`; its `prefix` / `suffix` parts and slots are not exposed.
|
|
208
|
+
- The field passthroughs are a fixed subset, forwarded as **property** bindings (`.prefixIcon=${…}`), so they are unaffected by attribute-spelling differences between the two components. `rel-textfield` props outside that subset — `prefixText`, `prefixStyle`, `suffixText`, `suffixDivider`, `helperIcon`, `maxLength`, `multiline`, `rows`, `type` — are not forwarded and cannot be set through `rel-autocomplete`.
|
|
209
|
+
- Since 1.5.0 `rel-textfield` accepts the same kebab spellings used here (`prefix-icon`, `helper-text`, `error-message`, `full-width`), so markup moves between the two elements unchanged. The one exception is `floating-label`: `rel-textfield`'s `floatingLabel` is deprecated and keeps its `floatinglabel` attribute; use `label-style="floating"` on both.
|