@rcarls/rc-combobox 0.2.0 → 0.3.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/README.md +103 -7
- package/dist/custom-elements.json +6 -6
- package/dist/{rc-combobox-D6aALkDz.js → rc-combobox-C51Lb-OH.js} +116 -83
- package/dist/rc-combobox-C51Lb-OH.js.map +1 -0
- package/dist/rc-combobox-define.js +1 -1
- package/dist/rc-combobox.js +1 -1
- package/dist/types/packages/rc-combobox/src/rc-combobox.d.ts +15 -6
- package/package.json +9 -12
- package/dist/demo.css +0 -85
- package/dist/demo.js +0 -40
- package/dist/rc-combobox-D6aALkDz.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# `@rcarls/rc-combobox`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Editable combobox with filtering and optional allow-create behavior, configured from native option data and following the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).
|
|
4
|
+
|
|
5
|
+
Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-combobox](https://richardcarls.github.io/rc-webcomponents/components/rc-combobox).
|
|
6
|
+
|
|
7
|
+
Extends `rc-select` with a text input and wraps a native slotted `<select>`.
|
|
6
8
|
|
|
7
9
|
## Installation
|
|
8
10
|
|
|
@@ -30,7 +32,7 @@ import '@rcarls/rc-combobox/define';
|
|
|
30
32
|
<label>
|
|
31
33
|
Fruit
|
|
32
34
|
<rc-combobox placeholder="Search fruit">
|
|
33
|
-
<select
|
|
35
|
+
<select name="fruit">
|
|
34
36
|
<option value="apple">Apple</option>
|
|
35
37
|
<option value="banana">Banana</option>
|
|
36
38
|
<option value="cherry">Cherry</option>
|
|
@@ -41,13 +43,107 @@ import '@rcarls/rc-combobox/define';
|
|
|
41
43
|
|
|
42
44
|
## Allow Create
|
|
43
45
|
|
|
46
|
+
Add `allow-create` to show a **"Create 'X'"** option when the typed text has no exact match.
|
|
47
|
+
Selecting it inserts the new option into the native `<select>`, selects it, and fires `rc-combobox-create`.
|
|
48
|
+
|
|
44
49
|
```html
|
|
45
|
-
<rc-combobox
|
|
46
|
-
<select
|
|
50
|
+
<rc-combobox allow-create placeholder="Add tag">
|
|
51
|
+
<select name="tags" multiple></select>
|
|
47
52
|
</rc-combobox>
|
|
48
53
|
```
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
### Validation
|
|
56
|
+
|
|
57
|
+
`rc-combobox-create` is cancelable. Call `event.preventDefault()` to block insertion when the
|
|
58
|
+
text fails validation. The default behavior (insert + select) runs otherwise.
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
combobox.addEventListener('rc-combobox-create', (event) => {
|
|
62
|
+
if (event.detail.text.trim().length < 2) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
showError('Tag must be at least 2 characters.');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### React — managing options as state
|
|
70
|
+
|
|
71
|
+
Call `preventDefault()` and add the new item to your React state instead. After React renders
|
|
72
|
+
the new `<option>`, set `el.value` in a `useEffect` to select it:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
const [options, setOptions] = useState(initialOptions);
|
|
76
|
+
const pendingValue = useRef<string | null>(null);
|
|
77
|
+
const comboRef = useRef<HTMLElement & { value: string | string[] | undefined }>(null);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const el = comboRef.current;
|
|
81
|
+
if (!el) return;
|
|
82
|
+
const handleCreate = (e: Event) => {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
const { text } = (e as CustomEvent<{ text: string }>).detail;
|
|
85
|
+
const value = text.trim().toLowerCase().replace(/\s+/g, '-');
|
|
86
|
+
setOptions((prev) => [...prev, { value, label: text.trim() }]);
|
|
87
|
+
pendingValue.current = value;
|
|
88
|
+
};
|
|
89
|
+
el.addEventListener('rc-combobox-create', handleCreate);
|
|
90
|
+
return () => el.removeEventListener('rc-combobox-create', handleCreate);
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
// Runs after React renders the new <option>; component has already processed slotchange.
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const value = pendingValue.current;
|
|
96
|
+
if (!value || !comboRef.current) return;
|
|
97
|
+
pendingValue.current = null;
|
|
98
|
+
const el = comboRef.current;
|
|
99
|
+
const current = Array.isArray(el.value) ? el.value : el.value ? [el.value] : [];
|
|
100
|
+
if (!current.includes(value)) {
|
|
101
|
+
el.value = [...current, value];
|
|
102
|
+
}
|
|
103
|
+
}, [options]);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Form usage — ephemeral options until committed
|
|
107
|
+
|
|
108
|
+
By default, created options are added to the native `<select>` and appear in `FormData` on submit
|
|
109
|
+
but are discarded on page reload. To persist them on submit, track them alongside the option list:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const pending = new Set();
|
|
113
|
+
|
|
114
|
+
combobox.addEventListener('rc-combobox-create', (event) => {
|
|
115
|
+
pending.add(event.detail.text.trim());
|
|
116
|
+
// Default runs — option is inserted and selected.
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
form.addEventListener('submit', (event) => {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
const data = new FormData(form);
|
|
122
|
+
const selected = data.getAll('tags');
|
|
123
|
+
const newValues = selected.filter((v) => pending.has(v));
|
|
124
|
+
// Save newValues to the server; they become persisted options next load.
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Controlled vs Uncontrolled
|
|
129
|
+
|
|
130
|
+
**Uncontrolled (default):** set `<option selected>` or `default-value` for the initial value;
|
|
131
|
+
the component owns selection thereafter. Listen to `rc-select-change` to observe changes.
|
|
132
|
+
|
|
133
|
+
**Controlled:** write `el.value` (the property) to drive selection programmatically. Writes are
|
|
134
|
+
silent — no `rc-select-change` is dispatched. Update `el.value` in response to `rc-select-change`
|
|
135
|
+
to keep external state in sync.
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
combobox.value = 'banana'; // single
|
|
139
|
+
combobox.value = ['apple', 'cherry']; // multiple
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For `allow-create`, the same split applies to options:
|
|
143
|
+
|
|
144
|
+
- **Uncontrolled options:** let the default behavior add the new `<option>` to the native `<select>`.
|
|
145
|
+
- **Controlled options:** call `event.preventDefault()` on `rc-combobox-create` and manage `<option>`
|
|
146
|
+
elements yourself (e.g., in React state), then set `el.value` to include the new value.
|
|
51
147
|
|
|
52
148
|
## API
|
|
53
149
|
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"declarations": [
|
|
47
47
|
{
|
|
48
48
|
"kind": "class",
|
|
49
|
-
"description": "
|
|
49
|
+
"description": "Editable combobox with filtering and optional allow-create behavior, configured from\nnative option data and following the WAI-ARIA Combobox pattern.\n\nExtends `rc-select` by replacing the trigger `<div>` with a text `<input>`\nand adding: live filtering of the listbox, keyboard navigation from input,\nand an optional \"Create '{text}'\" option for new entries.",
|
|
50
50
|
"name": "RCCombobox",
|
|
51
51
|
"cssProperties": [
|
|
52
52
|
{
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"slots": [
|
|
140
140
|
{
|
|
141
141
|
"description": "Required. A native `<select>` element for form submission.",
|
|
142
|
-
"name": "
|
|
142
|
+
"name": ""
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"description": "Optional. Replaces the default chevron icon.",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
},
|
|
156
156
|
"default": "false",
|
|
157
157
|
"description": "When set, shows a \"Create '{text}'\" option for text that has no exact match.",
|
|
158
|
-
"attribute": "
|
|
158
|
+
"attribute": "allow-create"
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
"kind": "field",
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
"text": "FilterStrategy"
|
|
165
165
|
},
|
|
166
166
|
"default": "'contains'",
|
|
167
|
-
"description": "How option labels are matched against typed input
|
|
167
|
+
"description": "How option labels are matched against typed input.\n\n- Forwarded to the internal `rc-listbox`.\n- Defaults to `'contains'` (substring).\n- Set to `'prefix'` for starts-with matching, or\n- Pass a custom `(label, query) => boolean` predicate.\n\nFunction values are JS-only; string values may be set via the `filter-strategy` attribute.",
|
|
168
168
|
"attribute": "filter-strategy"
|
|
169
169
|
},
|
|
170
170
|
{
|
|
@@ -340,7 +340,7 @@
|
|
|
340
340
|
"attributes": [
|
|
341
341
|
{
|
|
342
342
|
"description": "When set, shows a \"Create '{text}'\" option for text that has no exact match.",
|
|
343
|
-
"name": "
|
|
343
|
+
"name": "allow-create",
|
|
344
344
|
"type": {
|
|
345
345
|
"text": "boolean"
|
|
346
346
|
},
|
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
"text": "FilterStrategy"
|
|
354
354
|
},
|
|
355
355
|
"default": "'contains'",
|
|
356
|
-
"description": "How option labels are matched against typed input
|
|
356
|
+
"description": "How option labels are matched against typed input.\n\n- Forwarded to the internal `rc-listbox`.\n- Defaults to `'contains'` (substring).\n- Set to `'prefix'` for starts-with matching, or\n- Pass a custom `(label, query) => boolean` predicate.\n\nFunction values are JS-only; string values may be set via the `filter-strategy` attribute.",
|
|
357
357
|
"fieldName": "filterStrategy"
|
|
358
358
|
}
|
|
359
359
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { property as
|
|
1
|
+
import { css as p, nothing as d, html as u } from "lit";
|
|
2
|
+
import { property as h, query as b, state as g } from "lit/decorators.js";
|
|
3
3
|
import { RCSelect as v } from "@rcarls/rc-select";
|
|
4
|
-
const f =
|
|
4
|
+
const f = p`
|
|
5
5
|
:host {
|
|
6
6
|
display: inline-block;
|
|
7
7
|
}
|
|
@@ -12,13 +12,20 @@ const f = h`
|
|
|
12
12
|
flex-wrap: wrap;
|
|
13
13
|
gap: var(--rc-combobox-gap, var(--rc-control-gap, 0.25em));
|
|
14
14
|
min-block-size: var(--rc-combobox-control-block-size, var(--rc-control-block-size, auto));
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
padding: var(--rc-combobox-padding-block, var(--rc-control-padding-block, 1px))
|
|
16
|
+
var(--rc-combobox-padding-inline, var(--rc-control-padding-inline, 4px));
|
|
17
|
+
min-width: 8em;
|
|
18
|
+
cursor: text;
|
|
19
|
+
border: var(
|
|
20
|
+
--rc-combobox-border,
|
|
21
|
+
var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
|
|
22
|
+
);
|
|
23
|
+
border-radius: var(
|
|
24
|
+
--rc-combobox-radius,
|
|
25
|
+
var(--rc-control-radius, var(--rc-radius-sm, 0.125em))
|
|
26
|
+
);
|
|
17
27
|
background: var(--rc-field, Field);
|
|
18
28
|
color: var(--rc-field-text, FieldText);
|
|
19
|
-
padding: var(--rc-combobox-padding-block, calc(var(--rc-control-padding-block, 0.25em) / 2))
|
|
20
|
-
var(--rc-combobox-padding-inline, calc(var(--rc-control-padding-inline, 0.5em) / 2));
|
|
21
|
-
cursor: text;
|
|
22
29
|
font-family: var(--rc-font-family, inherit);
|
|
23
30
|
font-size: var(--rc-font-size, inherit);
|
|
24
31
|
line-height: var(--rc-line-height, normal);
|
|
@@ -26,6 +33,13 @@ const f = h`
|
|
|
26
33
|
background-color var(--rc-motion-duration, 120ms),
|
|
27
34
|
border-color var(--rc-motion-duration, 120ms),
|
|
28
35
|
box-shadow var(--rc-motion-duration, 120ms);
|
|
36
|
+
|
|
37
|
+
/* Focus ring — anchor shows the ring when the internal input is focused */
|
|
38
|
+
outline: none;
|
|
39
|
+
&:focus-within {
|
|
40
|
+
outline: var(--rc-focus-ring, auto);
|
|
41
|
+
outline-offset: var(--rc-focus-ring-offset, 0);
|
|
42
|
+
}
|
|
29
43
|
}
|
|
30
44
|
|
|
31
45
|
/* Chips — the whole chip is the remove button for a larger touch target */
|
|
@@ -35,7 +49,10 @@ const f = h`
|
|
|
35
49
|
gap: var(--rc-combobox-chip-gap, calc(var(--rc-control-gap, 0.25em) * 0.8));
|
|
36
50
|
padding: var(--rc-combobox-chip-padding-block, 0.1em)
|
|
37
51
|
var(--rc-combobox-chip-padding-inline, 0.3em);
|
|
38
|
-
border: var(
|
|
52
|
+
border: var(
|
|
53
|
+
--rc-combobox-chip-border,
|
|
54
|
+
var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
|
|
55
|
+
);
|
|
39
56
|
border-radius: var(--rc-combobox-chip-radius, var(--rc-radius-md, 0.25em));
|
|
40
57
|
background: var(--rc-button-bg, ButtonFace);
|
|
41
58
|
color: var(--rc-button-text, ButtonText);
|
|
@@ -66,14 +83,13 @@ const f = h`
|
|
|
66
83
|
|
|
67
84
|
#trigger {
|
|
68
85
|
flex: 1;
|
|
69
|
-
min-width:
|
|
86
|
+
min-width: 0;
|
|
70
87
|
border: none;
|
|
71
88
|
background: transparent;
|
|
72
89
|
color: inherit;
|
|
73
90
|
font: inherit;
|
|
74
91
|
outline: none;
|
|
75
|
-
padding:
|
|
76
|
-
var(--rc-combobox-input-padding-inline, calc(var(--rc-control-padding-inline, 0.5em) / 2));
|
|
92
|
+
padding: 0;
|
|
77
93
|
cursor: text;
|
|
78
94
|
}
|
|
79
95
|
|
|
@@ -86,12 +102,11 @@ const f = h`
|
|
|
86
102
|
display: inline-flex;
|
|
87
103
|
align-items: center;
|
|
88
104
|
justify-content: center;
|
|
89
|
-
|
|
105
|
+
inline-size: var(--rc-select-toggle-indicator-size, 1.1em);
|
|
90
106
|
border: none;
|
|
91
107
|
background: transparent;
|
|
92
108
|
color: inherit;
|
|
93
109
|
cursor: default;
|
|
94
|
-
font-size: 0.75em;
|
|
95
110
|
font: inherit;
|
|
96
111
|
|
|
97
112
|
&:focus-visible {
|
|
@@ -100,8 +115,8 @@ const f = h`
|
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
/*
|
|
104
|
-
slot[name
|
|
118
|
+
/* Hide the default slot — visually suppresses the slotted native <select> */
|
|
119
|
+
slot:not([name]) {
|
|
105
120
|
display: none;
|
|
106
121
|
}
|
|
107
122
|
|
|
@@ -110,43 +125,47 @@ const f = h`
|
|
|
110
125
|
max-height: var(--rc-combobox-max-height, 20em);
|
|
111
126
|
overflow-y: auto;
|
|
112
127
|
background: var(--rc-surface, Canvas);
|
|
113
|
-
border: var(
|
|
128
|
+
border: var(
|
|
129
|
+
--rc-combobox-listbox-border,
|
|
130
|
+
var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
|
|
131
|
+
);
|
|
114
132
|
border-radius: var(--rc-combobox-listbox-radius, var(--rc-control-radius, 0));
|
|
115
|
-
box-shadow: var(
|
|
133
|
+
box-shadow: var(
|
|
134
|
+
--rc-combobox-shadow,
|
|
135
|
+
var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))
|
|
136
|
+
);
|
|
116
137
|
color: var(--rc-field-text, FieldText);
|
|
117
|
-
padding-block: var(
|
|
138
|
+
padding-block: var(
|
|
139
|
+
--rc-combobox-listbox-padding-block,
|
|
140
|
+
var(--rc-control-padding-block, 0.25em)
|
|
141
|
+
);
|
|
142
|
+
--rc-listbox-option-gap: var(--rc-item-gap, 0.4em);
|
|
143
|
+
--rc-listbox-option-padding-block: var(--rc-item-padding-block, 0.3em);
|
|
144
|
+
--rc-listbox-option-padding-inline: var(--rc-item-padding-inline, 0.75em);
|
|
145
|
+
--rc-listbox-hover-bg: var(--rc-highlight, Highlight);
|
|
146
|
+
--rc-listbox-hover-color: var(--rc-highlight-text, HighlightText);
|
|
147
|
+
--rc-listbox-active-bg: var(--rc-highlight, Highlight);
|
|
148
|
+
--rc-listbox-active-color: var(--rc-highlight-text, HighlightText);
|
|
149
|
+
--rc-listbox-selected-bg: var(--rc-highlight, Highlight);
|
|
150
|
+
--rc-listbox-selected-color: var(--rc-highlight-text, HighlightText);
|
|
151
|
+
--rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);
|
|
118
152
|
|
|
119
153
|
&:not(:popover-open) {
|
|
120
154
|
display: none;
|
|
121
155
|
}
|
|
122
156
|
}
|
|
123
157
|
|
|
124
|
-
rc-listbox [part~='option'] {
|
|
125
|
-
display:
|
|
126
|
-
|
|
127
|
-
gap: var(--rc-item-gap, 0.4em);
|
|
128
|
-
padding: var(--rc-item-padding-block, 0.3em) var(--rc-item-padding-inline, 0.75em);
|
|
129
|
-
cursor: default;
|
|
130
|
-
|
|
131
|
-
/* display: flex overrides [hidden]'s browser-default display:none — restore it explicitly */
|
|
132
|
-
&[hidden] { display: none; }
|
|
133
|
-
|
|
134
|
-
&:not([hidden]):not([aria-disabled='true']):hover {
|
|
135
|
-
background: var(--rc-highlight, Highlight);
|
|
136
|
-
color: var(--rc-highlight-text, HighlightText);
|
|
137
|
-
}
|
|
158
|
+
rc-listbox [part~='option'][hidden] {
|
|
159
|
+
display: none;
|
|
160
|
+
}
|
|
138
161
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
outline-offset: -2px;
|
|
144
|
-
}
|
|
162
|
+
rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {
|
|
163
|
+
outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));
|
|
164
|
+
outline-offset: -2px;
|
|
165
|
+
}
|
|
145
166
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
cursor: not-allowed;
|
|
149
|
-
}
|
|
167
|
+
rc-listbox [part~='option'][aria-disabled='true'] {
|
|
168
|
+
cursor: not-allowed;
|
|
150
169
|
}
|
|
151
170
|
|
|
152
171
|
rc-listbox [part~='option-checkmark'] {
|
|
@@ -168,16 +187,15 @@ const f = h`
|
|
|
168
187
|
padding-top: 0.3em;
|
|
169
188
|
}
|
|
170
189
|
`;
|
|
171
|
-
var
|
|
172
|
-
for (var
|
|
173
|
-
(c = n[s]) && (
|
|
174
|
-
return
|
|
190
|
+
var x = Object.defineProperty, o = (n, t, e, a) => {
|
|
191
|
+
for (var i = void 0, s = n.length - 1, c; s >= 0; s--)
|
|
192
|
+
(c = n[s]) && (i = c(t, e, i) || i);
|
|
193
|
+
return i && x(t, e, i), i;
|
|
175
194
|
};
|
|
176
195
|
const l = class l extends v {
|
|
177
196
|
constructor() {
|
|
178
197
|
super(...arguments), this.allowCreate = !1, this.filterStrategy = "contains", this._filterText = "", this._closingPopup = !1;
|
|
179
198
|
}
|
|
180
|
-
// ── Override popup lifecycle ──────────────────────────────────────────────────
|
|
181
199
|
openPopup() {
|
|
182
200
|
super.openPopup(), this._$listbox?.filterOptions(this._filterText);
|
|
183
201
|
}
|
|
@@ -186,9 +204,8 @@ const l = class l extends v {
|
|
|
186
204
|
this._closingPopup = !1;
|
|
187
205
|
}, 0);
|
|
188
206
|
}
|
|
189
|
-
// ── Input events ──────────────────────────────────────────────────────────────
|
|
190
207
|
_handleInput(t) {
|
|
191
|
-
this._filterText = t.target.value, !this.open && this._filterText && this.openPopup(), this._$listbox?.filterOptions(this._filterText), this._updateCreateOption(), this._$listbox?.navigableItems.length ? this.
|
|
208
|
+
this._filterText = t.target.value, !this.open && this._filterText && this.openPopup(), this._$listbox?.filterOptions(this._filterText), this._updateCreateOption(), this._$listbox?.navigableItems.length ? this._activeDescendantCtrl.navigateToFirst() : this._activeDescendantCtrl.clear();
|
|
192
209
|
}
|
|
193
210
|
_handleInputFocus() {
|
|
194
211
|
!this.open && !this._closingPopup && this.openPopup();
|
|
@@ -200,27 +217,25 @@ const l = class l extends v {
|
|
|
200
217
|
const e = t.target;
|
|
201
218
|
e.closest('[part~="chip"]') || e.id === "toggle" || this._$trigger?.focus();
|
|
202
219
|
}
|
|
203
|
-
// ── Create option ─────────────────────────────────────────────────────────────
|
|
204
220
|
_updateCreateOption() {
|
|
205
221
|
if (!this.allowCreate || !this._filterText.trim()) {
|
|
206
222
|
this._$listbox?.setCreateOption(null);
|
|
207
223
|
return;
|
|
208
224
|
}
|
|
209
|
-
const t = this._filterText.trim().toLowerCase(), e = this._$listbox?.allOptions.some(
|
|
210
|
-
(i) => i.label.toLowerCase() === t
|
|
211
|
-
) ?? !1;
|
|
225
|
+
const t = this._filterText.trim().toLowerCase(), e = this._$listbox?.allOptions.some((a) => a.label.toLowerCase() === t) ?? !1;
|
|
212
226
|
this._$listbox?.setCreateOption(e ? null : this._filterText.trim());
|
|
213
227
|
}
|
|
214
228
|
_handleListboxChange(t) {
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
229
|
+
const e = t.detail;
|
|
230
|
+
if (e.reason === "action" && e.action === "create") {
|
|
217
231
|
t.stopPropagation(), this._activateCreate(this._filterText.trim());
|
|
218
232
|
return;
|
|
219
233
|
}
|
|
220
234
|
super._handleListboxChange(t), this.multiple ? (this._filterText = "", this._$trigger && (this._$trigger.value = ""), this._$listbox?.clearFilter(), this._updateCreateOption()) : this._syncInputToSelection();
|
|
221
235
|
}
|
|
222
236
|
async _activateCreate(t) {
|
|
223
|
-
if (!t)
|
|
237
|
+
if (!t)
|
|
238
|
+
return;
|
|
224
239
|
const e = new CustomEvent("rc-combobox-create", {
|
|
225
240
|
bubbles: !0,
|
|
226
241
|
composed: !0,
|
|
@@ -242,35 +257,41 @@ const l = class l extends v {
|
|
|
242
257
|
return super.value;
|
|
243
258
|
}
|
|
244
259
|
_syncInputToSelection() {
|
|
245
|
-
if (this.multiple)
|
|
260
|
+
if (this.multiple)
|
|
261
|
+
return;
|
|
246
262
|
const t = this.selectedValues[0], e = t ? this._labelFor(t) : "";
|
|
247
263
|
this._filterText = e, this._$trigger && (this._$trigger.value = e);
|
|
248
264
|
}
|
|
249
|
-
// ── Keyboard ──────────────────────────────────────────────────────────────────
|
|
250
265
|
_handleInputKeyDown(t) {
|
|
251
266
|
switch (t.key) {
|
|
252
267
|
case "ArrowDown":
|
|
253
|
-
t.preventDefault(), this.open ? this.
|
|
268
|
+
t.preventDefault(), this.open ? this._activeDescendantCtrl.navigate(1) : (this.openPopup(), this._activeDescendantCtrl.navigateToFirst());
|
|
254
269
|
break;
|
|
255
270
|
case "ArrowUp":
|
|
256
|
-
t.preventDefault(), this.open && this.
|
|
271
|
+
t.preventDefault(), this.open && this._activeDescendantCtrl.navigate(-1);
|
|
257
272
|
break;
|
|
258
273
|
case "Home":
|
|
259
|
-
this.open && (t.preventDefault(), this.
|
|
274
|
+
this.open && (t.preventDefault(), this._activeDescendantCtrl.navigateToFirst());
|
|
260
275
|
break;
|
|
261
276
|
case "End":
|
|
262
|
-
this.open && (t.preventDefault(), this.
|
|
277
|
+
this.open && (t.preventDefault(), this._activeDescendantCtrl.navigateToLast());
|
|
263
278
|
break;
|
|
264
279
|
case "Enter": {
|
|
265
280
|
t.preventDefault();
|
|
266
|
-
const e = this.
|
|
267
|
-
e ? e.dispatchEvent(
|
|
281
|
+
const e = this._activeDescendantCtrl.activeItem;
|
|
282
|
+
e ? e.dispatchEvent(
|
|
283
|
+
new PointerEvent("pointerdown", { bubbles: !0, cancelable: !0 })
|
|
284
|
+
) : this.open && this._$listbox?.navigableItems.length && this._$listbox.navigableItems[0]?.dispatchEvent(
|
|
285
|
+
new PointerEvent("pointerdown", { bubbles: !0, cancelable: !0 })
|
|
286
|
+
);
|
|
268
287
|
break;
|
|
269
288
|
}
|
|
270
289
|
case "Tab":
|
|
271
290
|
if (this.open) {
|
|
272
291
|
const e = this._$listbox?.navigableItems[0];
|
|
273
|
-
e && e.dispatchEvent(
|
|
292
|
+
e && e.dispatchEvent(
|
|
293
|
+
new PointerEvent("pointerdown", { bubbles: !0, cancelable: !0 })
|
|
294
|
+
), this.closePopup(!1);
|
|
274
295
|
}
|
|
275
296
|
break;
|
|
276
297
|
case "Escape":
|
|
@@ -293,7 +314,6 @@ const l = class l extends v {
|
|
|
293
314
|
get _inputPlaceholder() {
|
|
294
315
|
return this.multiple && this._selectedValues.size > 0 ? "" : this.placeholder;
|
|
295
316
|
}
|
|
296
|
-
// ── Render ────────────────────────────────────────────────────────────────────
|
|
297
317
|
render() {
|
|
298
318
|
const t = this.multiple && this._selectedValues.size > 0;
|
|
299
319
|
return u`
|
|
@@ -317,7 +337,7 @@ const l = class l extends v {
|
|
|
317
337
|
@input=${this._handleInput}
|
|
318
338
|
@keydown=${this._handleInputKeyDown}
|
|
319
339
|
@focus=${this._handleInputFocus}
|
|
320
|
-
|
|
340
|
+
/>
|
|
321
341
|
|
|
322
342
|
<button
|
|
323
343
|
id="toggle"
|
|
@@ -327,7 +347,20 @@ const l = class l extends v {
|
|
|
327
347
|
tabindex="-1"
|
|
328
348
|
@click=${this._handleToggleClick}
|
|
329
349
|
>
|
|
330
|
-
<slot name="toggle-icon"
|
|
350
|
+
<slot name="toggle-icon">
|
|
351
|
+
<svg
|
|
352
|
+
width="10"
|
|
353
|
+
height="6"
|
|
354
|
+
viewBox="0 0 10 6"
|
|
355
|
+
fill="none"
|
|
356
|
+
stroke="currentColor"
|
|
357
|
+
stroke-width="1.5"
|
|
358
|
+
stroke-linecap="round"
|
|
359
|
+
stroke-linejoin="round"
|
|
360
|
+
>
|
|
361
|
+
<polyline points="1,1 5,5 9,1" />
|
|
362
|
+
</svg>
|
|
363
|
+
</slot>
|
|
331
364
|
</button>
|
|
332
365
|
</div>
|
|
333
366
|
|
|
@@ -341,25 +374,25 @@ const l = class l extends v {
|
|
|
341
374
|
@rc-listbox-change=${this._handleListboxChange}
|
|
342
375
|
></rc-listbox>
|
|
343
376
|
|
|
344
|
-
<slot
|
|
377
|
+
<slot @slotchange=${this._handleSelectSlotChange}></slot>
|
|
345
378
|
`;
|
|
346
379
|
}
|
|
347
380
|
};
|
|
348
381
|
l.styles = f;
|
|
349
|
-
let
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
],
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
],
|
|
356
|
-
|
|
382
|
+
let r = l;
|
|
383
|
+
o([
|
|
384
|
+
h({ type: Boolean, attribute: "allow-create" })
|
|
385
|
+
], r.prototype, "allowCreate");
|
|
386
|
+
o([
|
|
387
|
+
h({ attribute: "filter-strategy", reflect: !1 })
|
|
388
|
+
], r.prototype, "filterStrategy");
|
|
389
|
+
o([
|
|
357
390
|
b("#trigger")
|
|
358
|
-
],
|
|
359
|
-
|
|
391
|
+
], r.prototype, "_$trigger");
|
|
392
|
+
o([
|
|
360
393
|
g()
|
|
361
|
-
],
|
|
394
|
+
], r.prototype, "_filterText");
|
|
362
395
|
export {
|
|
363
|
-
|
|
396
|
+
r as R
|
|
364
397
|
};
|
|
365
|
-
//# sourceMappingURL=rc-combobox-
|
|
398
|
+
//# sourceMappingURL=rc-combobox-C51Lb-OH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-combobox-C51Lb-OH.js","sources":["../src/rc-combobox.styles.ts","../src/rc-combobox.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const comboboxStyles = css`\n :host {\n display: inline-block;\n }\n\n #anchor {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--rc-combobox-gap, var(--rc-control-gap, 0.25em));\n min-block-size: var(--rc-combobox-control-block-size, var(--rc-control-block-size, auto));\n padding: var(--rc-combobox-padding-block, var(--rc-control-padding-block, 1px))\n var(--rc-combobox-padding-inline, var(--rc-control-padding-inline, 4px));\n min-width: 8em;\n cursor: text;\n border: var(\n --rc-combobox-border,\n var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))\n );\n border-radius: var(\n --rc-combobox-radius,\n var(--rc-control-radius, var(--rc-radius-sm, 0.125em))\n );\n background: var(--rc-field, Field);\n color: var(--rc-field-text, FieldText);\n font-family: var(--rc-font-family, inherit);\n font-size: var(--rc-font-size, inherit);\n line-height: var(--rc-line-height, normal);\n transition:\n background-color var(--rc-motion-duration, 120ms),\n border-color var(--rc-motion-duration, 120ms),\n box-shadow var(--rc-motion-duration, 120ms);\n\n /* Focus ring — anchor shows the ring when the internal input is focused */\n outline: none;\n &:focus-within {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n }\n\n /* Chips — the whole chip is the remove button for a larger touch target */\n [part='chip'] {\n display: inline-flex;\n align-items: center;\n gap: var(--rc-combobox-chip-gap, calc(var(--rc-control-gap, 0.25em) * 0.8));\n padding: var(--rc-combobox-chip-padding-block, 0.1em)\n var(--rc-combobox-chip-padding-inline, 0.3em);\n border: var(\n --rc-combobox-chip-border,\n var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))\n );\n border-radius: var(--rc-combobox-chip-radius, var(--rc-radius-md, 0.25em));\n background: var(--rc-button-bg, ButtonFace);\n color: var(--rc-button-text, ButtonText);\n font: inherit;\n font-size: 0.875em;\n cursor: pointer;\n\n &:hover {\n background: var(--rc-highlight, Highlight);\n color: var(--rc-highlight-text, HighlightText);\n }\n\n &:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n }\n\n /* Decorative × icon inside the chip — no interaction, aria-hidden */\n [part='chip-remove'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1em;\n font-size: 0.85em;\n pointer-events: none;\n }\n\n #trigger {\n flex: 1;\n min-width: 0;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n outline: none;\n padding: 0;\n cursor: text;\n }\n\n #trigger::placeholder {\n color: var(--rc-text-disabled, GrayText);\n }\n\n #toggle {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--rc-select-toggle-indicator-size, 1.1em);\n border: none;\n background: transparent;\n color: inherit;\n cursor: default;\n font: inherit;\n\n &:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n }\n\n /* Hide the default slot — visually suppresses the slotted native <select> */\n slot:not([name]) {\n display: none;\n }\n\n /* Listbox popup — positioned by AnchorController via adoptedStyleSheets */\n rc-listbox {\n max-height: var(--rc-combobox-max-height, 20em);\n overflow-y: auto;\n background: var(--rc-surface, Canvas);\n border: var(\n --rc-combobox-listbox-border,\n var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))\n );\n border-radius: var(--rc-combobox-listbox-radius, var(--rc-control-radius, 0));\n box-shadow: var(\n --rc-combobox-shadow,\n var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))\n );\n color: var(--rc-field-text, FieldText);\n padding-block: var(\n --rc-combobox-listbox-padding-block,\n var(--rc-control-padding-block, 0.25em)\n );\n --rc-listbox-option-gap: var(--rc-item-gap, 0.4em);\n --rc-listbox-option-padding-block: var(--rc-item-padding-block, 0.3em);\n --rc-listbox-option-padding-inline: var(--rc-item-padding-inline, 0.75em);\n --rc-listbox-hover-bg: var(--rc-highlight, Highlight);\n --rc-listbox-hover-color: var(--rc-highlight-text, HighlightText);\n --rc-listbox-active-bg: var(--rc-highlight, Highlight);\n --rc-listbox-active-color: var(--rc-highlight-text, HighlightText);\n --rc-listbox-selected-bg: var(--rc-highlight, Highlight);\n --rc-listbox-selected-color: var(--rc-highlight-text, HighlightText);\n --rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);\n\n &:not(:popover-open) {\n display: none;\n }\n }\n\n rc-listbox [part~='option'][hidden] {\n display: none;\n }\n\n rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {\n outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));\n outline-offset: -2px;\n }\n\n rc-listbox [part~='option'][aria-disabled='true'] {\n cursor: not-allowed;\n }\n\n rc-listbox [part~='option-checkmark'] {\n flex-shrink: 0;\n width: 1em;\n text-align: center;\n font-size: 0.85em;\n visibility: hidden;\n }\n\n rc-listbox [part~='option'][aria-selected='true'] [part~='option-checkmark'] {\n visibility: visible;\n }\n\n rc-listbox [part~='create-option'] {\n font-style: italic;\n border-top: 1px solid var(--rc-border-color, ButtonBorder);\n margin-top: 0.25em;\n padding-top: 0.3em;\n }\n`;\n\nexport default comboboxStyles;\n","import { html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\n\nimport { RCSelect } from '@rcarls/rc-select';\nimport type { FilterStrategy, RCListboxChangeEvent } from '@rcarls/rc-listbox';\n\nimport { comboboxStyles } from './rc-combobox.styles.js';\n\nexport interface RCComboboxCreateEvent {\n /** The text typed by the user that didn't match any existing option. */\n text: string;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-combobox': RCCombobox;\n }\n}\n\n/**\n * Editable combobox with filtering and optional allow-create behavior, configured from\n * native option data and following the WAI-ARIA Combobox pattern.\n *\n * Extends `rc-select` by replacing the trigger `<div>` with a text `<input>`\n * and adding: live filtering of the listbox, keyboard navigation from input,\n * and an optional \"Create '{text}'\" option for new entries.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-combobox rc-combobox docs}\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ WAI-ARIA Combobox pattern}\n *\n * @slot - Required. A native `<select>` element for form submission.\n * @slot toggle-icon - Optional. Replaces the default chevron icon.\n *\n * @fires rc-select-change - Inherited selection change event.\n * @fires rc-combobox-create - When the \"Create\" option is activated.\n * `detail: { text: string }`. Cancelable — call `preventDefault()` to stop\n * the default insertion of the new option.\n *\n * @csspart anchor - Outer container (includes chips + input + toggle).\n * @csspart chip - Individual chip (multiple mode).\n * @csspart chip-label - Text label inside a chip.\n * @csspart chip-remove - Remove button inside a chip.\n * @csspart input - The text input element.\n * @csspart toggle - The chevron toggle button.\n *\n * @attr [allow-create] - When present, shows a \"Create 'X'\" option for unmatched input.\n *\n * @cssprop [--rc-combobox-max-height=20em] - Maximum popup height.\n * @cssprop [--rc-combobox-control-block-size=var(--rc-control-block-size)] - Anchor block size.\n * @cssprop [--rc-combobox-padding-block=calc(var(--rc-control-padding-block) / 2)] - Anchor block-axis padding.\n * @cssprop [--rc-combobox-padding-inline=calc(var(--rc-control-padding-inline) / 2)] - Anchor inline-axis padding.\n * @cssprop [--rc-combobox-gap=var(--rc-control-gap)] - Gap between chips, input, and toggle.\n * @cssprop [--rc-combobox-radius=var(--rc-control-radius)] - Anchor border radius.\n * @cssprop [--rc-combobox-border=var(--rc-border)] - Anchor border.\n * @cssprop [--rc-combobox-listbox-radius=var(--rc-control-radius)] - Popup listbox border radius.\n * @cssprop [--rc-combobox-listbox-padding-block=var(--rc-control-padding-block)] - Popup listbox block padding.\n * @cssprop [--rc-combobox-chip-radius=var(--rc-radius-md)] - Multi-select chip border radius.\n * @cssprop [--rc-combobox-chip-padding-block=0.1em] - Multi-select chip block-axis padding.\n * @cssprop [--rc-combobox-chip-padding-inline=0.3em] - Multi-select chip inline-axis padding.\n */\nexport class RCCombobox extends RCSelect {\n static override styles = comboboxStyles;\n\n /** When set, shows a \"Create '{text}'\" option for text that has no exact match. */\n @property({ type: Boolean, attribute: 'allow-create' })\n allowCreate = false;\n\n /**\n * How option labels are matched against typed input.\n *\n * - Forwarded to the internal `rc-listbox`.\n * - Defaults to `'contains'` (substring).\n * - Set to `'prefix'` for starts-with matching, or\n * - Pass a custom `(label, query) => boolean` predicate.\n *\n * Function values are JS-only; string values may be set via the `filter-strategy` attribute.\n */\n @property({ attribute: 'filter-strategy', reflect: false })\n filterStrategy: FilterStrategy = 'contains';\n\n @query('#trigger')\n protected override _$trigger!: HTMLInputElement;\n\n @state()\n private _filterText = '';\n\n // Guard against _handleInputFocus re-opening the popup immediately after close.\n private _closingPopup = false;\n\n override openPopup() {\n super.openPopup();\n this._$listbox?.filterOptions(this._filterText);\n }\n\n override closePopup(_returnFocus = true) {\n this._filterText = '';\n this._$listbox?.clearFilter();\n this._$listbox?.setCreateOption(null);\n this._closingPopup = true;\n\n super.closePopup(false);\n\n // Deferred past any native focus-return from hidePopover() in Firefox\n setTimeout(() => {\n this._closingPopup = false;\n }, 0);\n }\n\n private _handleInput(e: InputEvent) {\n this._filterText = (e.target as HTMLInputElement).value;\n\n if (!this.open && this._filterText) {\n this.openPopup();\n }\n\n this._$listbox?.filterOptions(this._filterText);\n this._updateCreateOption();\n\n if (this._$listbox?.navigableItems.length) {\n this._activeDescendantCtrl.navigateToFirst();\n } else {\n this._activeDescendantCtrl.clear();\n }\n }\n\n private _handleInputFocus() {\n if (!this.open && !this._closingPopup) {\n this.openPopup();\n }\n }\n\n private _handleToggleClick(e: MouseEvent) {\n e.stopPropagation();\n\n if (this.open) {\n this.closePopup();\n } else {\n this.openPopup();\n this._$trigger?.focus();\n }\n }\n\n private _handleAnchorClick(e: MouseEvent) {\n const $target = e.target as HTMLElement;\n\n if (\n $target.closest('[part~=\"chip\"]') ||\n ($target as HTMLElement & { id?: string }).id === 'toggle'\n ) {\n return;\n }\n\n this._$trigger?.focus();\n }\n\n private _updateCreateOption() {\n if (!this.allowCreate || !this._filterText.trim()) {\n this._$listbox?.setCreateOption(null);\n\n return;\n }\n\n const trimmed = this._filterText.trim().toLowerCase();\n const hasExact =\n this._$listbox?.allOptions.some((o) => o.label.toLowerCase() === trimmed) ?? false;\n\n this._$listbox?.setCreateOption(hasExact ? null : this._filterText.trim());\n }\n\n protected override _handleListboxChange(e: CustomEvent) {\n const detail = e.detail as RCListboxChangeEvent;\n\n if (detail.reason === 'action' && detail.action === 'create') {\n e.stopPropagation();\n void this._activateCreate(this._filterText.trim());\n\n return;\n }\n\n super._handleListboxChange(e);\n\n if (!this.multiple) {\n this._syncInputToSelection();\n } else {\n this._filterText = '';\n\n if (this._$trigger) {\n this._$trigger.value = '';\n }\n\n this._$listbox?.clearFilter();\n this._updateCreateOption();\n }\n }\n\n private async _activateCreate(text: string) {\n if (!text) {\n return;\n }\n\n const createEvent = new CustomEvent<RCComboboxCreateEvent>('rc-combobox-create', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: { text },\n });\n\n if (!this.dispatchEvent(createEvent)) {\n return;\n }\n\n this._addOption({ value: text, label: text });\n\n if (this.multiple) {\n this._$listbox?.toggleOption(text);\n this._filterText = '';\n\n if (this._$trigger) {\n this._$trigger.value = '';\n }\n\n this._$listbox?.clearFilter();\n this._$listbox?.setCreateOption(null);\n\n return;\n }\n\n this._applySelection([text]);\n this._syncInputToSelection();\n this.closePopup(true);\n this._dispatchChange();\n }\n\n override set value(value: string | string[] | undefined) {\n super.value = value;\n this._syncInputToSelection();\n }\n\n override get value(): string | string[] {\n return super.value;\n }\n\n private _syncInputToSelection(): void {\n if (this.multiple) {\n return;\n }\n\n const value = this.selectedValues[0];\n const label = value ? this._labelFor(value) : '';\n\n this._filterText = label;\n\n if (this._$trigger) {\n this._$trigger.value = label;\n }\n }\n\n private _handleInputKeyDown(e: KeyboardEvent) {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n\n if (!this.open) {\n this.openPopup();\n this._activeDescendantCtrl.navigateToFirst();\n } else {\n this._activeDescendantCtrl.navigate(1);\n }\n\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n\n if (this.open) {\n this._activeDescendantCtrl.navigate(-1);\n }\n\n break;\n\n case 'Home':\n if (this.open) {\n e.preventDefault();\n\n this._activeDescendantCtrl.navigateToFirst();\n }\n break;\n\n case 'End':\n if (this.open) {\n e.preventDefault();\n\n this._activeDescendantCtrl.navigateToLast();\n }\n break;\n\n case 'Enter': {\n e.preventDefault();\n const active = this._activeDescendantCtrl.activeItem;\n\n if (active) {\n active.dispatchEvent(\n new PointerEvent('pointerdown', { bubbles: true, cancelable: true }),\n );\n } else if (this.open && this._$listbox?.navigableItems.length) {\n const $first = this._$listbox.navigableItems[0];\n\n $first?.dispatchEvent(\n new PointerEvent('pointerdown', { bubbles: true, cancelable: true }),\n );\n }\n\n break;\n }\n\n case 'Tab':\n if (this.open) {\n const $first = this._$listbox?.navigableItems[0];\n\n if ($first) {\n $first.dispatchEvent(\n new PointerEvent('pointerdown', { bubbles: true, cancelable: true }),\n );\n }\n\n this.closePopup(false);\n }\n\n break;\n\n case 'Escape':\n e.preventDefault();\n this._filterText = '';\n\n if (this._$trigger) {\n this._$trigger.value = '';\n }\n\n this.closePopup();\n break;\n\n case 'Backspace':\n if (this._filterText === '' && this.multiple && this._selectedValues.size > 0) {\n e.preventDefault();\n this._focusLastChipRemove();\n }\n\n break;\n\n case 'ArrowLeft':\n if (\n (e.target as HTMLInputElement).selectionStart === 0 &&\n this.multiple &&\n this._selectedValues.size > 0\n ) {\n e.preventDefault();\n this._focusLastChipRemove();\n }\n\n break;\n }\n }\n\n private _focusLastChipRemove() {\n const $buttons = Array.from(\n this.renderRoot.querySelectorAll<HTMLButtonElement>('button[part~=\"chip\"]'),\n );\n\n $buttons[$buttons.length - 1]?.focus();\n }\n\n private get _inputPlaceholder(): string {\n if (this.multiple && this._selectedValues.size > 0) {\n return '';\n }\n\n return this.placeholder;\n }\n\n protected override render() {\n const showChips = this.multiple && this._selectedValues.size > 0;\n\n return html`\n <div id=\"anchor\" part=\"anchor\" @click=${this._handleAnchorClick}>\n ${showChips ? this._renderChips() : nothing}\n\n <input\n id=\"trigger\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this.open ? 'true' : 'false'}\n aria-controls=\"listbox\"\n aria-autocomplete=\"list\"\n ?disabled=${this.disabled}\n placeholder=${this._inputPlaceholder}\n .value=${this._filterText}\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=${this._handleInput}\n @keydown=${this._handleInputKeyDown}\n @focus=${this._handleInputFocus}\n />\n\n <button\n id=\"toggle\"\n type=\"button\"\n part=\"toggle\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n @click=${this._handleToggleClick}\n >\n <slot name=\"toggle-icon\">\n <svg\n width=\"10\"\n height=\"6\"\n viewBox=\"0 0 10 6\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1,1 5,5 9,1\" />\n </svg>\n </slot>\n </button>\n </div>\n\n <rc-listbox\n id=\"listbox\"\n part=\"listbox\"\n popover=\"manual\"\n ?multiple=${this.multiple}\n checkmark\n .filterStrategy=${this.filterStrategy}\n @rc-listbox-change=${this._handleListboxChange}\n ></rc-listbox>\n\n <slot @slotchange=${this._handleSelectSlotChange}></slot>\n `;\n }\n}\n\nexport default RCCombobox;\n"],"names":["comboboxStyles","css","_RCCombobox","RCSelect","_returnFocus","e","$target","trimmed","hasExact","o","detail","text","createEvent","value","label","active","$first","$buttons","showChips","html","nothing","RCCombobox","__decorateClass","property","query","state"],"mappings":"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"}
|
package/dist/rc-combobox.js
CHANGED
|
@@ -10,13 +10,17 @@ declare global {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Editable combobox with filtering and optional allow-create behavior, configured from
|
|
14
|
+
* native option data and following the WAI-ARIA Combobox pattern.
|
|
14
15
|
*
|
|
15
16
|
* Extends `rc-select` by replacing the trigger `<div>` with a text `<input>`
|
|
16
17
|
* and adding: live filtering of the listbox, keyboard navigation from input,
|
|
17
18
|
* and an optional "Create '{text}'" option for new entries.
|
|
18
19
|
*
|
|
19
|
-
* @
|
|
20
|
+
* @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-combobox rc-combobox docs}
|
|
21
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ WAI-ARIA Combobox pattern}
|
|
22
|
+
*
|
|
23
|
+
* @slot - Required. A native `<select>` element for form submission.
|
|
20
24
|
* @slot toggle-icon - Optional. Replaces the default chevron icon.
|
|
21
25
|
*
|
|
22
26
|
* @fires rc-select-change - Inherited selection change event.
|
|
@@ -31,6 +35,8 @@ declare global {
|
|
|
31
35
|
* @csspart input - The text input element.
|
|
32
36
|
* @csspart toggle - The chevron toggle button.
|
|
33
37
|
*
|
|
38
|
+
* @attr [allow-create] - When present, shows a "Create 'X'" option for unmatched input.
|
|
39
|
+
*
|
|
34
40
|
* @cssprop [--rc-combobox-max-height=20em] - Maximum popup height.
|
|
35
41
|
* @cssprop [--rc-combobox-control-block-size=var(--rc-control-block-size)] - Anchor block size.
|
|
36
42
|
* @cssprop [--rc-combobox-padding-block=calc(var(--rc-control-padding-block) / 2)] - Anchor block-axis padding.
|
|
@@ -43,16 +49,19 @@ declare global {
|
|
|
43
49
|
* @cssprop [--rc-combobox-chip-radius=var(--rc-radius-md)] - Multi-select chip border radius.
|
|
44
50
|
* @cssprop [--rc-combobox-chip-padding-block=0.1em] - Multi-select chip block-axis padding.
|
|
45
51
|
* @cssprop [--rc-combobox-chip-padding-inline=0.3em] - Multi-select chip inline-axis padding.
|
|
46
|
-
* @attr [allowcreate] - When present, shows a "Create 'X'" option for unmatched input.
|
|
47
52
|
*/
|
|
48
53
|
export declare class RCCombobox extends RCSelect {
|
|
49
54
|
static styles: import('lit').CSSResult;
|
|
50
55
|
/** When set, shows a "Create '{text}'" option for text that has no exact match. */
|
|
51
56
|
allowCreate: boolean;
|
|
52
57
|
/**
|
|
53
|
-
* How option labels are matched against typed input.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
58
|
+
* How option labels are matched against typed input.
|
|
59
|
+
*
|
|
60
|
+
* - Forwarded to the internal `rc-listbox`.
|
|
61
|
+
* - Defaults to `'contains'` (substring).
|
|
62
|
+
* - Set to `'prefix'` for starts-with matching, or
|
|
63
|
+
* - Pass a custom `(label, query) => boolean` predicate.
|
|
64
|
+
*
|
|
56
65
|
* Function values are JS-only; string values may be set via the `filter-strategy` attribute.
|
|
57
66
|
*/
|
|
58
67
|
filterStrategy: FilterStrategy;
|
package/package.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
7
|
-
"description": "
|
|
6
|
+
"version": "0.3.0",
|
|
7
|
+
"description": "Editable combobox with filtering and optional allow-create behavior, configured from native option data.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/richardcarls/rc-webcomponents.git",
|
|
11
11
|
"directory": "packages/rc-combobox"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://github.
|
|
13
|
+
"homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-combobox",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"files": [
|
|
@@ -37,23 +37,20 @@
|
|
|
37
37
|
],
|
|
38
38
|
"customElements": "dist/custom-elements.json",
|
|
39
39
|
"scripts": {
|
|
40
|
-
"dev": "vite",
|
|
41
40
|
"build": "tsc && vite build && cem analyze",
|
|
42
41
|
"cem:analyze": "cem analyze",
|
|
43
42
|
"preview": "vite preview",
|
|
44
|
-
"test:browser": "vitest",
|
|
45
|
-
"test:browser:chrome": "vitest --project=chromium",
|
|
46
|
-
"test:browser:firefox": "vitest --project=firefox"
|
|
47
|
-
"yalc:publish": "yalc publish --push"
|
|
43
|
+
"test:browser": "vitest --run",
|
|
44
|
+
"test:browser:chrome": "vitest --run --project=chromium",
|
|
45
|
+
"test:browser:firefox": "vitest --run --project=firefox"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
50
|
-
"@rcarls/rc-common": "workspace
|
|
51
|
-
"@rcarls/rc-listbox": "workspace
|
|
52
|
-
"@rcarls/rc-select": "workspace
|
|
48
|
+
"@rcarls/rc-common": "workspace:*",
|
|
49
|
+
"@rcarls/rc-listbox": "workspace:*",
|
|
50
|
+
"@rcarls/rc-select": "workspace:*"
|
|
53
51
|
},
|
|
54
52
|
"devDependencies": {
|
|
55
53
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
56
|
-
"@guanghechen/rollup-plugin-copy": "^6.0.9",
|
|
57
54
|
"@vitest/browser-playwright": "4.1.5",
|
|
58
55
|
"lit": "^3.0.0",
|
|
59
56
|
"playwright": "^1.56.0",
|
package/dist/demo.css
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
*, *::before, *::after {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
:root {
|
|
6
|
-
color-scheme: light dark;
|
|
7
|
-
--rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
@supports (color: AccentColor) {
|
|
11
|
-
:root {
|
|
12
|
-
--rc-accent: AccentColor;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
[data-theme="light"] { color-scheme: light; }
|
|
17
|
-
[data-theme="dark"] { color-scheme: dark; }
|
|
18
|
-
|
|
19
|
-
[data-theme="solarized-light"] {
|
|
20
|
-
color-scheme: light;
|
|
21
|
-
--rc-surface: #fdf6e3;
|
|
22
|
-
--rc-text: #657b83;
|
|
23
|
-
--rc-border: 1px solid #93a1a1;
|
|
24
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
[data-theme="solarized-dark"] {
|
|
28
|
-
color-scheme: dark;
|
|
29
|
-
--rc-surface: #002b36;
|
|
30
|
-
--rc-text: #839496;
|
|
31
|
-
--rc-border: 1px solid #586e75;
|
|
32
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
body {
|
|
36
|
-
font-family: system-ui, sans-serif;
|
|
37
|
-
margin: 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.demo-page {
|
|
41
|
-
max-width: 60rem;
|
|
42
|
-
margin: 0 auto;
|
|
43
|
-
padding: 2rem 1.5rem;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.demo-controls {
|
|
47
|
-
display: flex;
|
|
48
|
-
align-items: center;
|
|
49
|
-
gap: 1rem;
|
|
50
|
-
margin-bottom: 2rem;
|
|
51
|
-
padding-bottom: 1rem;
|
|
52
|
-
border-bottom: 1px solid ButtonBorder;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.demo-controls .demo-title {
|
|
56
|
-
flex: 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.demo-controls h1 {
|
|
60
|
-
margin: 0 0 0.15rem;
|
|
61
|
-
font-size: 1.5rem;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.demo-controls a {
|
|
65
|
-
font-size: 0.8rem;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.demo-section {
|
|
69
|
-
margin-bottom: 2rem;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.demo-section h2 {
|
|
73
|
-
margin: 0 0 0.5rem;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.theme-picker {
|
|
77
|
-
padding: 0.35em 0.75em;
|
|
78
|
-
font-family: inherit;
|
|
79
|
-
font-size: 0.8rem;
|
|
80
|
-
cursor: pointer;
|
|
81
|
-
border: 1px solid ButtonBorder;
|
|
82
|
-
border-radius: 4px;
|
|
83
|
-
background: Canvas;
|
|
84
|
-
color: CanvasText;
|
|
85
|
-
}
|
package/dist/demo.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
|
|
2
|
-
const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
|
|
3
|
-
|
|
4
|
-
const stored = localStorage.getItem('rc-demo-theme') ?? '';
|
|
5
|
-
applyTheme(stored);
|
|
6
|
-
|
|
7
|
-
function applyTheme(theme) {
|
|
8
|
-
if (theme) {
|
|
9
|
-
document.documentElement.dataset.theme = theme;
|
|
10
|
-
} else {
|
|
11
|
-
delete document.documentElement.dataset.theme;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function currentIndex() {
|
|
16
|
-
const current = document.documentElement.dataset.theme ?? '';
|
|
17
|
-
const idx = THEMES.indexOf(current);
|
|
18
|
-
return idx === -1 ? 0 : idx;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function updateButtons() {
|
|
22
|
-
const label = LABELS[currentIndex()];
|
|
23
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
24
|
-
btn.textContent = label;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
window.cycleTheme = function () {
|
|
29
|
-
const next = THEMES[(currentIndex() + 1) % THEMES.length];
|
|
30
|
-
applyTheme(next);
|
|
31
|
-
localStorage.setItem('rc-demo-theme', next);
|
|
32
|
-
updateButtons();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
36
|
-
updateButtons();
|
|
37
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
38
|
-
btn.addEventListener('click', window.cycleTheme);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rc-combobox-D6aALkDz.js","sources":["../src/rc-combobox.styles.ts","../src/rc-combobox.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const comboboxStyles = css`\n :host {\n display: inline-block;\n }\n\n #anchor {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--rc-combobox-gap, var(--rc-control-gap, 0.25em));\n min-block-size: var(--rc-combobox-control-block-size, var(--rc-control-block-size, auto));\n border: var(--rc-combobox-border, var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder)));\n border-radius: var(--rc-combobox-radius, var(--rc-control-radius, var(--rc-radius-sm, 0.125em)));\n background: var(--rc-field, Field);\n color: var(--rc-field-text, FieldText);\n padding: var(--rc-combobox-padding-block, calc(var(--rc-control-padding-block, 0.25em) / 2))\n var(--rc-combobox-padding-inline, calc(var(--rc-control-padding-inline, 0.5em) / 2));\n cursor: text;\n font-family: var(--rc-font-family, inherit);\n font-size: var(--rc-font-size, inherit);\n line-height: var(--rc-line-height, normal);\n transition:\n background-color var(--rc-motion-duration, 120ms),\n border-color var(--rc-motion-duration, 120ms),\n box-shadow var(--rc-motion-duration, 120ms);\n }\n\n /* Chips — the whole chip is the remove button for a larger touch target */\n [part='chip'] {\n display: inline-flex;\n align-items: center;\n gap: var(--rc-combobox-chip-gap, calc(var(--rc-control-gap, 0.25em) * 0.8));\n padding: var(--rc-combobox-chip-padding-block, 0.1em)\n var(--rc-combobox-chip-padding-inline, 0.3em);\n border: var(--rc-combobox-chip-border, var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder)));\n border-radius: var(--rc-combobox-chip-radius, var(--rc-radius-md, 0.25em));\n background: var(--rc-button-bg, ButtonFace);\n color: var(--rc-button-text, ButtonText);\n font: inherit;\n font-size: 0.875em;\n cursor: pointer;\n\n &:hover {\n background: var(--rc-highlight, Highlight);\n color: var(--rc-highlight-text, HighlightText);\n }\n\n &:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n }\n\n /* Decorative × icon inside the chip — no interaction, aria-hidden */\n [part='chip-remove'] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1em;\n font-size: 0.85em;\n pointer-events: none;\n }\n\n #trigger {\n flex: 1;\n min-width: 6em;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n outline: none;\n padding: var(--rc-combobox-input-padding-block, var(--rc-control-padding-block, 0.25em))\n var(--rc-combobox-input-padding-inline, calc(var(--rc-control-padding-inline, 0.5em) / 2));\n cursor: text;\n }\n\n #trigger::placeholder {\n color: var(--rc-text-disabled, GrayText);\n }\n\n #toggle {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: var(--rc-combobox-toggle-padding, var(--rc-control-padding-block, 0.25em));\n border: none;\n background: transparent;\n color: inherit;\n cursor: default;\n font-size: 0.75em;\n font: inherit;\n\n &:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n }\n\n /* Hidden native select slot */\n slot[name='select'] {\n display: none;\n }\n\n /* Listbox popup — positioned by AnchorController via adoptedStyleSheets */\n rc-listbox {\n max-height: var(--rc-combobox-max-height, 20em);\n overflow-y: auto;\n background: var(--rc-surface, Canvas);\n border: var(--rc-combobox-listbox-border, var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder)));\n border-radius: var(--rc-combobox-listbox-radius, var(--rc-control-radius, 0));\n box-shadow: var(--rc-combobox-shadow, var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent)));\n color: var(--rc-field-text, FieldText);\n padding-block: var(--rc-combobox-listbox-padding-block, var(--rc-control-padding-block, 0.25em));\n\n &:not(:popover-open) {\n display: none;\n }\n }\n\n rc-listbox [part~='option'] {\n display: flex;\n align-items: center;\n gap: var(--rc-item-gap, 0.4em);\n padding: var(--rc-item-padding-block, 0.3em) var(--rc-item-padding-inline, 0.75em);\n cursor: default;\n\n /* display: flex overrides [hidden]'s browser-default display:none — restore it explicitly */\n &[hidden] { display: none; }\n\n &:not([hidden]):not([aria-disabled='true']):hover {\n background: var(--rc-highlight, Highlight);\n color: var(--rc-highlight-text, HighlightText);\n }\n\n &[data-active]:not([aria-disabled='true']) {\n background: var(--rc-highlight, Highlight);\n color: var(--rc-highlight-text, HighlightText);\n outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));\n outline-offset: -2px;\n }\n\n &[aria-disabled='true'] {\n opacity: var(--rc-disabled-opacity, 0.5);\n cursor: not-allowed;\n }\n }\n\n rc-listbox [part~='option-checkmark'] {\n flex-shrink: 0;\n width: 1em;\n text-align: center;\n font-size: 0.85em;\n visibility: hidden;\n }\n\n rc-listbox [part~='option'][aria-selected='true'] [part~='option-checkmark'] {\n visibility: visible;\n }\n\n rc-listbox [part~='create-option'] {\n font-style: italic;\n border-top: 1px solid var(--rc-border-color, ButtonBorder);\n margin-top: 0.25em;\n padding-top: 0.3em;\n }\n`;\n\nexport default comboboxStyles;\n","import { html, nothing } from 'lit';\nimport { property, query, state } from 'lit/decorators.js';\nimport { RCSelect } from '@rcarls/rc-select';\nimport type { FilterStrategy } from '@rcarls/rc-listbox';\nimport { comboboxStyles } from './rc-combobox.styles.js';\n\nexport interface RCComboboxCreateEvent {\n /** The text typed by the user that didn't match any existing option. */\n text: string;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-combobox': RCCombobox;\n }\n}\n\n/**\n * An editable combobox with autocomplete filtering and optional allow-create.\n *\n * Extends `rc-select` by replacing the trigger `<div>` with a text `<input>`\n * and adding: live filtering of the listbox, keyboard navigation from input,\n * and an optional \"Create '{text}'\" option for new entries.\n *\n * @slot select - Required. A native `<select>` element for form submission.\n * @slot toggle-icon - Optional. Replaces the default chevron icon.\n *\n * @fires rc-select-change - Inherited selection change event.\n * @fires rc-combobox-create - When the \"Create\" option is activated.\n * `detail: { text: string }`. Cancelable — call `preventDefault()` to stop\n * the default insertion of the new option.\n *\n * @csspart anchor - Outer container (includes chips + input + toggle).\n * @csspart chip - Individual chip (multiple mode).\n * @csspart chip-label - Text label inside a chip.\n * @csspart chip-remove - Remove button inside a chip.\n * @csspart input - The text input element.\n * @csspart toggle - The chevron toggle button.\n *\n * @cssprop [--rc-combobox-max-height=20em] - Maximum popup height.\n * @cssprop [--rc-combobox-control-block-size=var(--rc-control-block-size)] - Anchor block size.\n * @cssprop [--rc-combobox-padding-block=calc(var(--rc-control-padding-block) / 2)] - Anchor block-axis padding.\n * @cssprop [--rc-combobox-padding-inline=calc(var(--rc-control-padding-inline) / 2)] - Anchor inline-axis padding.\n * @cssprop [--rc-combobox-gap=var(--rc-control-gap)] - Gap between chips, input, and toggle.\n * @cssprop [--rc-combobox-radius=var(--rc-control-radius)] - Anchor border radius.\n * @cssprop [--rc-combobox-border=var(--rc-border)] - Anchor border.\n * @cssprop [--rc-combobox-listbox-radius=var(--rc-control-radius)] - Popup listbox border radius.\n * @cssprop [--rc-combobox-listbox-padding-block=var(--rc-control-padding-block)] - Popup listbox block padding.\n * @cssprop [--rc-combobox-chip-radius=var(--rc-radius-md)] - Multi-select chip border radius.\n * @cssprop [--rc-combobox-chip-padding-block=0.1em] - Multi-select chip block-axis padding.\n * @cssprop [--rc-combobox-chip-padding-inline=0.3em] - Multi-select chip inline-axis padding.\n * @attr [allowcreate] - When present, shows a \"Create 'X'\" option for unmatched input.\n */\nexport class RCCombobox extends RCSelect {\n static override styles = comboboxStyles;\n\n /** When set, shows a \"Create '{text}'\" option for text that has no exact match. */\n @property({ type: Boolean, attribute: 'allowcreate' }) allowCreate = false;\n\n /**\n * How option labels are matched against typed input. Forwarded to the internal `rc-listbox`.\n * Defaults to `'contains'` (substring). Set to `'prefix'` for starts-with matching,\n * or pass a custom `(label, query) => boolean` predicate.\n * Function values are JS-only; string values may be set via the `filter-strategy` attribute.\n */\n @property({ attribute: 'filter-strategy', reflect: false }) filterStrategy: FilterStrategy = 'contains';\n\n @query('#trigger') protected override _$trigger!: HTMLInputElement;\n\n @state() private _filterText = '';\n\n // Guard against _handleInputFocus re-opening the popup immediately after close.\n // hidePopover() can trigger a browser-native focus-return to the input in Firefox,\n // so we use setTimeout(0) to defer the reset past any such focus events.\n private _closingPopup = false;\n\n // ── Override popup lifecycle ──────────────────────────────────────────────────\n\n override openPopup() {\n super.openPopup();\n this._$listbox?.filterOptions(this._filterText);\n }\n\n override closePopup(_returnFocus = true) {\n this._filterText = '';\n this._$listbox?.clearFilter();\n this._$listbox?.setCreateOption(null);\n this._closingPopup = true;\n super.closePopup(false);\n // Reset after any native focus-return from hidePopover() fires\n setTimeout(() => { this._closingPopup = false; }, 0);\n }\n\n // ── Input events ──────────────────────────────────────────────────────────────\n\n private _handleInput(e: InputEvent) {\n this._filterText = (e.target as HTMLInputElement).value;\n if (!this.open && this._filterText) this.openPopup();\n this._$listbox?.filterOptions(this._filterText);\n this._updateCreateOption();\n if (this._$listbox?.navigableItems.length) {\n this._adc.navigateToFirst();\n } else {\n this._adc.clear();\n }\n }\n\n private _handleInputFocus() {\n if (!this.open && !this._closingPopup) this.openPopup();\n }\n\n private _handleToggleClick(e: MouseEvent) {\n e.stopPropagation();\n if (this.open) this.closePopup();\n else {\n this.openPopup();\n this._$trigger?.focus();\n }\n }\n\n private _handleAnchorClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (target.closest('[part~=\"chip\"]') || (target as HTMLElement & { id?: string }).id === 'toggle') return;\n this._$trigger?.focus();\n }\n\n // ── Create option ─────────────────────────────────────────────────────────────\n\n private _updateCreateOption() {\n if (!this.allowCreate || !this._filterText.trim()) {\n this._$listbox?.setCreateOption(null);\n return;\n }\n const trimmed = this._filterText.trim().toLowerCase();\n const hasExact = this._$listbox?.allOptions.some(\n (o) => o.label.toLowerCase() === trimmed\n ) ?? false;\n this._$listbox?.setCreateOption(hasExact ? null : this._filterText.trim());\n }\n\n protected override _handleListboxChange(e: CustomEvent) {\n const { optionValue, value } = e.detail as {\n optionValue?: string;\n value: string | string[];\n selected: boolean;\n };\n const activatedValue = optionValue ?? (Array.isArray(value) ? value.at(-1) : value);\n if (activatedValue === '__create__') {\n e.stopPropagation();\n void this._activateCreate(this._filterText.trim());\n return;\n }\n super._handleListboxChange(e);\n if (!this.multiple) {\n this._syncInputToSelection();\n } else {\n this._filterText = '';\n if (this._$trigger) this._$trigger.value = '';\n this._$listbox?.clearFilter();\n this._updateCreateOption();\n }\n }\n\n private async _activateCreate(text: string) {\n if (!text) return;\n const createEvent = new CustomEvent<RCComboboxCreateEvent>('rc-combobox-create', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: { text },\n });\n if (!this.dispatchEvent(createEvent)) return;\n\n this._addOption({ value: text, label: text });\n\n if (this.multiple) {\n this._$listbox?.toggleOption(text);\n this._filterText = '';\n if (this._$trigger) this._$trigger.value = '';\n this._$listbox?.clearFilter();\n this._$listbox?.setCreateOption(null);\n return;\n }\n\n this._applySelection([text]);\n this._syncInputToSelection();\n this.closePopup(true);\n this._dispatchChange();\n }\n\n override set value(value: string | string[] | undefined) {\n super.value = value;\n this._syncInputToSelection();\n }\n\n override get value(): string | string[] {\n return super.value;\n }\n\n private _syncInputToSelection(): void {\n if (this.multiple) return;\n\n const value = this.selectedValues[0];\n const label = value ? this._labelFor(value) : '';\n\n this._filterText = label;\n if (this._$trigger) this._$trigger.value = label;\n }\n\n // ── Keyboard ──────────────────────────────────────────────────────────────────\n\n private _handleInputKeyDown(e: KeyboardEvent) {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!this.open) { this.openPopup(); this._adc.navigateToFirst(); }\n else this._adc.navigate(1);\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (this.open) this._adc.navigate(-1);\n break;\n case 'Home':\n if (this.open) { e.preventDefault(); this._adc.navigateToFirst(); }\n break;\n case 'End':\n if (this.open) { e.preventDefault(); this._adc.navigateToLast(); }\n break;\n case 'Enter': {\n e.preventDefault();\n const active = this._adc.activeItem;\n if (active) {\n active.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true }));\n } else if (this.open && this._$listbox?.navigableItems.length) {\n const first = this._$listbox.navigableItems[0];\n first?.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true }));\n }\n break;\n }\n case 'Tab':\n if (this.open) {\n const first = this._$listbox?.navigableItems[0];\n if (first) {\n first.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, cancelable: true }));\n }\n this.closePopup(false);\n }\n break;\n case 'Escape':\n e.preventDefault();\n this._filterText = '';\n if (this._$trigger) this._$trigger.value = '';\n this.closePopup();\n break;\n case 'Backspace':\n if (this._filterText === '' && this.multiple && this._selectedValues.size > 0) {\n e.preventDefault();\n this._focusLastChipRemove();\n }\n break;\n case 'ArrowLeft':\n if ((e.target as HTMLInputElement).selectionStart === 0\n && this.multiple && this._selectedValues.size > 0) {\n e.preventDefault();\n this._focusLastChipRemove();\n }\n break;\n }\n }\n\n private _focusLastChipRemove() {\n const buttons = Array.from(\n this.renderRoot.querySelectorAll<HTMLButtonElement>('button[part~=\"chip\"]')\n );\n buttons[buttons.length - 1]?.focus();\n }\n\n private get _inputPlaceholder(): string {\n if (this.multiple && this._selectedValues.size > 0) return '';\n return this.placeholder;\n }\n\n // ── Render ────────────────────────────────────────────────────────────────────\n\n protected override render() {\n const showChips = this.multiple && this._selectedValues.size > 0;\n\n return html`\n <div id=\"anchor\" part=\"anchor\" @click=${this._handleAnchorClick}>\n ${showChips ? this._renderChips() : nothing}\n\n <input\n id=\"trigger\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded=${this.open ? 'true' : 'false'}\n aria-controls=\"listbox\"\n aria-autocomplete=\"list\"\n ?disabled=${this.disabled}\n placeholder=${this._inputPlaceholder}\n .value=${this._filterText}\n autocomplete=\"off\"\n spellcheck=\"false\"\n @input=${this._handleInput}\n @keydown=${this._handleInputKeyDown}\n @focus=${this._handleInputFocus}\n >\n\n <button\n id=\"toggle\"\n type=\"button\"\n part=\"toggle\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n @click=${this._handleToggleClick}\n >\n <slot name=\"toggle-icon\">▼</slot>\n </button>\n </div>\n\n <rc-listbox\n id=\"listbox\"\n part=\"listbox\"\n popover=\"manual\"\n ?multiple=${this.multiple}\n checkmark\n .filterStrategy=${this.filterStrategy}\n @rc-listbox-change=${this._handleListboxChange}\n ></rc-listbox>\n\n <slot name=\"select\" @slotchange=${this._handleSelectSlotChange}></slot>\n `;\n }\n}\n\nexport default RCCombobox;\n"],"names":["comboboxStyles","css","_RCCombobox","RCSelect","_returnFocus","e","target","trimmed","hasExact","o","optionValue","value","text","createEvent","label","active","first","buttons","showChips","html","nothing","RCCombobox","__decorateClass","property","query","state"],"mappings":"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"}
|