@rcarls/rc-select 0.5.0 → 0.7.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.
@@ -1,198 +1,47 @@
1
- import { css as f, LitElement as b, nothing as _, html as h } from "lit";
2
- import { property as n, query as d, state as p } from "lit/decorators.js";
3
- import { NativeChildController as m, ActiveDescendantController as g, AnchorController as y } from "@rcarls/rc-common";
4
- const x = f`
5
- :host {
6
- display: inline-block;
7
- }
8
-
9
- #anchor {
10
- display: flex;
11
- align-items: center;
12
- flex-wrap: wrap;
13
- gap: var(--rc-select-gap, var(--rc-control-gap, 0.25em));
14
- }
15
-
16
- #trigger {
17
- display: flex;
18
- align-items: center;
19
- flex-wrap: wrap;
20
- gap: var(--rc-select-gap, var(--rc-control-gap, 0.25em));
21
- min-block-size: var(--rc-select-control-block-size, var(--rc-control-block-size, auto));
22
- padding: var(--rc-select-padding-block, var(--rc-control-padding-block, 1px))
23
- var(--rc-select-padding-inline, var(--rc-control-padding-inline, 4px));
24
- min-width: 8em;
25
- cursor: default;
26
- user-select: none;
27
- border: var(
28
- --rc-select-border,
29
- var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
30
- );
31
- border-radius: var(--rc-select-radius, var(--rc-control-radius, var(--rc-radius-sm, 0.125em)));
32
- background: var(--rc-field, Field);
33
- color: var(--rc-field-text, FieldText);
34
- font-family: var(--rc-font-family, inherit);
35
- font-size: var(--rc-font-size, inherit);
36
- line-height: var(--rc-line-height, normal);
37
- transition:
38
- background-color var(--rc-motion-duration, 120ms),
39
- border-color var(--rc-motion-duration, 120ms),
40
- box-shadow var(--rc-motion-duration, 120ms);
41
-
42
- /* Keyboard focus indicator */
43
- outline: none;
44
- &:focus-visible {
45
- outline: var(--rc-focus-ring, auto);
46
- outline-offset: var(--rc-focus-ring-offset, 0);
47
- }
48
- }
49
-
50
- [part='value-display'] {
51
- flex: 1;
52
- overflow: hidden;
53
- text-overflow: ellipsis;
54
- white-space: nowrap;
55
- }
56
-
57
- [part='toggle-indicator'] {
58
- flex-shrink: 0;
59
- display: inline-flex;
60
- align-items: center;
61
- justify-content: center;
62
- inline-size: var(--rc-select-toggle-indicator-size, 1.1em);
63
- }
64
-
65
- /* Chips — the whole chip is the remove button for a larger touch target */
66
- [part='chip'] {
67
- display: inline-flex;
68
- align-items: center;
69
- gap: var(--rc-select-chip-gap, calc(var(--rc-control-gap, 0.25em) * 0.8));
70
- padding: var(--rc-select-chip-padding-block, 0.1em) var(--rc-select-chip-padding-inline, 0.3em);
71
- border: var(
72
- --rc-select-chip-border,
73
- var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
74
- );
75
- border-radius: var(--rc-select-chip-radius, var(--rc-radius-md, 0.25em));
76
- background: var(--rc-button-bg, ButtonFace);
77
- color: var(--rc-button-text, ButtonText);
78
- font: inherit;
79
- font-size: 0.875em;
80
- cursor: pointer;
81
-
82
- &:hover {
83
- background: var(--rc-highlight, Highlight);
84
- color: var(--rc-highlight-text, HighlightText);
85
- }
86
-
87
- &:focus-visible {
88
- outline: var(--rc-focus-ring, auto);
89
- outline-offset: var(--rc-focus-ring-offset, 0);
90
- }
91
- }
92
-
93
- /* Decorative × icon inside the chip — no interaction, aria-hidden */
94
- [part='chip-remove'] {
95
- display: inline-flex;
96
- align-items: center;
97
- justify-content: center;
98
- width: 1em;
99
- font-size: 0.85em;
100
- pointer-events: none;
101
- }
102
-
103
- /* Hide the default slot — visually suppresses the slotted native <select> */
104
- slot:not([name]) {
105
- display: none;
106
- }
107
-
108
- /* Listbox popup — positioned by AnchorController via adoptedStyleSheets */
109
- rc-listbox {
110
- max-height: var(--rc-select-max-height, 20em);
111
- overflow-y: auto;
112
- background: var(--rc-surface, Canvas);
113
- border: var(
114
- --rc-select-listbox-border,
115
- var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
116
- );
117
- border-radius: var(--rc-select-listbox-radius, var(--rc-control-radius, 0));
118
- box-shadow: var(
119
- --rc-select-shadow,
120
- var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))
121
- );
122
- color: var(--rc-field-text, FieldText);
123
- padding-block: var(--rc-select-listbox-padding-block, var(--rc-control-padding-block, 0.25em));
124
- --rc-listbox-option-gap: var(--rc-item-gap, 0.4em);
125
- --rc-listbox-option-padding-block: var(--rc-item-padding-block, 0.3em);
126
- --rc-listbox-option-padding-inline: var(--rc-item-padding-inline, 0.75em);
127
- --rc-listbox-hover-bg: var(--rc-highlight, Highlight);
128
- --rc-listbox-hover-color: var(--rc-highlight-text, HighlightText);
129
- --rc-listbox-active-bg: var(--rc-highlight, Highlight);
130
- --rc-listbox-active-color: var(--rc-highlight-text, HighlightText);
131
- --rc-listbox-selected-bg: var(--rc-highlight, Highlight);
132
- --rc-listbox-selected-color: var(--rc-highlight-text, HighlightText);
133
- --rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);
134
-
135
- &:not(:popover-open) {
136
- display: none;
137
- }
138
- }
139
-
140
- rc-listbox [part~='option'][hidden] {
141
- display: none;
142
- }
143
-
144
- rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {
145
- outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));
146
- outline-offset: -2px;
147
- }
148
-
149
- rc-listbox [part~='option'][aria-disabled='true'] {
150
- cursor: not-allowed;
151
- }
152
-
153
- /* Selection checkmark — reserves space whether visible or not */
154
- rc-listbox [part~='option-checkmark'] {
155
- flex-shrink: 0;
156
- width: 1em;
157
- text-align: center;
158
- font-size: 0.85em;
159
- visibility: hidden;
160
- }
161
-
162
- rc-listbox [part~='option'][aria-selected='true'] [part~='option-checkmark'] {
163
- visibility: visible;
164
- }
165
-
166
- rc-listbox [part~='create-option'] {
167
- font-style: italic;
168
- border-top: 1px solid var(--rc-border-color, ButtonBorder);
169
- margin-top: 0.25em;
170
- padding-top: 0.3em;
171
- }
172
- `;
173
- var k = Object.defineProperty, a = (v, e, t, i) => {
174
- for (var s = void 0, r = v.length - 1, l; r >= 0; r--)
175
- (l = v[r]) && (s = l(e, t, s) || s);
176
- return s && k(e, t, s), s;
1
+ import { css as k, LitElement as $, nothing as p, html as c } from "lit";
2
+ import { property as d, query as _, state as u } from "lit/decorators.js";
3
+ import { MessagesController as C, NativeChildController as w, ActiveDescendantController as S, AnchorController as V, PopoverController as D, inlineArrowKeys as g, resolveFlow as f, bidiIsolate as b, numberFormat as A } from "@rcarls/rc-common";
4
+ const F = k`:host{display:inline-block}#anchor{align-items:center;gap:var(--rc-select-gap,var(--rc-control-gap,.25em));flex-wrap:wrap;display:flex}#trigger{align-items:center;gap:var(--rc-select-gap,var(--rc-control-gap,.25em));min-block-size:var(--rc-select-control-block-size,var(--rc-control-block-size,auto));padding:var(--rc-select-padding-block,var(--rc-control-padding-block,1px)) var(--rc-select-padding-inline,var(--rc-control-padding-inline,4px));cursor:default;user-select:none;border:var(--rc-select-border,var(--rc-border,1px solid var(--rc-border-color,ButtonBorder)));border-radius:var(--rc-select-radius,var(--rc-control-radius,var(--rc-radius-sm,.125em)));background:var(--rc-field,Field);min-width:8em;color:var(--rc-field-text,FieldText);font-family:var(--rc-font-family,inherit);font-size:var(--rc-font-size,inherit);line-height:var(--rc-line-height,normal);transition:background-color var(--rc-motion-duration,.12s), border-color var(--rc-motion-duration,.12s), box-shadow var(--rc-motion-duration,.12s);outline:none;flex-wrap:wrap;display:flex;&:focus-visible{outline:var(--rc-focus-ring,auto);outline-offset:var(--rc-focus-ring-offset,0)}}[part=value-display]{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}[part=toggle-indicator]{inline-size:var(--rc-select-toggle-indicator-size,1.1em);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}[part=chips]{--rc-chip-group-column-gap:var(--rc-select-gap,var(--rc-control-gap,.25em));--rc-chip-group-row-gap:calc(var(--rc-chip-touch-target-overlap-block-start,0px) + var(--rc-chip-touch-target-overlap-block-end,0px));flex:0 auto;min-inline-size:0;max-inline-size:100%}[part=chip]{align-items:center;gap:var(--rc-select-chip-gap,calc(var(--rc-control-gap,.25em) * .8));padding-block:var(--rc-select-chip-padding-block,.1em);border:var(--rc-select-chip-border,var(--rc-border,1px solid var(--rc-border-color,ButtonBorder)));border-radius:var(--rc-select-chip-radius,var(--rc-radius-md,.25em));background:var(--rc-button-bg,ButtonFace);color:var(--rc-button-text,ButtonText);font:inherit;cursor:pointer;padding-inline-start:var(--rc-select-chip-padding-inline-start,.3em);padding-inline-end:var(--rc-select-chip-padding-inline-end,calc(var(--rc-chip-remove-target-size,1.5rem) + var(--rc-chip-remove-offset-inline,.125rem)));font-size:.875em;display:inline-flex;&:hover{background:var(--rc-highlight,Highlight);color:var(--rc-highlight-text,HighlightText)}&:focus-visible{outline:var(--rc-focus-ring,auto);outline-offset:var(--rc-focus-ring-offset,0)}}[part=chip-remove]{pointer-events:none;justify-content:center;align-items:center;width:1em;font-size:.85em;display:inline-flex}slot:not([name]){display:none}rc-listbox{max-height:var(--rc-select-max-height,20em);overflow-block:auto;background:var(--rc-select-listbox-background,var(--rc-popup-background,var(--rc-surface,Canvas)));border:var(--rc-select-listbox-border,var(--rc-popup-border,var(--rc-border,1px solid var(--rc-border-color,ButtonBorder))));border-radius:var(--rc-select-listbox-radius,var(--rc-popup-radius,var(--rc-radius-sm,0)));box-shadow:var(--rc-select-shadow,var(--rc-popup-shadow,var(--rc-shadow,0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))));color:var(--rc-select-listbox-color,var(--rc-popup-color,var(--rc-field-text,FieldText)));padding-block:var(--rc-select-listbox-padding-block,var(--rc-popup-padding-block,var(--rc-control-padding-block,.25em)));--rc-listbox-option-gap:var(--rc-item-gap,.4em);--rc-listbox-option-padding-block:var(--rc-item-padding-block,.3em);--rc-listbox-option-padding-inline:var(--rc-item-padding-inline,.75em);--rc-listbox-hover-bg:var(--rc-highlight,Highlight);--rc-listbox-hover-color:var(--rc-highlight-text,HighlightText);--rc-listbox-active-bg:var(--rc-highlight,Highlight);--rc-listbox-active-color:var(--rc-highlight-text,HighlightText);--rc-listbox-selected-bg:var(--rc-highlight,Highlight);--rc-listbox-selected-color:var(--rc-highlight-text,HighlightText);--rc-listbox-disabled-opacity:var(--rc-disabled-opacity,.5);overflow-y:auto}@media (prefers-reduced-motion:no-preference){[part=listbox]:is(:popover-open,[data-rc-popover-fallback-open]){opacity:1;transition:opacity var(--rc-select-listbox-duration,.15s) var(--rc-motion-effects-easing-enter,ease-out), overlay var(--rc-select-listbox-duration,.15s) allow-discrete, display var(--rc-select-listbox-duration,.15s) allow-discrete}[part=listbox]:not(:popover-open):not([data-rc-popover-fallback-open]){opacity:0;transition:opacity var(--rc-select-listbox-duration,.15s) var(--rc-motion-effects-easing-exit,ease-in), overlay var(--rc-select-listbox-duration,.15s) allow-discrete, display var(--rc-select-listbox-duration,.15s) allow-discrete}@starting-style{[part=listbox]:is(:popover-open,[data-rc-popover-fallback-open]){opacity:0}}}[part=listbox]:not(:popover-open):not([data-rc-popover-fallback-open]){display:none}rc-dialog[variant=fullscreen]{display:contents}[part~=dialog]{box-sizing:border-box;background:var(--rc-surface,Canvas);color:var(--rc-field-text,CanvasText);font-family:var(--rc-font-family,inherit);&[open]{gap:var(--rc-select-dialog-gap,1rem);flex-direction:column;display:flex}}[part~=dialog-header]{align-items:center;gap:var(--rc-select-dialog-header-gap,.5rem);grid-template-columns:auto minmax(0,1fr) auto;display:grid}[part~=dialog-header]>[part~=dialog-title]:first-child{grid-column:1/3}[part~=dialog-title]{text-overflow:ellipsis;white-space:nowrap;min-inline-size:0;font:inherit;margin:0;overflow:hidden}[part~=dialog-cancel],[part~=dialog-confirm]{min-block-size:2.75rem;min-inline-size:2.75rem}[part~=dialog-cancel]{justify-content:center;align-items:center;padding:0;display:inline-flex}[part~=dialog-cancel-icon]{fill:currentColor}[part~=dialog-selected]{max-block-size:30%;overflow:auto}[part~=dialog-listbox]{min-block-size:0;max-block-size:none;box-shadow:none;flex:1}rc-listbox [part~=option][hidden]{display:none}rc-listbox [part~=option][data-active]:not([aria-disabled=true]){outline:var(--rc-focus-ring,2px solid var(--rc-accent,Highlight));outline-offset:-2px}rc-listbox [part~=option][aria-disabled=true]{cursor:not-allowed}rc-listbox [part~=option-checkmark]{text-align:center;visibility:hidden;flex-shrink:0;width:1em;font-size:.85em}rc-listbox [part~=option][aria-selected=true] [part~=option-checkmark]{visibility:visible}rc-listbox [part~=create-option]{border-top:1px solid var(--rc-border-color,ButtonBorder);margin-top:.25em;padding-top:.3em;font-style:italic}`;
5
+ var E = Object.defineProperty, l = (h, e, t, i) => {
6
+ for (var s = void 0, r = h.length - 1, a; r >= 0; r--)
7
+ (a = h[r]) && (s = a(e, t, s) || s);
8
+ return s && E(e, t, s), s;
177
9
  };
178
- const u = class u extends b {
10
+ let m, y;
11
+ function x() {
12
+ return m ??= import("@rcarls/rc-dialog/define"), m;
13
+ }
14
+ function O() {
15
+ return y ??= import("@rcarls/rc-chip-group/define"), y;
16
+ }
17
+ const z = Object.freeze({
18
+ options: "Options",
19
+ selectedItems: "Selected items",
20
+ remove: (h) => `Remove ${b(h)}`,
21
+ summary: (h, e, t) => `${b(h)}, +${A(t, { useGrouping: !1 }).format(e)} more`,
22
+ done: "Done",
23
+ cancel: "Cancel"
24
+ }), v = class v extends $ {
179
25
  constructor() {
180
- super(...arguments), this.open = !1, this.multiple = !1, this.disabled = !1, this.placeholder = "", this.display = "auto", this._selectedValues = /* @__PURE__ */ new Set(), this._chipNavIndex = -1, this._options = [], this._$selectRef = null, this._selectController = new m(this, {
26
+ super(...arguments), this._messages = new C(this, "rc-select", z, {
27
+ overrides: () => this.messages
28
+ }), this.open = !1, this.multiple = !1, this.disabled = !1, this.required = !1, this.placeholder = "", this.display = "auto", this.popupMode = "popover", this.dialogCancelButton = "visible", this._selectedValues = /* @__PURE__ */ new Set(), this._pendingSelectedValues = null, this._accessibleName = "", this._activePopupMode = "popover", this._chipNavIndex = -1, this._options = [], this._$selectRef = null, this._selectController = new w(this, {
181
29
  selector: ":scope > select",
182
30
  observe: !0,
183
31
  onChange: (e) => this._setupSelect(e),
184
32
  onMissing: () => {
185
33
  }
186
- }), this._mutationObserver = null, this._selectionInitialized = !1, this._pickerGuardActive = !1, this._pickerGuardConsumerDisabled = !1, this._pickerGuardForm = null, this._typeAheadBuffer = "", this._typeAheadTimer = 0, this._activeDescendantCtrl = new g(this, {
187
- host: () => this._$trigger ?? null,
34
+ }), this._mutationObserver = null, this._selectionInitialized = !1, this._pickerGuardActive = !1, this._pickerGuardConsumerDisabled = !1, this._pickerGuardForm = null, this._typeAheadBuffer = "", this._typeAheadTimer = 0, this._activeDescendantCtrl = new S(this, {
35
+ host: () => this._$activeDescendantHost,
188
36
  items: () => this._$listbox?.navigableItems ?? []
189
- }), this._anchorCtrl = new y(this, {
37
+ }), this._anchorCtrl = new V(this, {
190
38
  anchor: () => this._$anchor ?? null,
191
39
  floating: () => this._$listbox ?? null,
192
40
  shadowHost: () => this,
193
- placement: "bottom-start",
194
- offset: 2
195
- }), this._onDocClick = (e) => {
41
+ placement: "block-end-start",
42
+ offset: 2,
43
+ disabled: () => !this.open || this._activePopupMode === "dialog"
44
+ }), this._popoverCtrl = new D(this, { element: () => this._$listbox ?? null }), this._onDocClick = (e) => {
196
45
  this.open && (e.composedPath().includes(this) || this.closePopup(!1));
197
46
  }, this._onDocKeyDown = (e) => {
198
47
  this.open && e.key === "Escape" && (e.preventDefault(), this.closePopup());
@@ -213,6 +62,14 @@ const u = class u extends b {
213
62
  e.preventDefault(), t.dispatchEvent(new Event("invalid", { cancelable: !0 })) && this._$trigger?.focus();
214
63
  };
215
64
  }
65
+ /** The resolved built-in text. Subclasses with more text resolve their own scope. */
66
+ get _text() {
67
+ return this._messages.value;
68
+ }
69
+ /** Element that owns `aria-activedescendant` for the currently rendered popup. */
70
+ get _$activeDescendantHost() {
71
+ return this._$trigger ?? null;
72
+ }
216
73
  connectedCallback() {
217
74
  super.connectedCallback(), document.addEventListener("click", this._onDocClick, { capture: !0 }), document.addEventListener("keydown", this._onDocKeyDown, { capture: !0 });
218
75
  const e = this._$selectRef?.deref();
@@ -224,19 +81,47 @@ const u = class u extends b {
224
81
  }), this._mutationObserver?.disconnect(), this._syncFormListeners(null);
225
82
  }
226
83
  updated(e) {
227
- super.updated(e), this.toggleAttribute("has-value", this._selectedValues.size > 0);
84
+ super.updated(e), this.toggleAttribute("has-value", this._selectedValues.size > 0), this.popupMode === "dialog" && x(), this.multiple && O();
85
+ }
86
+ /**
87
+ * Forwards focus to the trigger. The host itself is never in the tab order, so without this
88
+ * override, calling `.focus()` on `<rc-select>` (as `rc-field` does when treating it as a
89
+ * control provider) would be a no-op.
90
+ */
91
+ focus(e) {
92
+ this._$trigger?.focus(e);
93
+ }
94
+ /** Forwards blur to the trigger; see `focus()`. */
95
+ blur() {
96
+ this._$trigger?.blur();
228
97
  }
229
98
  /** Opens the popup listbox if not already open or disabled. */
230
99
  openPopup() {
231
- this.open || this.disabled || (this.open = !0, this._$listbox.showPopover(), this._anchorCtrl.update(), this.dispatchEvent(new CustomEvent("rc-select-open", { bubbles: !0, composed: !0 })), this.requestUpdate());
100
+ if (!(this.open || this.disabled)) {
101
+ if (this._activePopupMode = this.popupMode, this._activePopupMode === "dialog") {
102
+ this._pendingSelectedValues = new Set(this._selectedValues), this.open = !0, this.requestUpdate(), this.updateComplete.then(async () => {
103
+ await x();
104
+ const e = this._$dialogHost;
105
+ !this.open || this._activePopupMode !== "dialog" || !e || (await e.updateComplete, !(!this.open || this._activePopupMode !== "dialog") && (this._$listbox?.setSelectedValues(this._dialogSelectedValues), e.showModal(), this._focusDialogContent(), this.dispatchEvent(new CustomEvent("rc-select-open", { bubbles: !0, composed: !0 }))));
106
+ });
107
+ return;
108
+ }
109
+ this.open = !0, this._popoverCtrl.sync(!0), this._anchorCtrl.update(), this.dispatchEvent(new CustomEvent("rc-select-open", { bubbles: !0, composed: !0 })), this.requestUpdate();
110
+ }
232
111
  }
233
112
  /**
234
113
  * Closes the popup listbox.
235
114
  *
236
- * @param returnFocus - When `true` (default), returns focus to the trigger.
115
+ * @param returnFocus - when `true` (default), returns focus to the trigger
237
116
  */
238
117
  closePopup(e = !0) {
239
- this.open && (this.open = !1, this._$listbox.hidePopover(), this._activeDescendantCtrl.clear(), e && this._$trigger?.focus(), this.dispatchEvent(new CustomEvent("rc-select-close", { bubbles: !0, composed: !0 })));
118
+ if (this.open) {
119
+ if (this._activePopupMode === "dialog") {
120
+ this._discardDialogSelection(), this._$dialogHost?.close?.("cancel"), this._finishDialogClose(e);
121
+ return;
122
+ }
123
+ this.open = !1, this._popoverCtrl.sync(!1), this._activeDescendantCtrl.clear(), e && this._$trigger?.focus(), this.dispatchEvent(new CustomEvent("rc-select-close", { bubbles: !0, composed: !0 }));
124
+ }
240
125
  }
241
126
  /**
242
127
  * Current selection. Host writes update selection silently; user interaction
@@ -282,10 +167,15 @@ const u = class u extends b {
282
167
  *
283
168
  * In single-select mode only the first value is kept. Syncs `_$listbox` and the
284
169
  * native `<select>` but does NOT dispatch `rc-select-change`.
170
+ *
171
+ * @param fromUser - pass `true` only for a selection driven by real user interaction, so
172
+ * `_syncNativeSelect` dispatches native `input`/`change` on the underlying `<select>` (a
173
+ * programmatic `value`/`defaultValue` write is not a native interaction and relies on an
174
+ * external `sync()` call instead, the same as `rc-textarea`'s controlled-value contract)
285
175
  */
286
- _applySelection(e) {
287
- const t = this.multiple ? e : e.slice(0, 1);
288
- this._selectedValues = new Set(t), this._$listbox?.setSelectedValues(t), this._syncNativeSelect(), this.requestUpdate();
176
+ _applySelection(e, t = !1) {
177
+ const i = this.multiple ? e : e.slice(0, 1);
178
+ this._selectedValues = new Set(i), this._$listbox?.setSelectedValues(i), this._syncNativeSelect(t), this.open && this._activePopupMode === "dialog" && this._pendingSelectedValues && (this._pendingSelectedValues = new Set(i)), this.requestUpdate();
289
179
  }
290
180
  /**
291
181
  * Countermeasure for select["multiple"] bug in Firefox for Android / GeckoView.
@@ -328,10 +218,10 @@ const u = class u extends b {
328
218
  this._mutationObserver?.disconnect(), this._mutationObserver = null;
329
219
  const t = this._$selectRef?.deref();
330
220
  t && (t.removeEventListener("click", this._onNativeSelectClick), this._pickerGuardActive && (t.disabled = this._pickerGuardConsumerDisabled)), this._pickerGuardActive = !1, this._syncFormListeners(null), this._$selectRef = e ? new WeakRef(e) : null, e && (e.addEventListener("click", this._onNativeSelectClick), queueMicrotask(() => {
331
- e.isConnected && (this.multiple = e.multiple, this.disabled = e.disabled, this._syncOptionsFromSelect(e), this._applyPickerGuard(e), this._mutationObserver = new MutationObserver(() => {
221
+ e.isConnected && (this.multiple = e.multiple, this.disabled = e.disabled, this.required = e.required, this._syncOptionsFromSelect(e), this._applyPickerGuard(e), this._mutationObserver = new MutationObserver(() => {
332
222
  const i = this._$selectRef?.deref();
333
223
  if (i) {
334
- if (this.multiple = i.multiple, this._pickerGuardActive || (this.disabled = i.disabled), this._applyPickerGuard(i), this._propertyOptions !== void 0) {
224
+ if (this.multiple = i.multiple, this.required = i.required, this._pickerGuardActive || (this.disabled = i.disabled), this._applyPickerGuard(i), this._propertyOptions !== void 0) {
335
225
  this._applySelectionFromCurrentSource();
336
226
  return;
337
227
  }
@@ -455,7 +345,7 @@ const u = class u extends b {
455
345
  if (!this._$trigger || this._$trigger.hasAttribute("aria-label"))
456
346
  return;
457
347
  const t = e.getAttribute("aria-label") ?? e.labels?.[0]?.textContent?.trim() ?? null;
458
- t ? this._$trigger.setAttribute("aria-label", t) : this._$trigger.removeAttribute("aria-label");
348
+ this._accessibleName = t ?? "", t ? this._$trigger.setAttribute("aria-label", t) : this._$trigger.removeAttribute("aria-label");
459
349
  }
460
350
  /**
461
351
  * Handles `rc-listbox-change` from the popup.
@@ -469,27 +359,41 @@ const u = class u extends b {
469
359
  e.stopPropagation();
470
360
  return;
471
361
  }
472
- const { optionValue: i, value: s, selected: r } = t, l = i ?? (Array.isArray(s) ? s.at(-1) : s);
473
- if (l) {
474
- if (e.stopPropagation(), this.multiple) {
475
- const c = new Set(this._selectedValues);
476
- r ? c.add(l) : c.delete(l), this._selectedValues = c;
362
+ const { optionValue: i, value: s, selected: r } = t, a = i ?? (Array.isArray(s) ? s.at(-1) : s);
363
+ if (a) {
364
+ if (e.stopPropagation(), this.open && this._activePopupMode === "dialog") {
365
+ const n = new Set(this._pendingSelectedValues ?? this._selectedValues);
366
+ this.multiple ? r ? n.add(a) : n.delete(a) : (n.clear(), r && n.add(a)), this._pendingSelectedValues = n, this._$listbox.setSelectedValues([...n]), this.requestUpdate();
367
+ return;
368
+ }
369
+ if (this.multiple) {
370
+ const n = new Set(this._selectedValues);
371
+ r ? n.add(a) : n.delete(a), this._selectedValues = n;
477
372
  } else
478
- this._selectedValues = r ? /* @__PURE__ */ new Set([l]) : /* @__PURE__ */ new Set(), r && this.closePopup();
479
- this._selectionInitialized = !0, this._syncNativeSelect(), this._$listbox.setSelectedValues(this.selectedValues), this._dispatchChange();
373
+ this._selectedValues = r ? /* @__PURE__ */ new Set([a]) : /* @__PURE__ */ new Set(), r && this.closePopup();
374
+ this._selectionInitialized = !0, this._syncNativeSelect(!0), this._$listbox.setSelectedValues(this.selectedValues), this._dispatchChange();
480
375
  }
481
376
  }
482
377
  /** Removes a single value from `_selectedValues`, syncs the listbox and native select, and dispatches `rc-select-change`. */
483
378
  _removeValue(e) {
484
379
  const t = new Set(this._selectedValues);
485
- t.delete(e), this._selectedValues = t, this._$listbox.setSelectedValues([...t]), this._selectionInitialized = !0, this._syncNativeSelect(), this._dispatchChange();
380
+ t.delete(e), this._selectedValues = t, this._$listbox.setSelectedValues([...t]), this._selectionInitialized = !0, this._syncNativeSelect(!0), this._dispatchChange();
486
381
  }
487
- /** Flips `selected` on each `<option>` in the native `<select>` to match `_selectedValues`; no-op when no native select is present. */
488
- _syncNativeSelect() {
489
- const e = this._$selectRef?.deref();
490
- if (e)
491
- for (const t of e.options)
492
- t.selected = this._selectedValues.has(t.value);
382
+ /**
383
+ * Flips `selected` on each `<option>` in the native `<select>` to match `_selectedValues`;
384
+ * no-op when no native select is present.
385
+ *
386
+ * @param fromUser - when `true`, also dispatches native `input` and `change` on `$select`
387
+ * (in that order, matching a real `<select>`) so outside listeners, including an
388
+ * `rc-field` ancestor treating this component as its control provider, observe the change
389
+ */
390
+ _syncNativeSelect(e = !1) {
391
+ const t = this._$selectRef?.deref();
392
+ if (t) {
393
+ for (const i of t.options)
394
+ i.selected = this._selectedValues.has(i.value);
395
+ e && (t.dispatchEvent(new Event("input", { bubbles: !0 })), t.dispatchEvent(new Event("change", { bubbles: !0 })));
396
+ }
493
397
  }
494
398
  /** Constructs and dispatches `rc-select-change` with current `value`, `selectedValues`, and `selectedOptions`. */
495
399
  _dispatchChange() {
@@ -536,8 +440,8 @@ const u = class u extends b {
536
440
  return this.placeholder;
537
441
  if (this._selectedValues.size === 1)
538
442
  return this._labelFor([...this._selectedValues][0]);
539
- const e = [...this._selectedValues];
540
- return `${this._labelFor(e[0])}, +${e.length - 1} more`;
443
+ const e = [...this._selectedValues], t = this._labelFor(e[0]);
444
+ return this._text.summary(t, e.length - 1, this._messages.locale);
541
445
  }
542
446
  /**
543
447
  * APG Combobox keyboard handler on the trigger.
@@ -566,10 +470,11 @@ const u = class u extends b {
566
470
  case "Tab":
567
471
  this.open && this.closePopup(!1);
568
472
  break;
569
- case "ArrowLeft":
570
- !this.open && this.multiple && this._selectedValues.size > 0 && (e.preventDefault(), this._enterChipNav());
571
- break;
572
473
  default:
474
+ if (e.key === g(f(this)).prev && !this.open && this.multiple && this._selectedValues.size > 0) {
475
+ e.preventDefault(), this._enterChipNav();
476
+ break;
477
+ }
573
478
  e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && this._handleTypeAhead(e.key);
574
479
  }
575
480
  }
@@ -596,9 +501,9 @@ const u = class u extends b {
596
501
  );
597
502
  if (i)
598
503
  if (!this.open)
599
- this.multiple || (this._selectionInitialized = !0, this._applySelection([i.value]), this._dispatchChange());
504
+ this.multiple || (this._selectionInitialized = !0, this._applySelection([i.value], !0), this._dispatchChange());
600
505
  else {
601
- const r = this._$listbox.navigableItems.find((l) => l.getAttribute("data-value") === i.value);
506
+ const r = this._$listbox.navigableItems.find((a) => a.getAttribute("data-value") === i.value);
602
507
  r && this._activeDescendantCtrl.navigateToItem(r);
603
508
  }
604
509
  }
@@ -607,9 +512,11 @@ const u = class u extends b {
607
512
  const e = this._$chipButtons();
608
513
  e.length !== 0 && (this._chipNavIndex = e.length - 1, e[this._chipNavIndex].focus());
609
514
  }
610
- /** Queries all `button[part~="chip"]` elements in `renderRoot`; order matches DOM order. */
515
+ /** Queries all native remove buttons inside input chips; order matches DOM order. */
611
516
  _$chipButtons() {
612
- return Array.from(this.renderRoot.querySelectorAll('button[part~="chip"]'));
517
+ return Array.from(
518
+ this.renderRoot.querySelectorAll('rc-chip > button[part~="chip"]')
519
+ );
613
520
  }
614
521
  /**
615
522
  * Keyboard handler for chip buttons.
@@ -619,12 +526,12 @@ const u = class u extends b {
619
526
  * and return focus to the trigger.
620
527
  */
621
528
  _handleChipKeyDown(e, t) {
622
- const i = this._$chipButtons();
529
+ const i = this._$chipButtons(), s = g(f(this));
623
530
  switch (e.key) {
624
- case "ArrowLeft":
531
+ case s.prev:
625
532
  e.preventDefault(), this._chipNavIndex > 0 && (this._chipNavIndex--, i[this._chipNavIndex]?.focus());
626
533
  break;
627
- case "ArrowRight":
534
+ case s.next:
628
535
  e.preventDefault(), this._chipNavIndex < i.length - 1 ? (this._chipNavIndex++, i[this._chipNavIndex]?.focus()) : (this._chipNavIndex = -1, this._$trigger?.focus());
629
536
  break;
630
537
  case "Escape":
@@ -642,24 +549,145 @@ const u = class u extends b {
642
549
  _handleTriggerClick() {
643
550
  this.open ? this.closePopup() : this.openPopup();
644
551
  }
552
+ get _dialogSelectedValues() {
553
+ return [...this._pendingSelectedValues ?? this._selectedValues];
554
+ }
555
+ _focusDialogContent() {
556
+ this._$listbox?.focus();
557
+ }
558
+ /** Hook for subclasses to finalize provisional options before dialog selection commits. */
559
+ _prepareDialogCommit() {
560
+ }
561
+ _commitDialogSelection() {
562
+ if (!this.open || this._activePopupMode !== "dialog")
563
+ return;
564
+ this._prepareDialogCommit();
565
+ const e = this._dialogSelectedValues, t = this.selectedValues, i = e.length !== t.length || e.some((s) => !t.includes(s));
566
+ this._pendingSelectedValues = null, i && (this._selectionInitialized = !0, this._applySelection(e, !0), this._dispatchChange()), this._$dialogHost?.close("done"), this._finishDialogClose(!0);
567
+ }
568
+ _discardDialogSelection() {
569
+ this._pendingSelectedValues = null, this._$listbox?.setSelectedValues(this.selectedValues);
570
+ }
571
+ _finishDialogClose(e) {
572
+ this.open && (this.open = !1, this._activeDescendantCtrl.clear(), e && this._$trigger?.focus(), this.dispatchEvent(new CustomEvent("rc-select-close", { bubbles: !0, composed: !0 })), this.requestUpdate());
573
+ }
574
+ _handleDialogClose(e) {
575
+ e.stopPropagation(), this.open && (this._discardDialogSelection(), this._finishDialogClose(!0));
576
+ }
577
+ _stopDialogEvent(e) {
578
+ e.stopPropagation();
579
+ }
580
+ _removePendingValue(e) {
581
+ const t = new Set(this._pendingSelectedValues ?? this._selectedValues);
582
+ t.delete(e), this._pendingSelectedValues = t, this._$listbox?.setSelectedValues([...t]), this.requestUpdate();
583
+ }
584
+ _renderDialogBody() {
585
+ return c`
586
+ <rc-listbox
587
+ lang=${this.getAttribute("lang") ?? p}
588
+ id="dialog-listbox"
589
+ part="listbox dialog-listbox"
590
+ tabindex="0"
591
+ aria-label=${this._accessibleName || this._text.options}
592
+ ?multiple=${this.multiple}
593
+ checkmark
594
+ .options=${this._options}
595
+ @rc-listbox-change=${this._handleListboxChange}
596
+ ></rc-listbox>
597
+ `;
598
+ }
599
+ _renderDialogPopup() {
600
+ const e = this._accessibleName || this._text.options, t = this.multiple && this._dialogSelectedValues.length > 0;
601
+ return c`
602
+ <rc-dialog
603
+ id="dialog-host"
604
+ variant="fullscreen"
605
+ closed-by="closerequest"
606
+ @rc-dialog-open=${this._stopDialogEvent}
607
+ @rc-dialog-toggle=${this._stopDialogEvent}
608
+ @rc-dialog-request-close=${this._stopDialogEvent}
609
+ @rc-dialog-cancel=${this._stopDialogEvent}
610
+ @rc-dialog-close=${this._handleDialogClose}
611
+ >
612
+ <dialog id="dialog" part="dialog" aria-labelledby="dialog-title">
613
+ <header part="dialog-header">
614
+ ${this.dialogCancelButton === "visible" ? c`
615
+ <button
616
+ part="dialog-cancel"
617
+ type="button"
618
+ aria-label=${this._text.cancel}
619
+ @click=${() => this.closePopup()}
620
+ >
621
+ <svg
622
+ part="dialog-cancel-icon"
623
+ aria-hidden="true"
624
+ viewBox="0 0 24 24"
625
+ width="24"
626
+ height="24"
627
+ >
628
+ <path
629
+ d="M6.4 5 5 6.4l5.6 5.6L5 17.6 6.4 19l5.6-5.6 5.6 5.6 1.4-1.4-5.6-5.6L19 6.4 17.6 5 12 10.6Z"
630
+ />
631
+ </svg>
632
+ </button>
633
+ ` : p}
634
+
635
+ <h2 id="dialog-title" part="dialog-title">${e}</h2>
636
+
637
+ <div part="dialog-actions">
638
+ <button part="dialog-confirm" type="button" @click=${this._commitDialogSelection}>
639
+ ${this._text.done}
640
+ </button>
641
+ </div>
642
+ </header>
643
+
644
+ ${t ? c`<div part="dialog-selected">
645
+ ${this._renderChips(this._dialogSelectedValues, !0)}
646
+ </div>` : p}
647
+ ${this._renderDialogBody()}
648
+ </dialog>
649
+ </rc-dialog>
650
+ `;
651
+ }
645
652
  render() {
646
- const e = this.multiple && this._effectiveDisplay === "chips" && this._selectedValues.size > 0;
647
- return h`
653
+ return (this.open ? this._activePopupMode : this.popupMode) === "dialog" ? c`
654
+ ${this._renderAnchor()} ${this.open ? this._renderDialogPopup() : p}
655
+ <slot @slotchange=${this._handleSelectSlotChange}></slot>
656
+ ` : c`
657
+ ${this._renderAnchor()}
658
+
659
+ <rc-listbox
660
+ lang=${this.getAttribute("lang") ?? p}
661
+ id="listbox"
662
+ part="listbox"
663
+ popover="manual"
664
+ ?multiple=${this.multiple}
665
+ checkmark
666
+ @rc-listbox-change=${this._handleListboxChange}
667
+ ></rc-listbox>
668
+
669
+ <slot @slotchange=${this._handleSelectSlotChange}></slot>
670
+ `;
671
+ }
672
+ _renderAnchor() {
673
+ const e = this.multiple && this._effectiveDisplay === "chips" && this._selectedValues.size > 0, t = (this.open ? this._activePopupMode : this.popupMode) === "dialog";
674
+ return c`
648
675
  <div id="anchor" part="anchor">
649
676
  <div
650
677
  id="trigger"
651
678
  part="trigger"
652
679
  role="combobox"
653
680
  tabindex=${this.disabled ? "-1" : "0"}
654
- aria-haspopup="listbox"
681
+ aria-haspopup=${t ? "dialog" : "listbox"}
655
682
  aria-expanded=${this.open ? "true" : "false"}
656
- aria-controls="listbox"
683
+ aria-controls=${t ? "dialog" : "listbox"}
657
684
  aria-disabled=${this.disabled ? "true" : "false"}
658
- aria-label=${this.placeholder || _}
685
+ aria-required=${this.required ? "true" : "false"}
686
+ aria-label=${this.placeholder || p}
659
687
  @click=${this._handleTriggerClick}
660
688
  @keydown=${this._handleTriggerKeyDown}
661
689
  >
662
- ${e ? this._renderChips() : _}
690
+ ${e ? this._renderChips() : p}
663
691
 
664
692
  <span part="value-display">${this._displayLabel}</span>
665
693
 
@@ -681,82 +709,100 @@ const u = class u extends b {
681
709
  </span>
682
710
  </div>
683
711
  </div>
684
-
685
- <rc-listbox
686
- id="listbox"
687
- part="listbox"
688
- popover="manual"
689
- ?multiple=${this.multiple}
690
- checkmark
691
- @rc-listbox-change=${this._handleListboxChange}
692
- ></rc-listbox>
693
-
694
- <slot @slotchange=${this._handleSelectSlotChange}></slot>
695
712
  `;
696
713
  }
697
- /** Renders the chip group `<span>` with one `<button>` per selected value for multi-select chip display mode. */
698
- _renderChips() {
699
- return h`
700
- <span part="chips" role="group" aria-label="Selected items">
701
- ${[...this._selectedValues].map((e) => {
702
- const t = this._labelFor(e);
703
- return h`
704
- <button
705
- type="button"
706
- part="chip"
707
- data-value=${e}
708
- tabindex="-1"
709
- aria-label=${`Remove ${t}`}
710
- @click=${(i) => {
711
- i.stopPropagation(), this._removeValue(e);
714
+ /** Renders selected values as removable input chips inside the shared chip-group layout. */
715
+ _renderChips(e = this._selectedValues, t = !1) {
716
+ const i = this._text;
717
+ return c`
718
+ <rc-chip-group part="chips" kind="generic" layout="wrap" aria-label=${i.selectedItems}>
719
+ ${[...e].map((s) => {
720
+ const r = this._labelFor(s);
721
+ return c`
722
+ <rc-chip
723
+ variant="input"
724
+ removable
725
+ data-value=${s}
726
+ @rc-chip-remove=${(a) => {
727
+ a.stopPropagation(), t ? this._removePendingValue(s) : this._removeValue(s);
712
728
  }}
713
- @keydown=${(i) => this._handleChipKeyDown(i, e)}
714
729
  >
715
- <span part="chip-label">${t}</span
716
- ><span part="chip-remove" aria-hidden="true">&#215;</span>
717
- </button>
730
+ <button
731
+ type="button"
732
+ part="chip"
733
+ tabindex="-1"
734
+ aria-label=${i.remove(r)}
735
+ @click=${(a) => a.stopPropagation()}
736
+ @keydown=${(a) => this._handleChipKeyDown(a, s)}
737
+ >
738
+ <span part="chip-label">${r}</span>
739
+ </button>
740
+ <span slot="remove-icon" part="chip-remove" aria-hidden="true">&#215;</span>
741
+ </rc-chip>
718
742
  `;
719
743
  })}
720
- </span>
744
+ </rc-chip-group>
721
745
  `;
722
746
  }
723
747
  };
724
- u.styles = x;
725
- let o = u;
726
- a([
727
- n({ type: Boolean, reflect: !0 })
748
+ v.styles = F;
749
+ let o = v;
750
+ l([
751
+ d({ attribute: !1 })
752
+ ], o.prototype, "messages");
753
+ l([
754
+ d({ type: Boolean, reflect: !0 })
728
755
  ], o.prototype, "open");
729
- a([
730
- n({ type: Boolean, reflect: !0 })
756
+ l([
757
+ d({ type: Boolean, reflect: !0 })
731
758
  ], o.prototype, "multiple");
732
- a([
733
- n({ type: Boolean, reflect: !0 })
759
+ l([
760
+ d({ type: Boolean, reflect: !0 })
734
761
  ], o.prototype, "disabled");
735
- a([
736
- n()
762
+ l([
763
+ d({ type: Boolean, reflect: !0 })
764
+ ], o.prototype, "required");
765
+ l([
766
+ d()
737
767
  ], o.prototype, "placeholder");
738
- a([
739
- n()
768
+ l([
769
+ d()
740
770
  ], o.prototype, "display");
741
- a([
742
- d("#anchor")
771
+ l([
772
+ d({ attribute: "popup-mode", reflect: !0 })
773
+ ], o.prototype, "popupMode");
774
+ l([
775
+ d({ attribute: "dialog-cancel-button", reflect: !0 })
776
+ ], o.prototype, "dialogCancelButton");
777
+ l([
778
+ _("#anchor")
743
779
  ], o.prototype, "_$anchor");
744
- a([
745
- d("#trigger")
780
+ l([
781
+ _("#trigger")
746
782
  ], o.prototype, "_$trigger");
747
- a([
748
- d("#listbox")
783
+ l([
784
+ _("rc-listbox")
749
785
  ], o.prototype, "_$listbox");
750
- a([
751
- p()
786
+ l([
787
+ _("#dialog-host")
788
+ ], o.prototype, "_$dialogHost");
789
+ l([
790
+ u()
752
791
  ], o.prototype, "_selectedValues");
753
- a([
754
- p()
792
+ l([
793
+ u()
794
+ ], o.prototype, "_pendingSelectedValues");
795
+ l([
796
+ u()
797
+ ], o.prototype, "_accessibleName");
798
+ l([
799
+ u()
755
800
  ], o.prototype, "_chipNavIndex");
756
- a([
757
- p()
801
+ l([
802
+ u()
758
803
  ], o.prototype, "_options");
759
804
  export {
805
+ z as D,
760
806
  o as R
761
807
  };
762
- //# sourceMappingURL=rc-select-BRp1j-JD.js.map
808
+ //# sourceMappingURL=rc-select-DX-fKYeR.js.map