@rcarls/rc-search-bar 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,8 @@
1
1
  # `@rcarls/rc-search-bar`
2
2
 
3
- Enhances a consumer-provided native `<input type="search">` with leading icon
4
- chrome, an accessible clear button, and debounced search events.
3
+ Search field wrapper for a native `<input type="search">` with icon chrome, clear button, and debounced events.
5
4
 
6
- The native input is **required** and stays in light DOM as the source of
7
- truth: form submission (`name`), label association (`label[for]`, wrapping
8
- `<label>`, `aria-label`), and pre-upgrade usability all keep working. Without
9
- a slotted search input the component renders no chrome and stays inert.
5
+ Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-search-bar](https://richardcarls.github.io/rc-webcomponents/components/rc-search-bar).
10
6
 
11
7
  ---
12
8
 
@@ -40,10 +36,9 @@ import { RCSearchBar } from '@rcarls/rc-search-bar';
40
36
  </search>
41
37
 
42
38
  <script>
43
- document.querySelector('rc-search-bar').addEventListener(
44
- 'rc-search-bar-input',
45
- (e) => console.log(e.detail.value),
46
- );
39
+ document
40
+ .querySelector('rc-search-bar')
41
+ .addEventListener('rc-search-bar-input', (e) => console.log(e.detail.value));
47
42
  </script>
48
43
  ```
49
44
 
@@ -56,67 +51,58 @@ The native WebKit cancel button is suppressed automatically. Set
56
51
 
57
52
  ---
58
53
 
59
- ## Controlled and uncontrolled value
60
-
61
- - **Uncontrolled** — the input (and the user) own the value. Optionally seed
62
- it with `default-value`; an author `value` attribute on the input wins over
63
- `default-value`.
64
- - **Controlled** — assign the `value` property. Host writes are silent (no
65
- events) and win over slotted author values, including across re-slots.
66
- User interaction always dispatches events.
67
-
68
- ---
69
-
70
54
  ## API
71
55
 
72
56
  ### Properties / attributes
73
57
 
74
- | Property | Attribute | Type | Default | Description |
75
- |---|---|---|---|---|
76
- | `value` | — | `string` | `''` | Current search value; reads from the native input, host writes are silent |
77
- | `defaultValue` | `default-value` | `string \| undefined` | `undefined` | Initial uncontrolled value hint, applied once |
78
- | `debounce` | `debounce` | `number` | `200` | Debounce window in ms for `rc-search-bar-input`; `0` dispatches synchronously |
79
- | `clearLabel` | `clear-label` | `string` | `'Clear search'` | Accessible label for the clear button |
80
- | `allowNativeClear` | `allow-native-clear` | `boolean` | `false` | When set, restores the browser's native WebKit cancel button |
81
- | `showClearOnFocus` | `show-clear-on-focus` | `boolean` | `false` | When set, shows the clear button on focus even with no value (Apple HIG cancel pattern) |
82
- | `placeholder` | `placeholder` | `string \| undefined` | `undefined` | Mirrored onto the input unless the author already set one |
58
+ | Property | Attribute | Type | Default | Description |
59
+ | ------------------ | --------------------- | --------------------- | ---------------- | --------------------------------------------------------------------------------------- |
60
+ | `value` | — | `string` | `''` | Current search value; reads from the native input, host writes are silent |
61
+ | `defaultValue` | `default-value` | `string \| undefined` | `undefined` | Initial uncontrolled value hint, applied once |
62
+ | `debounce` | `debounce` | `number` | `200` | Debounce window in ms for `rc-search-bar-input`; `0` dispatches synchronously |
63
+ | `clearLabel` | `clear-label` | `string` | `'Clear search'` | Accessible label for the clear button |
64
+ | `allowNativeClear` | `allow-native-clear` | `boolean` | `false` | When set, restores the browser's native WebKit cancel button |
65
+ | `showClearOnFocus` | `show-clear-on-focus` | `boolean` | `false` | When set, shows the clear button on focus even with no value (Apple HIG cancel pattern) |
66
+ | `placeholder` | `placeholder` | `string \| undefined` | `undefined` | Mirrored onto the input unless the author already set one |
83
67
 
84
68
  ### CSS custom properties
85
69
 
86
- | Property | Default | Description |
87
- |---|---|---|
88
- | `--rc-search-bar-bg` | `Field` | Wrapper background |
89
- | `--rc-search-bar-color` | `FieldText` | Wrapper text color |
90
- | `--rc-search-bar-icon-color` | `GrayText` | Leading icon color |
91
- | `--rc-search-bar-clear-color` | `GrayText` | Clear button glyph color |
92
- | `--rc-search-bar-radius` | `var(--rc-control-radius, 0)` | Wrapper border radius |
93
- | `--rc-search-bar-height` | `48px` | Wrapper block size |
94
- | `--rc-search-bar-padding-inline` | `0.5em` | Wrapper horizontal padding |
95
- | `--rc-search-bar-gap` | `0.25em` | Gap between icon, input, and clear button |
70
+ | Property | Default | Description |
71
+ | -------------------------------- | ------------------------------------------- | ----------------------------------------- |
72
+ | `--rc-search-bar-border` | `1px solid ButtonBorder` | Border; M3 theme uses elevation instead |
73
+ | `--rc-search-bar-shadow` | `none` | Elevation shadow; M3 sets Level 1 at rest |
74
+ | `--rc-search-bar-bg` | `Field` | Wrapper background |
75
+ | `--rc-search-bar-color` | `FieldText` | Wrapper text color |
76
+ | `--rc-search-bar-icon-color` | `GrayText` | Leading icon color |
77
+ | `--rc-search-bar-clear-color` | `GrayText` | Clear button glyph color |
78
+ | `--rc-search-bar-radius` | `var(--rc-control-radius, 0.125em)` | Wrapper border radius |
79
+ | `--rc-search-bar-height` | `var(--rc-control-block-size, 2.5rem)` | Wrapper block size |
80
+ | `--rc-search-bar-padding-inline` | `var(--rc-control-padding-inline, 0.75rem)` | Wrapper horizontal padding |
81
+ | `--rc-search-bar-gap` | `var(--rc-control-gap, 0.25em)` | Gap between icon, input, and clear button |
96
82
 
97
83
  ### CSS parts
98
84
 
99
- | Part | Element | Description |
100
- |---|---|---|
101
- | `root` | wrapper `div` | The field chrome |
102
- | `leading` | `span` | Wrapper around the leading icon slot |
103
- | `clear` | `button` | The clear button |
85
+ | Part | Element | Description |
86
+ | --------- | ------------- | ------------------------------------ |
87
+ | `root` | wrapper `div` | The field chrome |
88
+ | `leading` | `span` | Wrapper around the leading icon slot |
89
+ | `clear` | `button` | The clear button |
104
90
 
105
91
  ### Slots
106
92
 
107
- | Slot | Description |
108
- |---|---|
109
- | *(default)* | The required native `<input type="search">` |
110
- | `leading` | Decorative leading icon; mark it `aria-hidden="true"` |
111
- | `clear-icon` | Optional glyph replacing the default ✕ in the clear button |
112
- | `trailing` | Optional content after the clear button (supplementary badges or actions) |
93
+ | Slot | Description |
94
+ | ------------ | ------------------------------------------------------------------------- |
95
+ | _(default)_ | The required native `<input type="search">` |
96
+ | `leading` | Decorative leading icon; mark it `aria-hidden="true"` |
97
+ | `clear-icon` | Optional glyph replacing the default ✕ in the clear button |
98
+ | `trailing` | Optional content after the clear button (supplementary badges or actions) |
113
99
 
114
100
  ### Events
115
101
 
116
- | Event | Detail | Description |
117
- |---|---|---|
102
+ | Event | Detail | Description |
103
+ | --------------------- | ------------------- | ---------------------------------------------------------------------------- |
118
104
  | `rc-search-bar-input` | `{ value: string }` | Debounced after typing; fired immediately (pending timer cancelled) on clear |
119
- | `rc-search-bar-clear` | `{}` | Fired when the clear button is activated |
105
+ | `rc-search-bar-clear` | `{}` | Fired when the clear button is activated |
120
106
 
121
107
  ---
122
108
 
@@ -138,10 +124,3 @@ The native WebKit cancel button is suppressed automatically. Set
138
124
  focused (Apple HIG cancel pattern). Consider `clear-label="Cancel"` in this
139
125
  mode. The `rc-search-bar-clear` event fires even when the value is empty,
140
126
  allowing the host to dismiss a search overlay.
141
-
142
- ---
143
-
144
- ## Browser support
145
-
146
- Evergreen browsers. The component is inert without JavaScript: the native
147
- search input remains fully usable and form-associated before upgrade.
@@ -46,9 +46,19 @@
46
46
  "declarations": [
47
47
  {
48
48
  "kind": "class",
49
- "description": "Enhances a consumer-provided native `<input type=\"search\">` with leading\nicon chrome, an accessible clear button, and debounced search events.\n\nThe native input is required and stays in light DOM as the source of\ntruth, so form submission, `label[for]`/wrapping-label association, and\npre-upgrade behavior are preserved. Without a slotted search input the\ncomponent renders no chrome and stays inert.\n\nThe native WebKit cancel button is suppressed by default via an\nadopted document stylesheet. Set `allow-native-clear` to restore it.\nWhen restored, the native button clears through the normal input path\n(a debounced `rc-search-bar-input` with an empty value) and never fires\n`rc-search-bar-clear`.",
49
+ "description": "Search field wrapper for a native <input type=\"search\"> with icon chrome, clear button,\nand debounced events.\n\nThe native input is required and stays in light DOM as the source of\ntruth, so form submission, `label[for]`/wrapping-label association, and\npre-upgrade behavior are preserved. Without a slotted search input the\ncomponent renders no chrome and stays inert.\n\nThe native WebKit cancel button is suppressed by default via an\nadopted document stylesheet. Set `allow-native-clear` to restore it.\nWhen restored, the native button clears through the normal input path\n(a debounced `rc-search-bar-input` with an empty value) and never fires\n`rc-search-bar-clear`.",
50
50
  "name": "RCSearchBar",
51
51
  "cssProperties": [
52
+ {
53
+ "description": "Wrapper border; set to `none` in M3 theme (uses elevation instead)",
54
+ "name": "--rc-search-bar-border",
55
+ "default": "1px solid ButtonBorder"
56
+ },
57
+ {
58
+ "description": "Wrapper box-shadow for elevation; M3 theme sets Level 1 at rest",
59
+ "name": "--rc-search-bar-shadow",
60
+ "default": "none"
61
+ },
52
62
  {
53
63
  "description": "Wrapper background",
54
64
  "name": "--rc-search-bar-bg",
@@ -72,22 +82,22 @@
72
82
  {
73
83
  "description": "Wrapper border radius",
74
84
  "name": "--rc-search-bar-radius",
75
- "default": "var(--rc-control-radius)"
85
+ "default": "var(--rc-control-radius,0.125em)"
76
86
  },
77
87
  {
78
88
  "description": "Wrapper block size",
79
89
  "name": "--rc-search-bar-height",
80
- "default": "48px"
90
+ "default": "var(--rc-control-block-size,2.5rem)"
81
91
  },
82
92
  {
83
93
  "description": "Wrapper horizontal padding",
84
94
  "name": "--rc-search-bar-padding-inline",
85
- "default": "0.5em"
95
+ "default": "var(--rc-control-padding-inline,0.75rem)"
86
96
  },
87
97
  {
88
98
  "description": "Gap between icon, input, and clear button",
89
99
  "name": "--rc-search-bar-gap",
90
- "default": "0.25em"
100
+ "default": "var(--rc-control-gap,0.25em)"
91
101
  },
92
102
  {
93
103
  "description": "Input font size (inherits when unset)",
@@ -177,7 +187,7 @@
177
187
  "text": "boolean"
178
188
  },
179
189
  "default": "false",
180
- "description": "When set, the clear button is visible whenever the input is focused,\neven with no value — matching the Apple HIG \"cancel\" pattern.\nConsider setting `clear-label=\"Cancel\"` in this mode.",
190
+ "description": "When set, the clear button is visible whenever the input is focused.\n\nMatching the Apple HIG \"cancel\" pattern.\nConsider setting `clear-label=\"Cancel\"` in this mode.",
181
191
  "attribute": "show-clear-on-focus"
182
192
  },
183
193
  {
@@ -196,7 +206,7 @@
196
206
  "type": {
197
207
  "text": "string | undefined"
198
208
  },
199
- "description": "Placeholder mirrored onto the native input. An author `placeholder`\nattribute already present on the input is never overwritten.",
209
+ "description": "Placeholder mirrored onto the native input.\n\nConsumer `placeholder` attribute is always honored.",
200
210
  "attribute": "placeholder"
201
211
  },
202
212
  {
@@ -247,7 +257,7 @@
247
257
  },
248
258
  {
249
259
  "kind": "field",
250
- "name": "_inputRef",
260
+ "name": "_$inputRef",
251
261
  "type": {
252
262
  "text": "WeakRef<HTMLInputElement> | null"
253
263
  },
@@ -328,49 +338,29 @@
328
338
  },
329
339
  {
330
340
  "kind": "field",
331
- "name": "_lastInteractionWasPointer",
332
- "type": {
333
- "text": "boolean"
334
- },
335
- "privacy": "private",
336
- "default": "false"
337
- },
338
- {
339
- "kind": "field",
340
- "name": "_onPointerDown",
341
- "privacy": "private",
342
- "readonly": true
343
- },
344
- {
345
- "kind": "field",
346
- "name": "_onFocusIn",
347
- "privacy": "private",
348
- "readonly": true
349
- },
350
- {
351
- "kind": "field",
352
- "name": "_onFocusOut",
341
+ "name": "_onInputFocus",
353
342
  "privacy": "private",
354
343
  "readonly": true
355
344
  },
356
345
  {
357
346
  "kind": "field",
358
- "name": "_onInputFocus",
347
+ "name": "_onInputBlur",
359
348
  "privacy": "private",
360
349
  "readonly": true
361
350
  },
362
351
  {
363
352
  "kind": "field",
364
- "name": "_onInputBlur",
353
+ "name": "_disabledObserver",
365
354
  "privacy": "private",
366
- "readonly": true
355
+ "readonly": true,
356
+ "default": "new MutationObserver(() => { const $input = this._$input(); if ($input) { this._inputDisabled = $input.disabled; } })"
367
357
  },
368
358
  {
369
359
  "kind": "field",
370
- "name": "_disabledObserver",
360
+ "name": "_inputController",
371
361
  "privacy": "private",
372
362
  "readonly": true,
373
- "default": "new MutationObserver(() => { const input = this._input(); if (input) this._inputDisabled = input.disabled; })"
363
+ "default": "new NativeChildController<HTMLInputElement>(this, { selector: ':scope > input[type=\"search\"]', observe: true, onChange: ($input, $previousInput) => this._setupInput($input, $previousInput), onMissing: () => { if (import.meta.env.DEV) { warnMissingDirectChild(this, { selector: ':scope > input[type=\"search\"]', message: '[rc-search-bar] No direct child <input type=\"search\"> found. ' + 'Place a native search input inside <rc-search-bar>.', }); } }, })"
374
364
  },
375
365
  {
376
366
  "kind": "field",
@@ -391,7 +381,7 @@
391
381
  },
392
382
  {
393
383
  "kind": "method",
394
- "name": "_input",
384
+ "name": "_$input",
395
385
  "privacy": "private",
396
386
  "return": {
397
387
  "type": {
@@ -403,6 +393,16 @@
403
393
  "kind": "method",
404
394
  "name": "_handleSlotChange",
405
395
  "privacy": "private",
396
+ "return": {
397
+ "type": {
398
+ "text": "void"
399
+ }
400
+ }
401
+ },
402
+ {
403
+ "kind": "method",
404
+ "name": "_setupInput",
405
+ "privacy": "private",
406
406
  "return": {
407
407
  "type": {
408
408
  "text": "void"
@@ -410,9 +410,15 @@
410
410
  },
411
411
  "parameters": [
412
412
  {
413
- "name": "e",
413
+ "name": "$input",
414
414
  "type": {
415
- "text": "Event"
415
+ "text": "HTMLInputElement | null"
416
+ }
417
+ },
418
+ {
419
+ "name": "$previousInput",
420
+ "type": {
421
+ "text": "HTMLInputElement | null"
416
422
  }
417
423
  }
418
424
  ]
@@ -428,7 +434,7 @@
428
434
  },
429
435
  "parameters": [
430
436
  {
431
- "name": "input",
437
+ "name": "$input",
432
438
  "type": {
433
439
  "text": "HTMLInputElement"
434
440
  }
@@ -446,7 +452,7 @@
446
452
  },
447
453
  "parameters": [
448
454
  {
449
- "name": "input",
455
+ "name": "$input",
450
456
  "type": {
451
457
  "text": "HTMLInputElement"
452
458
  }
@@ -613,7 +619,7 @@
613
619
  "text": "boolean"
614
620
  },
615
621
  "default": "false",
616
- "description": "When set, the clear button is visible whenever the input is focused,\neven with no value — matching the Apple HIG \"cancel\" pattern.\nConsider setting `clear-label=\"Cancel\"` in this mode.",
622
+ "description": "When set, the clear button is visible whenever the input is focused.\n\nMatching the Apple HIG \"cancel\" pattern.\nConsider setting `clear-label=\"Cancel\"` in this mode.",
617
623
  "fieldName": "showClearOnFocus"
618
624
  },
619
625
  {
@@ -629,7 +635,7 @@
629
635
  "type": {
630
636
  "text": "string | undefined"
631
637
  },
632
- "description": "Placeholder mirrored onto the native input. An author `placeholder`\nattribute already present on the input is never overwritten.",
638
+ "description": "Placeholder mirrored onto the native input.\n\nConsumer `placeholder` attribute is always honored.",
633
639
  "fieldName": "placeholder"
634
640
  },
635
641
  {
@@ -1,7 +1,8 @@
1
- import { css as f, LitElement as b, html as v } from "lit";
2
- import { property as r, state as o } from "lit/decorators.js";
3
- import { classMap as h } from "lit/directives/class-map.js";
4
- const g = f`
1
+ import { css as b, LitElement as f, html as v } from "lit";
2
+ import { property as r, state as l } from "lit/decorators.js";
3
+ import { classMap as d } from "lit/directives/class-map.js";
4
+ import { NativeChildController as g, keyInteraction as m } from "@rcarls/rc-common";
5
+ const y = b`
5
6
  :host {
6
7
  display: inline-flex;
7
8
  color-scheme: inherit;
@@ -16,15 +17,21 @@ const g = f`
16
17
  flex: 1 1 auto;
17
18
  align-items: center;
18
19
  min-inline-size: 0;
19
- gap: var(--rc-search-bar-gap, 0.25em);
20
- block-size: var(--rc-search-bar-height, 48px);
21
- padding-inline: var(--rc-search-bar-padding-inline, 0.5em);
22
- background: var(--rc-search-bar-bg, Field);
23
- color: var(--rc-search-bar-color, FieldText);
24
- border-radius: var(--rc-search-bar-radius, var(--rc-control-radius, 0));
20
+ gap: var(--rc-search-bar-gap, var(--rc-control-gap, 0.25em));
21
+ block-size: var(--rc-search-bar-height, var(--rc-control-block-size, 2.5rem));
22
+ padding-inline: var(--rc-search-bar-padding-inline, var(--rc-control-padding-inline, 0.75rem));
23
+ border: var(--rc-search-bar-border, var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder)));
24
+ border-radius: var(--rc-search-bar-radius, var(--rc-control-radius, var(--rc-radius-sm, 0.125em)));
25
+ box-shadow: var(--rc-search-bar-shadow, none);
26
+ background: var(--rc-search-bar-bg, var(--rc-field, Field));
27
+ color: var(--rc-search-bar-color, var(--rc-field-text, FieldText));
28
+ font-family: var(--rc-font-family, inherit);
29
+ font-size: var(--rc-font-size, inherit);
30
+ line-height: var(--rc-line-height, normal);
31
+ transition: background-color 120ms, border-color 120ms, box-shadow 120ms;
25
32
  }
26
33
 
27
- :host([data-focus-visible]) #root {
34
+ :host([data-interaction-mode='keyboard']) #root:focus-within {
28
35
  outline: var(--rc-focus-ring, auto);
29
36
  outline-offset: var(--rc-focus-ring-offset, 0);
30
37
  }
@@ -34,6 +41,8 @@ const g = f`
34
41
  block-size: auto;
35
42
  padding: 0;
36
43
  background: none;
44
+ border: none;
45
+ box-shadow: none;
37
46
  border-radius: 0;
38
47
  }
39
48
 
@@ -51,12 +60,14 @@ const g = f`
51
60
  ::slotted(input[type='search']) {
52
61
  flex: 1 1 auto;
53
62
  min-inline-size: 0;
63
+ padding: 0;
54
64
  border: none;
55
65
  background: transparent;
56
66
  color: var(--rc-search-bar-input-color, inherit);
57
67
  font-family: var(--rc-search-bar-input-font-family, inherit);
58
68
  font-size: var(--rc-search-bar-input-font-size, inherit);
59
69
  outline: none;
70
+ appearance: textfield;
60
71
  }
61
72
 
62
73
  #trailing {
@@ -84,15 +95,19 @@ const g = f`
84
95
  cursor: pointer;
85
96
  }
86
97
 
98
+ #clear[inert] {
99
+ visibility: hidden;
100
+ }
101
+
87
102
  #clear:focus-visible {
88
103
  outline: var(--rc-focus-ring, auto);
89
104
  outline-offset: var(--rc-focus-ring-offset, 0);
90
105
  }
91
106
  `;
92
- var m = Object.defineProperty, y = Object.getOwnPropertyDescriptor, a = (c, e, i, t) => {
93
- for (var n = t > 1 ? void 0 : t ? y(e, i) : e, l = c.length - 1, u; l >= 0; l--)
94
- (u = c[l]) && (n = (t ? u(e, i, n) : u(n)) || n);
95
- return t && n && m(e, i, n), n;
107
+ var I = Object.defineProperty, C = Object.getOwnPropertyDescriptor, a = (c, e, t, s) => {
108
+ for (var n = s > 1 ? void 0 : s ? C(e, t) : e, o = c.length - 1, h; o >= 0; o--)
109
+ (h = c[o]) && (n = (s ? h(e, t, n) : h(n)) || n);
110
+ return s && n && I(e, t, n), n;
96
111
  };
97
112
  const _ = new CSSStyleSheet();
98
113
  _.replaceSync(
@@ -100,85 +115,83 @@ _.replaceSync(
100
115
  { -webkit-appearance: none; display: none; }`
101
116
  );
102
117
  let p = !1;
103
- function I() {
118
+ function w() {
104
119
  p || (document.adoptedStyleSheets = [...document.adoptedStyleSheets, _], p = !0);
105
120
  }
106
- const d = class d extends b {
121
+ const u = class u extends f {
107
122
  constructor() {
108
- super(...arguments), this.debounce = 200, this.clearLabel = "Clear search", this.allowNativeClear = !1, this.showClearOnFocus = !1, this._value = void 0, this._defaultValue = void 0, this._valueSetByHost = !1, this._valueInitialized = !1, this._authorPlaceholder = !1, this._inputRef = null, this._debounceTimer = void 0, this._hasInput = !1, this._hasValue = !1, this._hasLeading = !1, this._hasTrailing = !1, this._inputDisabled = !1, this._disabledHost = void 0, this._focused = !1, this._lastInteractionWasPointer = !1, this._onPointerDown = () => {
109
- this._lastInteractionWasPointer = !0;
110
- }, this._onFocusIn = () => {
111
- this._lastInteractionWasPointer || this.toggleAttribute("data-focus-visible", !0), this._lastInteractionWasPointer = !1;
112
- }, this._onFocusOut = () => {
113
- this.toggleAttribute("data-focus-visible", !1);
114
- }, this._onInputFocus = () => {
123
+ super(...arguments), this.debounce = 200, this.clearLabel = "Clear search", this.allowNativeClear = !1, this.showClearOnFocus = !1, this._value = void 0, this._defaultValue = void 0, this._valueSetByHost = !1, this._valueInitialized = !1, this._authorPlaceholder = !1, this._$inputRef = null, this._debounceTimer = void 0, this._hasInput = !1, this._hasValue = !1, this._hasLeading = !1, this._hasTrailing = !1, this._inputDisabled = !1, this._disabledHost = void 0, this._focused = !1, this._onInputFocus = () => {
115
124
  this._focused = !0;
116
125
  }, this._onInputBlur = () => {
117
126
  this._focused = !1;
118
127
  }, this._disabledObserver = new MutationObserver(() => {
119
- const e = this._input();
128
+ const e = this._$input();
120
129
  e && (this._inputDisabled = e.disabled);
130
+ }), this._inputController = new g(this, {
131
+ selector: ':scope > input[type="search"]',
132
+ observe: !0,
133
+ onChange: (e, t) => this._setupInput(e, t),
134
+ onMissing: () => {
135
+ }
121
136
  }), this._onNativeInput = (e) => {
122
- const i = e.currentTarget;
123
- this._valueInitialized = !0, this._hasValue = i.value.length > 0, this._scheduleInputEvent(i.value);
137
+ const t = e.currentTarget;
138
+ this._valueInitialized = !0, this._hasValue = t.value.length > 0, this._scheduleInputEvent(t.value);
124
139
  };
125
140
  }
126
141
  get disabled() {
127
142
  return this._disabledHost ?? this._inputDisabled;
128
143
  }
129
144
  set disabled(e) {
130
- const i = this.disabled;
145
+ const t = this.disabled;
131
146
  this._disabledHost = e;
132
- const t = this._input();
133
- t && (t.disabled = e), this.requestUpdate("disabled", i);
147
+ const s = this._$input();
148
+ s && (s.disabled = e), this.requestUpdate("disabled", t);
134
149
  }
135
150
  get value() {
136
- return this._input()?.value ?? this._value ?? this._defaultValue ?? "";
151
+ return this._$input()?.value ?? this._value ?? this._defaultValue ?? "";
137
152
  }
138
153
  set value(e) {
139
- const i = this.value;
154
+ const t = this.value;
140
155
  this._value = e, this._valueSetByHost = !0, this._valueInitialized = !0;
141
- const t = this._input();
142
- t && (t.value = e), this._hasValue = e.length > 0, this.requestUpdate("value", i);
156
+ const s = this._$input();
157
+ s && (s.value = e), this._hasValue = e.length > 0, this.requestUpdate("value", t);
143
158
  }
144
159
  get defaultValue() {
145
160
  return this._defaultValue;
146
161
  }
147
162
  set defaultValue(e) {
148
- const i = this._defaultValue;
163
+ const t = this._defaultValue;
149
164
  this._defaultValue = e;
150
- const t = this._input();
151
- t && e !== void 0 && !this._valueInitialized && (t.value = e, this._hasValue = e.length > 0), this.requestUpdate("defaultValue", i);
165
+ const s = this._$input();
166
+ s && e !== void 0 && !this._valueInitialized && (s.value = e, this._hasValue = e.length > 0), this.requestUpdate("defaultValue", t);
152
167
  }
153
168
  connectedCallback() {
154
- super.connectedCallback(), I(), this.addEventListener("pointerdown", this._onPointerDown, { capture: !0 }), this.addEventListener("focusin", this._onFocusIn), this.addEventListener("focusout", this._onFocusOut);
155
- const e = this._input();
156
- e && this._bindInput(e);
169
+ super.connectedCallback(), w();
157
170
  }
158
171
  disconnectedCallback() {
159
- this.removeEventListener("pointerdown", this._onPointerDown, { capture: !0 }), this.removeEventListener("focusin", this._onFocusIn), this.removeEventListener("focusout", this._onFocusOut), super.disconnectedCallback(), this._focused = !1, this._cancelPendingInput();
160
- const e = this._input();
172
+ super.disconnectedCallback(), this._focused = !1, this._cancelPendingInput();
173
+ const e = this._$input();
161
174
  e?.removeEventListener("input", this._onNativeInput), e?.removeEventListener("focus", this._onInputFocus), e?.removeEventListener("blur", this._onInputBlur), this._disabledObserver.disconnect();
162
175
  }
163
176
  updated(e) {
164
177
  if (e.has("placeholder")) {
165
- const i = this._input();
166
- i && this._applyPlaceholder(i);
178
+ const t = this._$input();
179
+ t && this._applyPlaceholder(t);
167
180
  }
168
181
  }
169
- _input() {
170
- return this._inputRef?.deref() ?? null;
182
+ _$input() {
183
+ return this._$inputRef?.deref() ?? null;
184
+ }
185
+ _handleSlotChange() {
186
+ this._inputController.sync();
171
187
  }
172
- _handleSlotChange(e) {
173
- const t = e.target.assignedElements({ flatten: !0 }).find(
174
- (l) => l instanceof HTMLInputElement && l.type === "search"
175
- ) ?? null, n = this._input();
176
- if (n && n !== t && (n.removeEventListener("input", this._onNativeInput), n.removeEventListener("focus", this._onInputFocus), n.removeEventListener("blur", this._onInputBlur)), this._disabledObserver.disconnect(), this._focused = !1, this._inputRef = t ? new WeakRef(t) : null, this._hasInput = t !== null, !t) {
188
+ _setupInput(e, t) {
189
+ if (t && t !== e && (t.removeEventListener("input", this._onNativeInput), t.removeEventListener("focus", this._onInputFocus), t.removeEventListener("blur", this._onInputBlur)), this._disabledObserver.disconnect(), this._focused = !1, this._$inputRef = e ? new WeakRef(e) : null, this._hasInput = e !== null, this._authorPlaceholder = e?.hasAttribute("placeholder") ?? !1, !e) {
177
190
  this._hasValue = !1;
178
191
  return;
179
192
  }
180
193
  queueMicrotask(() => {
181
- t.isConnected && (this._valueSetByHost ? t.value = this._value ?? "" : t.value ? this._valueInitialized = !0 : this._defaultValue !== void 0 && !this._valueInitialized && (t.value = this._defaultValue), this._authorPlaceholder = t.hasAttribute("placeholder"), this._applyPlaceholder(t), this._disabledHost !== void 0 && (t.disabled = this._disabledHost), this._inputDisabled = t.disabled, this._hasValue = t.value.length > 0, this._bindInput(t));
194
+ e.isConnected && (this._valueSetByHost ? e.value = this._value ?? "" : e.value ? this._valueInitialized = !0 : this._defaultValue !== void 0 && !this._valueInitialized && (e.value = this._defaultValue), this._applyPlaceholder(e), this._disabledHost !== void 0 && (e.disabled = this._disabledHost), this._inputDisabled = e.disabled, this._hasValue = e.value.length > 0, this._bindInput(e));
182
195
  });
183
196
  }
184
197
  _bindInput(e) {
@@ -209,7 +222,7 @@ const d = class d extends b {
209
222
  );
210
223
  }
211
224
  _handleClear() {
212
- const e = this._input();
225
+ const e = this._$input();
213
226
  e && (this._cancelPendingInput(), e.value = "", this._valueInitialized = !0, this._hasValue = !1, this.dispatchEvent(
214
227
  new CustomEvent("rc-search-bar-clear", {
215
228
  bubbles: !0,
@@ -220,31 +233,31 @@ const d = class d extends b {
220
233
  }
221
234
  render() {
222
235
  return v`
223
- <div id="root" part="root" class=${h({ empty: !this._hasInput })}>
236
+ <div id="root" part="root" class=${d({ empty: !this._hasInput })} ${m({ attributeTarget: this })}>
224
237
  <span
225
238
  id="leading"
226
239
  part="leading"
227
- class=${h({ empty: !this._hasLeading || !this._hasInput })}
240
+ class=${d({ empty: !this._hasLeading || !this._hasInput })}
228
241
  >
229
242
  <slot name="leading" @slotchange=${this._onLeadingSlotChange}></slot>
230
243
  </span>
244
+
231
245
  <slot @slotchange=${this._handleSlotChange}></slot>
246
+
232
247
  <button
233
248
  id="clear"
234
249
  part="clear"
235
250
  type="button"
236
251
  aria-label=${this.clearLabel}
237
- ?hidden=${!this._hasInput || !this._hasValue && !(this.showClearOnFocus && this._focused)}
252
+ ?hidden=${!this._hasInput}
253
+ ?inert=${this._hasInput && !this._hasValue && !(this.showClearOnFocus && this._focused)}
238
254
  ?disabled=${this._inputDisabled}
239
255
  @click=${this._handleClear}
240
256
  >
241
257
  <slot name="clear-icon"><span aria-hidden="true">&#10005;</span></slot>
242
258
  </button>
243
- <span
244
- id="trailing"
245
- part="trailing"
246
- class=${h({ empty: !this._hasTrailing })}
247
- >
259
+
260
+ <span id="trailing" part="trailing" class=${d({ empty: !this._hasTrailing })}>
248
261
  <slot name="trailing" @slotchange=${this._onTrailingSlotChange}></slot>
249
262
  </span>
250
263
  </div>
@@ -255,59 +268,59 @@ const d = class d extends b {
255
268
  this._handleClear();
256
269
  }
257
270
  _onLeadingSlotChange(e) {
258
- const i = e.target;
259
- this._hasLeading = i.assignedElements().length > 0;
271
+ const t = e.target;
272
+ this._hasLeading = t.assignedElements().length > 0;
260
273
  }
261
274
  _onTrailingSlotChange(e) {
262
- const i = e.target;
263
- this._hasTrailing = i.assignedElements().length > 0;
275
+ const t = e.target;
276
+ this._hasTrailing = t.assignedElements().length > 0;
264
277
  }
265
278
  };
266
- d.styles = [g];
267
- let s = d;
279
+ u.styles = [y];
280
+ let i = u;
268
281
  a([
269
282
  r({ type: Number })
270
- ], s.prototype, "debounce", 2);
283
+ ], i.prototype, "debounce", 2);
271
284
  a([
272
285
  r({ type: String, attribute: "clear-label" })
273
- ], s.prototype, "clearLabel", 2);
286
+ ], i.prototype, "clearLabel", 2);
274
287
  a([
275
288
  r({ type: Boolean, attribute: "allow-native-clear", reflect: !0 })
276
- ], s.prototype, "allowNativeClear", 2);
289
+ ], i.prototype, "allowNativeClear", 2);
277
290
  a([
278
291
  r({ type: Boolean, attribute: "show-clear-on-focus" })
279
- ], s.prototype, "showClearOnFocus", 2);
292
+ ], i.prototype, "showClearOnFocus", 2);
280
293
  a([
281
294
  r({ type: Boolean, reflect: !0 })
282
- ], s.prototype, "disabled", 1);
295
+ ], i.prototype, "disabled", 1);
283
296
  a([
284
297
  r({ type: String })
285
- ], s.prototype, "placeholder", 2);
298
+ ], i.prototype, "placeholder", 2);
286
299
  a([
287
- o()
288
- ], s.prototype, "_hasInput", 2);
300
+ l()
301
+ ], i.prototype, "_hasInput", 2);
289
302
  a([
290
- o()
291
- ], s.prototype, "_hasValue", 2);
303
+ l()
304
+ ], i.prototype, "_hasValue", 2);
292
305
  a([
293
- o()
294
- ], s.prototype, "_hasLeading", 2);
306
+ l()
307
+ ], i.prototype, "_hasLeading", 2);
295
308
  a([
296
- o()
297
- ], s.prototype, "_hasTrailing", 2);
309
+ l()
310
+ ], i.prototype, "_hasTrailing", 2);
298
311
  a([
299
- o()
300
- ], s.prototype, "_inputDisabled", 2);
312
+ l()
313
+ ], i.prototype, "_inputDisabled", 2);
301
314
  a([
302
- o()
303
- ], s.prototype, "_focused", 2);
315
+ l()
316
+ ], i.prototype, "_focused", 2);
304
317
  a([
305
318
  r({ attribute: !1 })
306
- ], s.prototype, "value", 1);
319
+ ], i.prototype, "value", 1);
307
320
  a([
308
321
  r({ type: String, attribute: "default-value" })
309
- ], s.prototype, "defaultValue", 1);
322
+ ], i.prototype, "defaultValue", 1);
310
323
  export {
311
- s as R
324
+ i as R
312
325
  };
313
- //# sourceMappingURL=rc-search-bar-Ssb7YNqn.js.map
326
+ //# sourceMappingURL=rc-search-bar-DVczVFvA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-search-bar-DVczVFvA.js","sources":["../src/rc-search-bar.styles.ts","../src/rc-search-bar.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const searchBarStyles = css`\n :host {\n display: inline-flex;\n color-scheme: inherit;\n }\n\n [hidden] {\n display: none !important;\n }\n\n #root {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-inline-size: 0;\n gap: var(--rc-search-bar-gap, var(--rc-control-gap, 0.25em));\n block-size: var(--rc-search-bar-height, var(--rc-control-block-size, 2.5rem));\n padding-inline: var(--rc-search-bar-padding-inline, var(--rc-control-padding-inline, 0.75rem));\n border: var(--rc-search-bar-border, var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder)));\n border-radius: var(--rc-search-bar-radius, var(--rc-control-radius, var(--rc-radius-sm, 0.125em)));\n box-shadow: var(--rc-search-bar-shadow, none);\n background: var(--rc-search-bar-bg, var(--rc-field, Field));\n color: var(--rc-search-bar-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: background-color 120ms, border-color 120ms, box-shadow 120ms;\n }\n\n :host([data-interaction-mode='keyboard']) #root:focus-within {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n\n /* No slotted search input: render no chrome at all. */\n #root.empty {\n block-size: auto;\n padding: 0;\n background: none;\n border: none;\n box-shadow: none;\n border-radius: 0;\n }\n\n #leading {\n display: flex;\n flex: none;\n align-items: center;\n color: var(--rc-search-bar-icon-color, GrayText);\n }\n\n #leading.empty {\n display: none;\n }\n\n ::slotted(input[type='search']) {\n flex: 1 1 auto;\n min-inline-size: 0;\n padding: 0;\n border: none;\n background: transparent;\n color: var(--rc-search-bar-input-color, inherit);\n font-family: var(--rc-search-bar-input-font-family, inherit);\n font-size: var(--rc-search-bar-input-font-size, inherit);\n outline: none;\n appearance: textfield;\n }\n\n #trailing {\n display: flex;\n flex: none;\n align-items: center;\n }\n\n #trailing.empty {\n display: none;\n }\n\n #clear {\n display: inline-grid;\n flex: none;\n place-items: center;\n min-inline-size: 24px;\n min-block-size: 24px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: none;\n color: var(--rc-search-bar-clear-color, GrayText);\n font: inherit;\n cursor: pointer;\n }\n\n #clear[inert] {\n visibility: hidden;\n }\n\n #clear:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n`;\n\nexport default searchBarStyles;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\n\nimport { NativeChildController, keyInteraction, warnMissingDirectChild } from '@rcarls/rc-common';\n\nimport searchBarStyles from './rc-search-bar.styles';\n\nconst _uaClearSheet = new CSSStyleSheet();\n_uaClearSheet.replaceSync(\n `rc-search-bar:not([allow-native-clear]) input[type=\"search\"]::-webkit-search-cancel-button\n { -webkit-appearance: none; display: none; }`,\n);\nlet _uaClearSuppressed = false;\n\nfunction _suppressUaClear(): void {\n if (_uaClearSuppressed) {\n return;\n }\n\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, _uaClearSheet];\n\n _uaClearSuppressed = true;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-search-bar': RCSearchBar;\n }\n}\n\nexport interface RCSearchBarInputDetail {\n /** The native input's value at dispatch time. */\n value: string;\n}\n\n/**\n * Search field wrapper for a native <input type=\"search\"> with icon chrome, clear button,\n * and debounced events.\n *\n * The native input is required and stays in light DOM as the source of\n * truth, so form submission, `label[for]`/wrapping-label association, and\n * pre-upgrade behavior are preserved. Without a slotted search input the\n * component renders no chrome and stays inert.\n *\n * The native WebKit cancel button is suppressed by default via an\n * adopted document stylesheet. Set `allow-native-clear` to restore it.\n * When restored, the native button clears through the normal input path\n * (a debounced `rc-search-bar-input` with an empty value) and never fires\n * `rc-search-bar-clear`.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-search-bar rc-search-bar docs}\n *\n * @slot - The required native `<input type=\"search\">`\n * @slot leading - Decorative leading icon; mark it `aria-hidden=\"true\"`\n * @slot trailing - Optional trailing content after the clear button\n * @slot clear-icon - Optional glyph replacing the default clear glyph\n *\n * @fires rc-search-bar-input - Debounced after typing, immediate on clear;\n * `detail: { value }`\n * @fires rc-search-bar-clear - When the clear button is activated\n *\n * @cssprop [--rc-search-bar-border=1px solid ButtonBorder] - Wrapper border; set to `none` in M3 theme (uses elevation instead)\n * @cssprop [--rc-search-bar-shadow=none] - Wrapper box-shadow for elevation; M3 theme sets Level 1 at rest\n * @cssprop [--rc-search-bar-bg=Field] - Wrapper background\n * @cssprop [--rc-search-bar-color=FieldText] - Wrapper text color\n * @cssprop [--rc-search-bar-icon-color=GrayText] - Leading icon color\n * @cssprop [--rc-search-bar-clear-color=GrayText] - Clear button glyph color\n * @cssprop [--rc-search-bar-radius=var(--rc-control-radius,0.125em)] - Wrapper border radius\n * @cssprop [--rc-search-bar-height=var(--rc-control-block-size,2.5rem)] - Wrapper block size\n * @cssprop [--rc-search-bar-padding-inline=var(--rc-control-padding-inline,0.75rem)] - Wrapper horizontal padding\n * @cssprop [--rc-search-bar-gap=var(--rc-control-gap,0.25em)] - Gap between icon, input, and clear button\n * @cssprop [--rc-search-bar-input-font-size] - Input font size (inherits when unset)\n * @cssprop [--rc-search-bar-input-font-family] - Input font family (inherits when unset)\n * @cssprop [--rc-search-bar-input-color] - Input text color (inherits when unset)\n * @csspart root - The wrapper element\n * @csspart leading - Wrapper around the leading icon slot\n * @csspart trailing - Wrapper around the trailing slot\n * @csspart clear - The clear button\n */\nexport class RCSearchBar extends LitElement {\n static styles = [searchBarStyles];\n\n /** Debounce window in ms for `rc-search-bar-input`; `0` dispatches synchronously. */\n @property({ type: Number })\n debounce = 200;\n\n /** Accessible label for the clear button. */\n @property({ type: String, attribute: 'clear-label' })\n clearLabel = 'Clear search';\n\n /** When set, leaves the browser's native WebKit cancel button visible. */\n @property({ type: Boolean, attribute: 'allow-native-clear', reflect: true })\n allowNativeClear = false;\n\n /**\n * When set, the clear button is visible whenever the input is focused.\n *\n * Matching the Apple HIG \"cancel\" pattern.\n * Consider setting `clear-label=\"Cancel\"` in this mode.\n */\n @property({ type: Boolean, attribute: 'show-clear-on-focus' })\n showClearOnFocus = false;\n\n /** Disables the component and mirrors the state to the slotted input. */\n @property({ type: Boolean, reflect: true })\n get disabled(): boolean {\n // _disabledHost holds what the host last set; fall back to what the input\n // actually reports (kept in sync by the MutationObserver).\n return this._disabledHost ?? this._inputDisabled;\n }\n set disabled(value: boolean) {\n const old = this.disabled;\n this._disabledHost = value;\n\n const $input = this._$input();\n if ($input) {\n $input.disabled = value;\n }\n\n this.requestUpdate('disabled', old);\n }\n\n /**\n * Placeholder mirrored onto the native input.\n *\n * Consumer `placeholder` attribute is always honored.\n */\n @property({ type: String })\n placeholder?: string;\n\n private _value: string | undefined = undefined;\n\n private _defaultValue: string | undefined = undefined;\n\n private _valueSetByHost = false;\n\n /** True once an author value, host write, or user input owns the value. */\n private _valueInitialized = false;\n\n private _authorPlaceholder = false;\n\n private _$inputRef: WeakRef<HTMLInputElement> | null = null;\n\n private _debounceTimer: ReturnType<typeof setTimeout> | undefined = undefined;\n\n @state()\n private _hasInput = false;\n\n @state()\n private _hasValue = false;\n\n @state()\n private _hasLeading = false;\n\n @state()\n private _hasTrailing = false;\n\n @state()\n private _inputDisabled = false;\n\n // Tracks the last value written via the disabled setter so the getter and\n // Lit's reflect can return it before the input is slotted.\n private _disabledHost: boolean | undefined = undefined;\n\n @state()\n private _focused = false;\n\n private readonly _onInputFocus = (): void => {\n this._focused = true;\n };\n\n private readonly _onInputBlur = (): void => {\n this._focused = false;\n };\n\n private readonly _disabledObserver = new MutationObserver(() => {\n const $input = this._$input();\n\n if ($input) {\n this._inputDisabled = $input.disabled;\n }\n });\n\n private readonly _inputController = new NativeChildController<HTMLInputElement>(this, {\n selector: ':scope > input[type=\"search\"]',\n observe: true,\n onChange: ($input, $previousInput) => this._setupInput($input, $previousInput),\n onMissing: () => {\n if (import.meta.env.DEV) {\n warnMissingDirectChild(this, {\n selector: ':scope > input[type=\"search\"]',\n message:\n '[rc-search-bar] No direct child <input type=\"search\"> found. ' +\n 'Place a native search input inside <rc-search-bar>.',\n });\n }\n },\n });\n\n /**\n * The current search value. Reads from the native input when present.\n * Host writes are silent (no events) and win over slotted author values.\n */\n @property({ attribute: false })\n get value(): string {\n return this._$input()?.value ?? this._value ?? this._defaultValue ?? '';\n }\n set value(value: string) {\n const oldValue = this.value;\n\n this._value = value;\n this._valueSetByHost = true;\n this._valueInitialized = true;\n\n const $input = this._$input();\n\n if ($input) {\n $input.value = value;\n }\n\n this._hasValue = value.length > 0;\n\n this.requestUpdate('value', oldValue);\n }\n\n /**\n * Initial uncontrolled value hint. Applied once, and only when neither an\n * author `value`, a host `value` write, nor user input owns the value.\n */\n @property({ type: String, attribute: 'default-value' })\n get defaultValue(): string | undefined {\n return this._defaultValue;\n }\n\n set defaultValue(value: string | undefined) {\n const oldValue = this._defaultValue;\n\n this._defaultValue = value;\n\n const $input = this._$input();\n if ($input && value !== undefined && !this._valueInitialized) {\n $input.value = value;\n this._hasValue = value.length > 0;\n }\n\n this.requestUpdate('defaultValue', oldValue);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n _suppressUaClear();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n\n this._focused = false;\n this._cancelPendingInput();\n\n const $input = this._$input();\n\n $input?.removeEventListener('input', this._onNativeInput);\n $input?.removeEventListener('focus', this._onInputFocus);\n $input?.removeEventListener('blur', this._onInputBlur);\n\n this._disabledObserver.disconnect();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n if (changed.has('placeholder')) {\n const $input = this._$input();\n\n if ($input) {\n this._applyPlaceholder($input);\n }\n }\n }\n\n private _$input(): HTMLInputElement | null {\n return this._$inputRef?.deref() ?? null;\n }\n\n private _handleSlotChange(): void {\n this._inputController.sync();\n }\n\n private _setupInput(\n $input: HTMLInputElement | null,\n $previousInput: HTMLInputElement | null,\n ): void {\n if ($previousInput && $previousInput !== $input) {\n $previousInput.removeEventListener('input', this._onNativeInput);\n $previousInput.removeEventListener('focus', this._onInputFocus);\n $previousInput.removeEventListener('blur', this._onInputBlur);\n }\n\n this._disabledObserver.disconnect();\n this._focused = false;\n\n this._$inputRef = $input ? new WeakRef($input) : null;\n this._hasInput = $input !== null;\n this._authorPlaceholder = $input?.hasAttribute('placeholder') ?? false;\n\n if (!$input) {\n this._hasValue = false;\n\n return;\n }\n\n // Deferred reads keep framework render cycles (e.g. SolidJS) untouched.\n queueMicrotask(() => {\n if (!$input.isConnected) {\n return;\n }\n\n if (this._valueSetByHost) {\n $input.value = this._value ?? '';\n } else if ($input.value) {\n // An author-provided value wins over defaultValue.\n this._valueInitialized = true;\n } else if (this._defaultValue !== undefined && !this._valueInitialized) {\n $input.value = this._defaultValue;\n }\n\n this._applyPlaceholder($input);\n\n if (this._disabledHost !== undefined) {\n $input.disabled = this._disabledHost;\n }\n\n this._inputDisabled = $input.disabled;\n this._hasValue = $input.value.length > 0;\n\n if (\n import.meta.env.DEV &&\n !$input.labels?.length &&\n !$input.getAttribute('aria-label') &&\n !$input.getAttribute('aria-labelledby')\n ) {\n console.warn(\n '[rc-search-bar] Slotted <input type=\"search\"> has no accessible name.',\n 'Add aria-label, wrap in <label>, or use <label for=\"...\">.',\n $input,\n );\n }\n\n this._bindInput($input);\n });\n }\n\n private _bindInput($input: HTMLInputElement): void {\n $input.addEventListener('input', this._onNativeInput);\n $input.addEventListener('focus', this._onInputFocus);\n $input.addEventListener('blur', this._onInputBlur);\n\n this._disabledObserver.observe($input, { attributeFilter: ['disabled'] });\n }\n\n private _applyPlaceholder($input: HTMLInputElement): void {\n if (this.placeholder === undefined || this._authorPlaceholder) {\n return;\n }\n\n $input.placeholder = this.placeholder;\n }\n\n private readonly _onNativeInput = (e: Event): void => {\n const $input = e.currentTarget as HTMLInputElement;\n\n this._valueInitialized = true;\n this._hasValue = $input.value.length > 0;\n this._scheduleInputEvent($input.value);\n };\n\n private _scheduleInputEvent(value: string): void {\n this._cancelPendingInput();\n\n if (this.debounce <= 0) {\n this._dispatchInput(value);\n\n return;\n }\n\n this._debounceTimer = setTimeout(() => {\n this._debounceTimer = undefined;\n this._dispatchInput(value);\n }, this.debounce);\n }\n\n private _cancelPendingInput(): void {\n if (this._debounceTimer === undefined) {\n return;\n }\n\n clearTimeout(this._debounceTimer);\n this._debounceTimer = undefined;\n }\n\n private _dispatchInput(value: string): void {\n this.dispatchEvent(\n new CustomEvent<RCSearchBarInputDetail>('rc-search-bar-input', {\n bubbles: true,\n composed: true,\n detail: { value },\n }),\n );\n }\n\n private _handleClear(): void {\n const $input = this._$input();\n\n if (!$input) {\n return;\n }\n\n this._cancelPendingInput();\n\n $input.value = '';\n\n this._valueInitialized = true;\n this._hasValue = false;\n\n this.dispatchEvent(\n new CustomEvent('rc-search-bar-clear', {\n bubbles: true,\n composed: true,\n detail: {},\n }),\n );\n this._dispatchInput('');\n\n // Refocus before the re-render hides the button, so focus never drops\n // to <body>.\n $input.focus();\n }\n\n protected override render() {\n return html`\n <div id=\"root\" part=\"root\" class=${classMap({ empty: !this._hasInput })} ${keyInteraction({ attributeTarget: this })}>\n <span\n id=\"leading\"\n part=\"leading\"\n class=${classMap({ empty: !this._hasLeading || !this._hasInput })}\n >\n <slot name=\"leading\" @slotchange=${this._onLeadingSlotChange}></slot>\n </span>\n\n <slot @slotchange=${this._handleSlotChange}></slot>\n\n <button\n id=\"clear\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this.clearLabel}\n ?hidden=${!this._hasInput}\n ?inert=${this._hasInput && !this._hasValue && !(this.showClearOnFocus && this._focused)}\n ?disabled=${this._inputDisabled}\n @click=${this._handleClear}\n >\n <slot name=\"clear-icon\"><span aria-hidden=\"true\">&#10005;</span></slot>\n </button>\n\n <span id=\"trailing\" part=\"trailing\" class=${classMap({ empty: !this._hasTrailing })}>\n <slot name=\"trailing\" @slotchange=${this._onTrailingSlotChange}></slot>\n </span>\n </div>\n `;\n }\n\n /** Programmatically clears the value and fires rc-search-bar-clear + rc-search-bar-input. */\n public clear(): void {\n this._handleClear();\n }\n\n private _onLeadingSlotChange(e: Event): void {\n const $slot = e.target as HTMLSlotElement;\n\n this._hasLeading = $slot.assignedElements().length > 0;\n }\n\n private _onTrailingSlotChange(e: Event): void {\n const $slot = e.target as HTMLSlotElement;\n\n this._hasTrailing = $slot.assignedElements().length > 0;\n }\n}\n\nexport default RCSearchBar;\n"],"names":["searchBarStyles","css","_uaClearSheet","_uaClearSuppressed","_suppressUaClear","_RCSearchBar","LitElement","$input","NativeChildController","$previousInput","value","old","oldValue","changed","html","classMap","keyInteraction","$slot","RCSearchBar","__decorateClass","property","state"],"mappings":";;;;AAEO,MAAMA,IAAkBC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;ACM/B,MAAMC,IAAgB,IAAI,cAAA;AAC1BA,EAAc;AAAA,EACZ;AAAA;AAEF;AACA,IAAIC,IAAqB;AAEzB,SAASC,IAAyB;AAChC,EAAID,MAIJ,SAAS,qBAAqB,CAAC,GAAG,SAAS,oBAAoBD,CAAa,GAE5EC,IAAqB;AACvB;AAyDO,MAAME,IAAN,MAAMA,UAAoBC,EAAW;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA,GAKL,KAAA,WAAW,KAIX,KAAA,aAAa,gBAIb,KAAA,mBAAmB,IASnB,KAAA,mBAAmB,IA6BnB,KAAQ,SAA6B,QAErC,KAAQ,gBAAoC,QAE5C,KAAQ,kBAAkB,IAG1B,KAAQ,oBAAoB,IAE5B,KAAQ,qBAAqB,IAE7B,KAAQ,aAA+C,MAEvD,KAAQ,iBAA4D,QAGpE,KAAQ,YAAY,IAGpB,KAAQ,YAAY,IAGpB,KAAQ,cAAc,IAGtB,KAAQ,eAAe,IAGvB,KAAQ,iBAAiB,IAIzB,KAAQ,gBAAqC,QAG7C,KAAQ,WAAW,IAEnB,KAAiB,gBAAgB,MAAY;AAC3C,WAAK,WAAW;AAAA,IAClB,GAEA,KAAiB,eAAe,MAAY;AAC1C,WAAK,WAAW;AAAA,IAClB,GAEA,KAAiB,oBAAoB,IAAI,iBAAiB,MAAM;AAC9D,YAAMC,IAAS,KAAK,QAAA;AAEpB,MAAIA,MACF,KAAK,iBAAiBA,EAAO;AAAA,IAEjC,CAAC,GAED,KAAiB,mBAAmB,IAAIC,EAAwC,MAAM;AAAA,MACpF,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU,CAACD,GAAQE,MAAmB,KAAK,YAAYF,GAAQE,CAAc;AAAA,MAC7E,WAAW,MAAM;AAAA,MASjB;AAAA,IAAA,CACD,GA0KD,KAAiB,iBAAiB,CAAC,MAAmB;AACpD,YAAMF,IAAS,EAAE;AAEjB,WAAK,oBAAoB,IACzB,KAAK,YAAYA,EAAO,MAAM,SAAS,GACvC,KAAK,oBAAoBA,EAAO,KAAK;AAAA,IACvC;AAAA,EAAA;AAAA,EA5QA,IAAI,WAAoB;AAGtB,WAAO,KAAK,iBAAiB,KAAK;AAAA,EACpC;AAAA,EACA,IAAI,SAASG,GAAgB;AAC3B,UAAMC,IAAM,KAAK;AACjB,SAAK,gBAAgBD;AAErB,UAAMH,IAAS,KAAK,QAAA;AACpB,IAAIA,MACFA,EAAO,WAAWG,IAGpB,KAAK,cAAc,YAAYC,CAAG;AAAA,EACpC;AAAA,EAoFA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAA,GAAW,SAAS,KAAK,UAAU,KAAK,iBAAiB;AAAA,EACvE;AAAA,EACA,IAAI,MAAMD,GAAe;AACvB,UAAME,IAAW,KAAK;AAEtB,SAAK,SAASF,GACd,KAAK,kBAAkB,IACvB,KAAK,oBAAoB;AAEzB,UAAMH,IAAS,KAAK,QAAA;AAEpB,IAAIA,MACFA,EAAO,QAAQG,IAGjB,KAAK,YAAYA,EAAM,SAAS,GAEhC,KAAK,cAAc,SAASE,CAAQ;AAAA,EACtC;AAAA,EAOA,IAAI,eAAmC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,aAAaF,GAA2B;AAC1C,UAAME,IAAW,KAAK;AAEtB,SAAK,gBAAgBF;AAErB,UAAMH,IAAS,KAAK,QAAA;AACpB,IAAIA,KAAUG,MAAU,UAAa,CAAC,KAAK,sBACzCH,EAAO,QAAQG,GACf,KAAK,YAAYA,EAAM,SAAS,IAGlC,KAAK,cAAc,gBAAgBE,CAAQ;AAAA,EAC7C;AAAA,EAES,oBAA0B;AACjC,UAAM,kBAAA,GAENR,EAAA;AAAA,EACF;AAAA,EAES,uBAA6B;AACpC,UAAM,qBAAA,GAEN,KAAK,WAAW,IAChB,KAAK,oBAAA;AAEL,UAAMG,IAAS,KAAK,QAAA;AAEpB,IAAAA,GAAQ,oBAAoB,SAAS,KAAK,cAAc,GACxDA,GAAQ,oBAAoB,SAAS,KAAK,aAAa,GACvDA,GAAQ,oBAAoB,QAAQ,KAAK,YAAY,GAErD,KAAK,kBAAkB,WAAA;AAAA,EACzB;AAAA,EAEmB,QAAQM,GAAqC;AAC9D,QAAIA,EAAQ,IAAI,aAAa,GAAG;AAC9B,YAAMN,IAAS,KAAK,QAAA;AAEpB,MAAIA,KACF,KAAK,kBAAkBA,CAAM;AAAA,IAEjC;AAAA,EACF;AAAA,EAEQ,UAAmC;AACzC,WAAO,KAAK,YAAY,MAAA,KAAW;AAAA,EACrC;AAAA,EAEQ,oBAA0B;AAChC,SAAK,iBAAiB,KAAA;AAAA,EACxB;AAAA,EAEQ,YACNA,GACAE,GACM;AAcN,QAbIA,KAAkBA,MAAmBF,MACvCE,EAAe,oBAAoB,SAAS,KAAK,cAAc,GAC/DA,EAAe,oBAAoB,SAAS,KAAK,aAAa,GAC9DA,EAAe,oBAAoB,QAAQ,KAAK,YAAY,IAG9D,KAAK,kBAAkB,WAAA,GACvB,KAAK,WAAW,IAEhB,KAAK,aAAaF,IAAS,IAAI,QAAQA,CAAM,IAAI,MACjD,KAAK,YAAYA,MAAW,MAC5B,KAAK,qBAAqBA,GAAQ,aAAa,aAAa,KAAK,IAE7D,CAACA,GAAQ;AACX,WAAK,YAAY;AAEjB;AAAA,IACF;AAGA,mBAAe,MAAM;AACnB,MAAKA,EAAO,gBAIR,KAAK,kBACPA,EAAO,QAAQ,KAAK,UAAU,KACrBA,EAAO,QAEhB,KAAK,oBAAoB,KAChB,KAAK,kBAAkB,UAAa,CAAC,KAAK,sBACnDA,EAAO,QAAQ,KAAK,gBAGtB,KAAK,kBAAkBA,CAAM,GAEzB,KAAK,kBAAkB,WACzBA,EAAO,WAAW,KAAK,gBAGzB,KAAK,iBAAiBA,EAAO,UAC7B,KAAK,YAAYA,EAAO,MAAM,SAAS,GAevC,KAAK,WAAWA,CAAM;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEQ,WAAWA,GAAgC;AACjD,IAAAA,EAAO,iBAAiB,SAAS,KAAK,cAAc,GACpDA,EAAO,iBAAiB,SAAS,KAAK,aAAa,GACnDA,EAAO,iBAAiB,QAAQ,KAAK,YAAY,GAEjD,KAAK,kBAAkB,QAAQA,GAAQ,EAAE,iBAAiB,CAAC,UAAU,GAAG;AAAA,EAC1E;AAAA,EAEQ,kBAAkBA,GAAgC;AACxD,IAAI,KAAK,gBAAgB,UAAa,KAAK,uBAI3CA,EAAO,cAAc,KAAK;AAAA,EAC5B;AAAA,EAUQ,oBAAoBG,GAAqB;AAG/C,QAFA,KAAK,oBAAA,GAED,KAAK,YAAY,GAAG;AACtB,WAAK,eAAeA,CAAK;AAEzB;AAAA,IACF;AAEA,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,iBAAiB,QACtB,KAAK,eAAeA,CAAK;AAAA,IAC3B,GAAG,KAAK,QAAQ;AAAA,EAClB;AAAA,EAEQ,sBAA4B;AAClC,IAAI,KAAK,mBAAmB,WAI5B,aAAa,KAAK,cAAc,GAChC,KAAK,iBAAiB;AAAA,EACxB;AAAA,EAEQ,eAAeA,GAAqB;AAC1C,SAAK;AAAA,MACH,IAAI,YAAoC,uBAAuB;AAAA,QAC7D,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,EAAE,OAAAA,EAAA;AAAA,MAAM,CACjB;AAAA,IAAA;AAAA,EAEL;AAAA,EAEQ,eAAqB;AAC3B,UAAMH,IAAS,KAAK,QAAA;AAEpB,IAAKA,MAIL,KAAK,oBAAA,GAELA,EAAO,QAAQ,IAEf,KAAK,oBAAoB,IACzB,KAAK,YAAY,IAEjB,KAAK;AAAA,MACH,IAAI,YAAY,uBAAuB;AAAA,QACrC,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ,CAAA;AAAA,MAAC,CACV;AAAA,IAAA,GAEH,KAAK,eAAe,EAAE,GAItBA,EAAO,MAAA;AAAA,EACT;AAAA,EAEmB,SAAS;AAC1B,WAAOO;AAAA,yCAC8BC,EAAS,EAAE,OAAO,CAAC,KAAK,UAAA,CAAW,CAAC,IAAIC,EAAe,EAAE,iBAAiB,KAAA,CAAM,CAAC;AAAA;AAAA;AAAA;AAAA,kBAIxGD,EAAS,EAAE,OAAO,CAAC,KAAK,eAAe,CAAC,KAAK,WAAW,CAAC;AAAA;AAAA,6CAE9B,KAAK,oBAAoB;AAAA;AAAA;AAAA,4BAG1C,KAAK,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAM3B,KAAK,UAAU;AAAA,oBAClB,CAAC,KAAK,SAAS;AAAA,mBAChB,KAAK,aAAa,CAAC,KAAK,aAAa,EAAE,KAAK,oBAAoB,KAAK,SAAS;AAAA,sBAC3E,KAAK,cAAc;AAAA,mBACtB,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,oDAKgBA,EAAS,EAAE,OAAO,CAAC,KAAK,aAAA,CAAc,CAAC;AAAA,8CAC7C,KAAK,qBAAqB;AAAA;AAAA;AAAA;AAAA,EAItE;AAAA;AAAA,EAGO,QAAc;AACnB,SAAK,aAAA;AAAA,EACP;AAAA,EAEQ,qBAAqB,GAAgB;AAC3C,UAAME,IAAQ,EAAE;AAEhB,SAAK,cAAcA,EAAM,iBAAA,EAAmB,SAAS;AAAA,EACvD;AAAA,EAEQ,sBAAsB,GAAgB;AAC5C,UAAMA,IAAQ,EAAE;AAEhB,SAAK,eAAeA,EAAM,iBAAA,EAAmB,SAAS;AAAA,EACxD;AACF;AAtZEZ,EAAO,SAAS,CAACL,CAAe;AAD3B,IAAMkB,IAANb;AAKLc,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAJfF,EAKX,WAAA,YAAA,CAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAQ,WAAW,eAAe;AAAA,GARzCF,EASX,WAAA,cAAA,CAAA;AAIAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,sBAAsB,SAAS,IAAM;AAAA,GAZhEF,EAaX,WAAA,oBAAA,CAAA;AASAC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,SAAS,WAAW,uBAAuB;AAAA,GArBlDF,EAsBX,WAAA,oBAAA,CAAA;AAIIC,EAAA;AAAA,EADHC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GAzB/BF,EA0BP,WAAA,YAAA,CAAA;AAuBJC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAhDfF,EAiDX,WAAA,eAAA,CAAA;AAkBQC,EAAA;AAAA,EADPE,EAAA;AAAM,GAlEIH,EAmEH,WAAA,aAAA,CAAA;AAGAC,EAAA;AAAA,EADPE,EAAA;AAAM,GArEIH,EAsEH,WAAA,aAAA,CAAA;AAGAC,EAAA;AAAA,EADPE,EAAA;AAAM,GAxEIH,EAyEH,WAAA,eAAA,CAAA;AAGAC,EAAA;AAAA,EADPE,EAAA;AAAM,GA3EIH,EA4EH,WAAA,gBAAA,CAAA;AAGAC,EAAA;AAAA,EADPE,EAAA;AAAM,GA9EIH,EA+EH,WAAA,kBAAA,CAAA;AAOAC,EAAA;AAAA,EADPE,EAAA;AAAM,GArFIH,EAsFH,WAAA,YAAA,CAAA;AAuCJC,EAAA;AAAA,EADHC,EAAS,EAAE,WAAW,GAAA,CAAO;AAAA,GA5HnBF,EA6HP,WAAA,SAAA,CAAA;AA0BAC,EAAA;AAAA,EADHC,EAAS,EAAE,MAAM,QAAQ,WAAW,iBAAiB;AAAA,GAtJ3CF,EAuJP,WAAA,gBAAA,CAAA;"}
@@ -1,4 +1,4 @@
1
- import { R as e } from "./rc-search-bar-Ssb7YNqn.js";
1
+ import { R as e } from "./rc-search-bar-DVczVFvA.js";
2
2
  customElements.get("rc-search-bar") || customElements.define("rc-search-bar", e);
3
3
  export {
4
4
  e as RCSearchBar
@@ -1,4 +1,4 @@
1
- import { R as e } from "./rc-search-bar-Ssb7YNqn.js";
1
+ import { R as e } from "./rc-search-bar-DVczVFvA.js";
2
2
  export {
3
3
  e as RCSearchBar
4
4
  };
@@ -9,8 +9,8 @@ export interface RCSearchBarInputDetail {
9
9
  value: string;
10
10
  }
11
11
  /**
12
- * Enhances a consumer-provided native `<input type="search">` with leading
13
- * icon chrome, an accessible clear button, and debounced search events.
12
+ * Search field wrapper for a native <input type="search"> with icon chrome, clear button,
13
+ * and debounced events.
14
14
  *
15
15
  * The native input is required and stays in light DOM as the source of
16
16
  * truth, so form submission, `label[for]`/wrapping-label association, and
@@ -23,21 +23,27 @@ export interface RCSearchBarInputDetail {
23
23
  * (a debounced `rc-search-bar-input` with an empty value) and never fires
24
24
  * `rc-search-bar-clear`.
25
25
  *
26
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-search-bar rc-search-bar docs}
27
+ *
26
28
  * @slot - The required native `<input type="search">`
27
29
  * @slot leading - Decorative leading icon; mark it `aria-hidden="true"`
28
30
  * @slot trailing - Optional trailing content after the clear button
29
31
  * @slot clear-icon - Optional glyph replacing the default clear glyph
32
+ *
30
33
  * @fires rc-search-bar-input - Debounced after typing, immediate on clear;
31
34
  * `detail: { value }`
32
35
  * @fires rc-search-bar-clear - When the clear button is activated
36
+ *
37
+ * @cssprop [--rc-search-bar-border=1px solid ButtonBorder] - Wrapper border; set to `none` in M3 theme (uses elevation instead)
38
+ * @cssprop [--rc-search-bar-shadow=none] - Wrapper box-shadow for elevation; M3 theme sets Level 1 at rest
33
39
  * @cssprop [--rc-search-bar-bg=Field] - Wrapper background
34
40
  * @cssprop [--rc-search-bar-color=FieldText] - Wrapper text color
35
41
  * @cssprop [--rc-search-bar-icon-color=GrayText] - Leading icon color
36
42
  * @cssprop [--rc-search-bar-clear-color=GrayText] - Clear button glyph color
37
- * @cssprop [--rc-search-bar-radius=var(--rc-control-radius)] - Wrapper border radius
38
- * @cssprop [--rc-search-bar-height=48px] - Wrapper block size
39
- * @cssprop [--rc-search-bar-padding-inline=0.5em] - Wrapper horizontal padding
40
- * @cssprop [--rc-search-bar-gap=0.25em] - Gap between icon, input, and clear button
43
+ * @cssprop [--rc-search-bar-radius=var(--rc-control-radius,0.125em)] - Wrapper border radius
44
+ * @cssprop [--rc-search-bar-height=var(--rc-control-block-size,2.5rem)] - Wrapper block size
45
+ * @cssprop [--rc-search-bar-padding-inline=var(--rc-control-padding-inline,0.75rem)] - Wrapper horizontal padding
46
+ * @cssprop [--rc-search-bar-gap=var(--rc-control-gap,0.25em)] - Gap between icon, input, and clear button
41
47
  * @cssprop [--rc-search-bar-input-font-size] - Input font size (inherits when unset)
42
48
  * @cssprop [--rc-search-bar-input-font-family] - Input font family (inherits when unset)
43
49
  * @cssprop [--rc-search-bar-input-color] - Input text color (inherits when unset)
@@ -55,8 +61,9 @@ export declare class RCSearchBar extends LitElement {
55
61
  /** When set, leaves the browser's native WebKit cancel button visible. */
56
62
  allowNativeClear: boolean;
57
63
  /**
58
- * When set, the clear button is visible whenever the input is focused,
59
- * even with no value — matching the Apple HIG "cancel" pattern.
64
+ * When set, the clear button is visible whenever the input is focused.
65
+ *
66
+ * Matching the Apple HIG "cancel" pattern.
60
67
  * Consider setting `clear-label="Cancel"` in this mode.
61
68
  */
62
69
  showClearOnFocus: boolean;
@@ -64,8 +71,9 @@ export declare class RCSearchBar extends LitElement {
64
71
  get disabled(): boolean;
65
72
  set disabled(value: boolean);
66
73
  /**
67
- * Placeholder mirrored onto the native input. An author `placeholder`
68
- * attribute already present on the input is never overwritten.
74
+ * Placeholder mirrored onto the native input.
75
+ *
76
+ * Consumer `placeholder` attribute is always honored.
69
77
  */
70
78
  placeholder?: string;
71
79
  private _value;
@@ -74,7 +82,7 @@ export declare class RCSearchBar extends LitElement {
74
82
  /** True once an author value, host write, or user input owns the value. */
75
83
  private _valueInitialized;
76
84
  private _authorPlaceholder;
77
- private _inputRef;
85
+ private _$inputRef;
78
86
  private _debounceTimer;
79
87
  private _hasInput;
80
88
  private _hasValue;
@@ -83,13 +91,10 @@ export declare class RCSearchBar extends LitElement {
83
91
  private _inputDisabled;
84
92
  private _disabledHost;
85
93
  private _focused;
86
- private _lastInteractionWasPointer;
87
- private readonly _onPointerDown;
88
- private readonly _onFocusIn;
89
- private readonly _onFocusOut;
90
94
  private readonly _onInputFocus;
91
95
  private readonly _onInputBlur;
92
96
  private readonly _disabledObserver;
97
+ private readonly _inputController;
93
98
  /**
94
99
  * The current search value. Reads from the native input when present.
95
100
  * Host writes are silent (no events) and win over slotted author values.
@@ -105,8 +110,9 @@ export declare class RCSearchBar extends LitElement {
105
110
  connectedCallback(): void;
106
111
  disconnectedCallback(): void;
107
112
  protected updated(changed: PropertyValues<this>): void;
108
- private _input;
113
+ private _$input;
109
114
  private _handleSlotChange;
115
+ private _setupInput;
110
116
  private _bindInput;
111
117
  private _applyPlaceholder;
112
118
  private readonly _onNativeInput;
package/package.json CHANGED
@@ -3,14 +3,14 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.0",
7
- "description": "Headless search bar enhancing a native input[type=search] with clear button and debounced events, built with Lit",
6
+ "version": "0.3.1",
7
+ "description": "Search field wrapper for a native <input type=\"search\"> with icon chrome, clear button, and debounced events.",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
11
  "directory": "packages/rc-search-bar"
12
12
  },
13
- "homepage": "https://github.com/richardcarls/rc-webcomponents#readme",
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-search-bar",
14
14
  "license": "MIT",
15
15
  "type": "module",
16
16
  "files": [
@@ -37,18 +37,15 @@
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
  "devDependencies": {
50
48
  "@custom-elements-manifest/analyzer": "0.11.0",
51
- "@guanghechen/rollup-plugin-copy": "^6.0.9",
52
49
  "@vitest/browser-playwright": "4.1.5",
53
50
  "lit": "^3.0.0",
54
51
  "playwright": "^1.56.0",
@@ -61,5 +58,8 @@
61
58
  },
62
59
  "peerDependencies": {
63
60
  "lit": "^3.0.0"
61
+ },
62
+ "dependencies": {
63
+ "@rcarls/rc-common": "workspace:*"
64
64
  }
65
65
  }
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-search-bar-Ssb7YNqn.js","sources":["../src/rc-search-bar.styles.ts","../src/rc-search-bar.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const searchBarStyles = css`\n :host {\n display: inline-flex;\n color-scheme: inherit;\n }\n\n [hidden] {\n display: none !important;\n }\n\n #root {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n min-inline-size: 0;\n gap: var(--rc-search-bar-gap, 0.25em);\n block-size: var(--rc-search-bar-height, 48px);\n padding-inline: var(--rc-search-bar-padding-inline, 0.5em);\n background: var(--rc-search-bar-bg, Field);\n color: var(--rc-search-bar-color, FieldText);\n border-radius: var(--rc-search-bar-radius, var(--rc-control-radius, 0));\n }\n\n :host([data-focus-visible]) #root {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n\n /* No slotted search input: render no chrome at all. */\n #root.empty {\n block-size: auto;\n padding: 0;\n background: none;\n border-radius: 0;\n }\n\n #leading {\n display: flex;\n flex: none;\n align-items: center;\n color: var(--rc-search-bar-icon-color, GrayText);\n }\n\n #leading.empty {\n display: none;\n }\n\n ::slotted(input[type='search']) {\n flex: 1 1 auto;\n min-inline-size: 0;\n border: none;\n background: transparent;\n color: var(--rc-search-bar-input-color, inherit);\n font-family: var(--rc-search-bar-input-font-family, inherit);\n font-size: var(--rc-search-bar-input-font-size, inherit);\n outline: none;\n }\n\n #trailing {\n display: flex;\n flex: none;\n align-items: center;\n }\n\n #trailing.empty {\n display: none;\n }\n\n #clear {\n display: inline-grid;\n flex: none;\n place-items: center;\n min-inline-size: 24px;\n min-block-size: 24px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: none;\n color: var(--rc-search-bar-clear-color, GrayText);\n font: inherit;\n cursor: pointer;\n }\n\n #clear:focus-visible {\n outline: var(--rc-focus-ring, auto);\n outline-offset: var(--rc-focus-ring-offset, 0);\n }\n`;\n\nexport default searchBarStyles;\n","import { LitElement, html, type PropertyValues } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { classMap } from 'lit/directives/class-map.js';\n\nimport searchBarStyles from './rc-search-bar.styles';\n\nconst _uaClearSheet = new CSSStyleSheet();\n_uaClearSheet.replaceSync(\n `rc-search-bar:not([allow-native-clear]) input[type=\"search\"]::-webkit-search-cancel-button\n { -webkit-appearance: none; display: none; }`,\n);\nlet _uaClearSuppressed = false;\n\nfunction _suppressUaClear(): void {\n if (_uaClearSuppressed) return;\n document.adoptedStyleSheets = [...document.adoptedStyleSheets, _uaClearSheet];\n _uaClearSuppressed = true;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-search-bar': RCSearchBar;\n }\n}\n\nexport interface RCSearchBarInputDetail {\n /** The native input's value at dispatch time. */\n value: string;\n}\n\n/**\n * Enhances a consumer-provided native `<input type=\"search\">` with leading\n * icon chrome, an accessible clear button, and debounced search events.\n *\n * The native input is required and stays in light DOM as the source of\n * truth, so form submission, `label[for]`/wrapping-label association, and\n * pre-upgrade behavior are preserved. Without a slotted search input the\n * component renders no chrome and stays inert.\n *\n * The native WebKit cancel button is suppressed by default via an\n * adopted document stylesheet. Set `allow-native-clear` to restore it.\n * When restored, the native button clears through the normal input path\n * (a debounced `rc-search-bar-input` with an empty value) and never fires\n * `rc-search-bar-clear`.\n *\n * @slot - The required native `<input type=\"search\">`\n * @slot leading - Decorative leading icon; mark it `aria-hidden=\"true\"`\n * @slot trailing - Optional trailing content after the clear button\n * @slot clear-icon - Optional glyph replacing the default clear glyph\n * @fires rc-search-bar-input - Debounced after typing, immediate on clear;\n * `detail: { value }`\n * @fires rc-search-bar-clear - When the clear button is activated\n * @cssprop [--rc-search-bar-bg=Field] - Wrapper background\n * @cssprop [--rc-search-bar-color=FieldText] - Wrapper text color\n * @cssprop [--rc-search-bar-icon-color=GrayText] - Leading icon color\n * @cssprop [--rc-search-bar-clear-color=GrayText] - Clear button glyph color\n * @cssprop [--rc-search-bar-radius=var(--rc-control-radius)] - Wrapper border radius\n * @cssprop [--rc-search-bar-height=48px] - Wrapper block size\n * @cssprop [--rc-search-bar-padding-inline=0.5em] - Wrapper horizontal padding\n * @cssprop [--rc-search-bar-gap=0.25em] - Gap between icon, input, and clear button\n * @cssprop [--rc-search-bar-input-font-size] - Input font size (inherits when unset)\n * @cssprop [--rc-search-bar-input-font-family] - Input font family (inherits when unset)\n * @cssprop [--rc-search-bar-input-color] - Input text color (inherits when unset)\n * @csspart root - The wrapper element\n * @csspart leading - Wrapper around the leading icon slot\n * @csspart trailing - Wrapper around the trailing slot\n * @csspart clear - The clear button\n */\nexport class RCSearchBar extends LitElement {\n static styles = [searchBarStyles];\n\n /** Debounce window in ms for `rc-search-bar-input`; `0` dispatches synchronously. */\n @property({ type: Number })\n debounce = 200;\n\n /** Accessible label for the clear button. */\n @property({ type: String, attribute: 'clear-label' })\n clearLabel = 'Clear search';\n\n /** When set, leaves the browser's native WebKit cancel button visible. */\n @property({ type: Boolean, attribute: 'allow-native-clear', reflect: true })\n allowNativeClear = false;\n\n /**\n * When set, the clear button is visible whenever the input is focused,\n * even with no value — matching the Apple HIG \"cancel\" pattern.\n * Consider setting `clear-label=\"Cancel\"` in this mode.\n */\n @property({ type: Boolean, attribute: 'show-clear-on-focus' })\n showClearOnFocus = false;\n\n /** Disables the component and mirrors the state to the slotted input. */\n @property({ type: Boolean, reflect: true })\n get disabled(): boolean {\n // _disabledHost holds what the host last set; fall back to what the input\n // actually reports (kept in sync by the MutationObserver).\n return this._disabledHost ?? this._inputDisabled;\n }\n set disabled(value: boolean) {\n const old = this.disabled;\n this._disabledHost = value;\n const input = this._input();\n if (input) input.disabled = value;\n this.requestUpdate('disabled', old);\n }\n\n /**\n * Placeholder mirrored onto the native input. An author `placeholder`\n * attribute already present on the input is never overwritten.\n */\n @property({ type: String })\n placeholder?: string;\n\n private _value: string | undefined = undefined;\n\n private _defaultValue: string | undefined = undefined;\n\n private _valueSetByHost = false;\n\n /** True once an author value, host write, or user input owns the value. */\n private _valueInitialized = false;\n\n private _authorPlaceholder = false;\n\n private _inputRef: WeakRef<HTMLInputElement> | null = null;\n\n private _debounceTimer: ReturnType<typeof setTimeout> | undefined = undefined;\n\n @state()\n private _hasInput = false;\n\n @state()\n private _hasValue = false;\n\n @state()\n private _hasLeading = false;\n\n @state()\n private _hasTrailing = false;\n\n @state()\n private _inputDisabled = false;\n\n // Tracks the last value written via the disabled setter so the getter and\n // Lit's reflect can return it before the input is slotted.\n private _disabledHost: boolean | undefined = undefined;\n\n @state()\n private _focused = false;\n\n private _lastInteractionWasPointer = false;\n private readonly _onPointerDown = (): void => {\n this._lastInteractionWasPointer = true;\n };\n private readonly _onFocusIn = (): void => {\n if (!this._lastInteractionWasPointer) this.toggleAttribute('data-focus-visible', true);\n this._lastInteractionWasPointer = false;\n };\n private readonly _onFocusOut = (): void => {\n this.toggleAttribute('data-focus-visible', false);\n };\n\n private readonly _onInputFocus = (): void => {\n this._focused = true;\n };\n\n private readonly _onInputBlur = (): void => {\n this._focused = false;\n };\n\n private readonly _disabledObserver = new MutationObserver(() => {\n const input = this._input();\n if (input) this._inputDisabled = input.disabled;\n });\n\n /**\n * The current search value. Reads from the native input when present.\n * Host writes are silent (no events) and win over slotted author values.\n */\n @property({ attribute: false })\n get value(): string {\n return this._input()?.value ?? this._value ?? this._defaultValue ?? '';\n }\n set value(value: string) {\n const oldValue = this.value;\n\n this._value = value;\n this._valueSetByHost = true;\n this._valueInitialized = true;\n\n const input = this._input();\n if (input) input.value = value;\n this._hasValue = value.length > 0;\n\n this.requestUpdate('value', oldValue);\n }\n\n /**\n * Initial uncontrolled value hint. Applied once, and only when neither an\n * author `value`, a host `value` write, nor user input owns the value.\n */\n @property({ type: String, attribute: 'default-value' })\n get defaultValue(): string | undefined {\n return this._defaultValue;\n }\n set defaultValue(value: string | undefined) {\n const oldValue = this._defaultValue;\n\n this._defaultValue = value;\n\n const input = this._input();\n if (input && value !== undefined && !this._valueInitialized) {\n input.value = value;\n this._hasValue = value.length > 0;\n }\n\n this.requestUpdate('defaultValue', oldValue);\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n _suppressUaClear();\n\n this.addEventListener('pointerdown', this._onPointerDown, { capture: true });\n this.addEventListener('focusin', this._onFocusIn);\n this.addEventListener('focusout', this._onFocusOut);\n\n // Slot assignment survives a DOM move without re-firing slotchange, so\n // listeners are re-bound here (addEventListener is idempotent per ref).\n const input = this._input();\n if (input) this._bindInput(input);\n }\n\n override disconnectedCallback(): void {\n this.removeEventListener('pointerdown', this._onPointerDown, { capture: true });\n this.removeEventListener('focusin', this._onFocusIn);\n this.removeEventListener('focusout', this._onFocusOut);\n\n super.disconnectedCallback();\n\n this._focused = false;\n this._cancelPendingInput();\n\n const input = this._input();\n input?.removeEventListener('input', this._onNativeInput);\n input?.removeEventListener('focus', this._onInputFocus);\n input?.removeEventListener('blur', this._onInputBlur);\n\n this._disabledObserver.disconnect();\n }\n\n protected override updated(changed: PropertyValues<this>): void {\n if (changed.has('placeholder')) {\n const input = this._input();\n if (input) this._applyPlaceholder(input);\n }\n }\n\n private _input(): HTMLInputElement | null {\n return this._inputRef?.deref() ?? null;\n }\n\n private _handleSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n const input =\n slot\n .assignedElements({ flatten: true })\n .find(\n (el): el is HTMLInputElement =>\n el instanceof HTMLInputElement && el.type === 'search',\n ) ?? null;\n\n const previous = this._input();\n if (previous && previous !== input) {\n previous.removeEventListener('input', this._onNativeInput);\n previous.removeEventListener('focus', this._onInputFocus);\n previous.removeEventListener('blur', this._onInputBlur);\n }\n this._disabledObserver.disconnect();\n this._focused = false;\n\n this._inputRef = input ? new WeakRef(input) : null;\n this._hasInput = input !== null;\n\n if (!input) {\n this._hasValue = false;\n\n return;\n }\n\n // Deferred reads keep framework render cycles (e.g. SolidJS) untouched.\n queueMicrotask(() => {\n if (!input.isConnected) return;\n\n if (this._valueSetByHost) {\n input.value = this._value ?? '';\n } else if (input.value) {\n // An author-provided value wins over defaultValue.\n this._valueInitialized = true;\n } else if (this._defaultValue !== undefined && !this._valueInitialized) {\n input.value = this._defaultValue;\n }\n\n this._authorPlaceholder = input.hasAttribute('placeholder');\n this._applyPlaceholder(input);\n\n if (this._disabledHost !== undefined) {\n input.disabled = this._disabledHost;\n }\n this._inputDisabled = input.disabled;\n this._hasValue = input.value.length > 0;\n\n if (\n import.meta.env.DEV &&\n !input.labels?.length &&\n !input.getAttribute('aria-label') &&\n !input.getAttribute('aria-labelledby')\n ) {\n console.warn(\n '[rc-search-bar] Slotted <input type=\"search\"> has no accessible name.',\n 'Add aria-label, wrap in <label>, or use <label for=\"...\">.',\n input,\n );\n }\n\n this._bindInput(input);\n });\n }\n\n private _bindInput(input: HTMLInputElement): void {\n input.addEventListener('input', this._onNativeInput);\n input.addEventListener('focus', this._onInputFocus);\n input.addEventListener('blur', this._onInputBlur);\n this._disabledObserver.observe(input, { attributeFilter: ['disabled'] });\n }\n\n private _applyPlaceholder(input: HTMLInputElement): void {\n if (this.placeholder === undefined || this._authorPlaceholder) return;\n\n input.placeholder = this.placeholder;\n }\n\n private readonly _onNativeInput = (e: Event): void => {\n const input = e.currentTarget as HTMLInputElement;\n\n this._valueInitialized = true;\n this._hasValue = input.value.length > 0;\n this._scheduleInputEvent(input.value);\n };\n\n private _scheduleInputEvent(value: string): void {\n this._cancelPendingInput();\n\n if (this.debounce <= 0) {\n this._dispatchInput(value);\n\n return;\n }\n\n this._debounceTimer = setTimeout(() => {\n this._debounceTimer = undefined;\n this._dispatchInput(value);\n }, this.debounce);\n }\n\n private _cancelPendingInput(): void {\n if (this._debounceTimer === undefined) return;\n\n clearTimeout(this._debounceTimer);\n this._debounceTimer = undefined;\n }\n\n private _dispatchInput(value: string): void {\n this.dispatchEvent(\n new CustomEvent<RCSearchBarInputDetail>('rc-search-bar-input', {\n bubbles: true,\n composed: true,\n detail: { value },\n }),\n );\n }\n\n private _handleClear(): void {\n const input = this._input();\n if (!input) return;\n\n this._cancelPendingInput();\n\n input.value = '';\n this._valueInitialized = true;\n this._hasValue = false;\n\n this.dispatchEvent(\n new CustomEvent('rc-search-bar-clear', {\n bubbles: true,\n composed: true,\n detail: {},\n }),\n );\n this._dispatchInput('');\n\n // Refocus before the re-render hides the button, so focus never drops\n // to <body>.\n input.focus();\n }\n\n protected override render() {\n return html`\n <div id=\"root\" part=\"root\" class=${classMap({ empty: !this._hasInput })}>\n <span\n id=\"leading\"\n part=\"leading\"\n class=${classMap({ empty: !this._hasLeading || !this._hasInput })}\n >\n <slot name=\"leading\" @slotchange=${this._onLeadingSlotChange}></slot>\n </span>\n <slot @slotchange=${this._handleSlotChange}></slot>\n <button\n id=\"clear\"\n part=\"clear\"\n type=\"button\"\n aria-label=${this.clearLabel}\n ?hidden=${!this._hasInput || (!this._hasValue && !(this.showClearOnFocus && this._focused))}\n ?disabled=${this._inputDisabled}\n @click=${this._handleClear}\n >\n <slot name=\"clear-icon\"><span aria-hidden=\"true\">&#10005;</span></slot>\n </button>\n <span\n id=\"trailing\"\n part=\"trailing\"\n class=${classMap({ empty: !this._hasTrailing })}\n >\n <slot name=\"trailing\" @slotchange=${this._onTrailingSlotChange}></slot>\n </span>\n </div>\n `;\n }\n\n /** Programmatically clears the value and fires rc-search-bar-clear + rc-search-bar-input. */\n public clear(): void {\n this._handleClear();\n }\n\n private _onLeadingSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasLeading = slot.assignedElements().length > 0;\n }\n\n private _onTrailingSlotChange(e: Event): void {\n const slot = e.target as HTMLSlotElement;\n this._hasTrailing = slot.assignedElements().length > 0;\n }\n}\n\nexport default RCSearchBar;\n"],"names":["searchBarStyles","css","_uaClearSheet","_uaClearSuppressed","_suppressUaClear","_RCSearchBar","LitElement","input","value","old","oldValue","changed","el","previous","html","classMap","slot","RCSearchBar","__decorateClass","property","state"],"mappings":"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"}