@rcarls/rc-select 0.6.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,206 +1,47 @@
1
- import { css as f, LitElement as b, nothing as _, html as h } from "lit";
2
- import { property as n, query as p, state as d } 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
- [part='chips'] {
66
- flex: 0 1 auto;
67
- max-inline-size: 100%;
68
- min-inline-size: 0;
69
- --rc-chip-group-column-gap: var(--rc-select-gap, var(--rc-control-gap, 0.25em));
70
- --rc-chip-group-row-gap: 0;
71
- }
72
-
73
- /* Input-chip chrome; rc-chip keeps the native button as the removal action. */
74
- [part='chip'] {
75
- display: inline-flex;
76
- align-items: center;
77
- gap: var(--rc-select-chip-gap, calc(var(--rc-control-gap, 0.25em) * 0.8));
78
- padding: var(--rc-select-chip-padding-block, 0.1em) var(--rc-select-chip-padding-inline, 0.3em);
79
- border: var(
80
- --rc-select-chip-border,
81
- var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
82
- );
83
- border-radius: var(--rc-select-chip-radius, var(--rc-radius-md, 0.25em));
84
- background: var(--rc-button-bg, ButtonFace);
85
- color: var(--rc-button-text, ButtonText);
86
- font: inherit;
87
- font-size: 0.875em;
88
- cursor: pointer;
89
-
90
- &:hover {
91
- background: var(--rc-highlight, Highlight);
92
- color: var(--rc-highlight-text, HighlightText);
93
- }
94
-
95
- &:focus-visible {
96
- outline: var(--rc-focus-ring, auto);
97
- outline-offset: var(--rc-focus-ring-offset, 0);
98
- }
99
- }
100
-
101
- /* Decorative × icon inside the chip — no interaction, aria-hidden. */
102
- [part='chip-remove'] {
103
- display: inline-flex;
104
- align-items: center;
105
- justify-content: center;
106
- width: 1em;
107
- font-size: 0.85em;
108
- pointer-events: none;
109
- }
110
-
111
- /* Hide the default slot — visually suppresses the slotted native <select> */
112
- slot:not([name]) {
113
- display: none;
114
- }
115
-
116
- /* Listbox popup — positioned by AnchorController via adoptedStyleSheets */
117
- rc-listbox {
118
- max-height: var(--rc-select-max-height, 20em);
119
- overflow-y: auto;
120
- background: var(--rc-surface, Canvas);
121
- border: var(
122
- --rc-select-listbox-border,
123
- var(--rc-border, 1px solid var(--rc-border-color, ButtonBorder))
124
- );
125
- border-radius: var(--rc-select-listbox-radius, var(--rc-control-radius, 0));
126
- box-shadow: var(
127
- --rc-select-shadow,
128
- var(--rc-shadow, 0 2px 8px color-mix(in srgb, CanvasText 15%, transparent))
129
- );
130
- color: var(--rc-field-text, FieldText);
131
- padding-block: var(--rc-select-listbox-padding-block, var(--rc-control-padding-block, 0.25em));
132
- --rc-listbox-option-gap: var(--rc-item-gap, 0.4em);
133
- --rc-listbox-option-padding-block: var(--rc-item-padding-block, 0.3em);
134
- --rc-listbox-option-padding-inline: var(--rc-item-padding-inline, 0.75em);
135
- --rc-listbox-hover-bg: var(--rc-highlight, Highlight);
136
- --rc-listbox-hover-color: var(--rc-highlight-text, HighlightText);
137
- --rc-listbox-active-bg: var(--rc-highlight, Highlight);
138
- --rc-listbox-active-color: var(--rc-highlight-text, HighlightText);
139
- --rc-listbox-selected-bg: var(--rc-highlight, Highlight);
140
- --rc-listbox-selected-color: var(--rc-highlight-text, HighlightText);
141
- --rc-listbox-disabled-opacity: var(--rc-disabled-opacity, 0.5);
142
-
143
- &:not(:popover-open) {
144
- display: none;
145
- }
146
- }
147
-
148
- rc-listbox [part~='option'][hidden] {
149
- display: none;
150
- }
151
-
152
- rc-listbox [part~='option'][data-active]:not([aria-disabled='true']) {
153
- outline: var(--rc-focus-ring, 2px solid var(--rc-accent, Highlight));
154
- outline-offset: -2px;
155
- }
156
-
157
- rc-listbox [part~='option'][aria-disabled='true'] {
158
- cursor: not-allowed;
159
- }
160
-
161
- /* Selection checkmark — reserves space whether visible or not */
162
- rc-listbox [part~='option-checkmark'] {
163
- flex-shrink: 0;
164
- width: 1em;
165
- text-align: center;
166
- font-size: 0.85em;
167
- visibility: hidden;
168
- }
169
-
170
- rc-listbox [part~='option'][aria-selected='true'] [part~='option-checkmark'] {
171
- visibility: visible;
172
- }
173
-
174
- rc-listbox [part~='create-option'] {
175
- font-style: italic;
176
- border-top: 1px solid var(--rc-border-color, ButtonBorder);
177
- margin-top: 0.25em;
178
- padding-top: 0.3em;
179
- }
180
- `;
181
- var k = Object.defineProperty, a = (v, e, t, i) => {
182
- for (var s = void 0, r = v.length - 1, l; r >= 0; r--)
183
- (l = v[r]) && (s = l(e, t, s) || s);
184
- 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;
185
9
  };
186
- 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 $ {
187
25
  constructor() {
188
- 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, {
189
29
  selector: ":scope > select",
190
30
  observe: !0,
191
31
  onChange: (e) => this._setupSelect(e),
192
32
  onMissing: () => {
193
33
  }
194
- }), 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, {
195
- 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,
196
36
  items: () => this._$listbox?.navigableItems ?? []
197
- }), this._anchorCtrl = new y(this, {
37
+ }), this._anchorCtrl = new V(this, {
198
38
  anchor: () => this._$anchor ?? null,
199
39
  floating: () => this._$listbox ?? null,
200
40
  shadowHost: () => this,
201
- placement: "bottom-start",
202
- offset: 2
203
- }), 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) => {
204
45
  this.open && (e.composedPath().includes(this) || this.closePopup(!1));
205
46
  }, this._onDocKeyDown = (e) => {
206
47
  this.open && e.key === "Escape" && (e.preventDefault(), this.closePopup());
@@ -221,6 +62,14 @@ const u = class u extends b {
221
62
  e.preventDefault(), t.dispatchEvent(new Event("invalid", { cancelable: !0 })) && this._$trigger?.focus();
222
63
  };
223
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
+ }
224
73
  connectedCallback() {
225
74
  super.connectedCallback(), document.addEventListener("click", this._onDocClick, { capture: !0 }), document.addEventListener("keydown", this._onDocKeyDown, { capture: !0 });
226
75
  const e = this._$selectRef?.deref();
@@ -232,19 +81,47 @@ const u = class u extends b {
232
81
  }), this._mutationObserver?.disconnect(), this._syncFormListeners(null);
233
82
  }
234
83
  updated(e) {
235
- 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();
236
97
  }
237
98
  /** Opens the popup listbox if not already open or disabled. */
238
99
  openPopup() {
239
- 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
+ }
240
111
  }
241
112
  /**
242
113
  * Closes the popup listbox.
243
114
  *
244
- * @param returnFocus - When `true` (default), returns focus to the trigger.
115
+ * @param returnFocus - when `true` (default), returns focus to the trigger
245
116
  */
246
117
  closePopup(e = !0) {
247
- 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
+ }
248
125
  }
249
126
  /**
250
127
  * Current selection. Host writes update selection silently; user interaction
@@ -290,10 +167,15 @@ const u = class u extends b {
290
167
  *
291
168
  * In single-select mode only the first value is kept. Syncs `_$listbox` and the
292
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)
293
175
  */
294
- _applySelection(e) {
295
- const t = this.multiple ? e : e.slice(0, 1);
296
- 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();
297
179
  }
298
180
  /**
299
181
  * Countermeasure for select["multiple"] bug in Firefox for Android / GeckoView.
@@ -336,10 +218,10 @@ const u = class u extends b {
336
218
  this._mutationObserver?.disconnect(), this._mutationObserver = null;
337
219
  const t = this._$selectRef?.deref();
338
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(() => {
339
- 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(() => {
340
222
  const i = this._$selectRef?.deref();
341
223
  if (i) {
342
- 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) {
343
225
  this._applySelectionFromCurrentSource();
344
226
  return;
345
227
  }
@@ -463,7 +345,7 @@ const u = class u extends b {
463
345
  if (!this._$trigger || this._$trigger.hasAttribute("aria-label"))
464
346
  return;
465
347
  const t = e.getAttribute("aria-label") ?? e.labels?.[0]?.textContent?.trim() ?? null;
466
- 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");
467
349
  }
468
350
  /**
469
351
  * Handles `rc-listbox-change` from the popup.
@@ -477,27 +359,41 @@ const u = class u extends b {
477
359
  e.stopPropagation();
478
360
  return;
479
361
  }
480
- const { optionValue: i, value: s, selected: r } = t, l = i ?? (Array.isArray(s) ? s.at(-1) : s);
481
- if (l) {
482
- if (e.stopPropagation(), this.multiple) {
483
- const c = new Set(this._selectedValues);
484
- 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;
485
372
  } else
486
- this._selectedValues = r ? /* @__PURE__ */ new Set([l]) : /* @__PURE__ */ new Set(), r && this.closePopup();
487
- 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();
488
375
  }
489
376
  }
490
377
  /** Removes a single value from `_selectedValues`, syncs the listbox and native select, and dispatches `rc-select-change`. */
491
378
  _removeValue(e) {
492
379
  const t = new Set(this._selectedValues);
493
- 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();
494
381
  }
495
- /** Flips `selected` on each `<option>` in the native `<select>` to match `_selectedValues`; no-op when no native select is present. */
496
- _syncNativeSelect() {
497
- const e = this._$selectRef?.deref();
498
- if (e)
499
- for (const t of e.options)
500
- 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
+ }
501
397
  }
502
398
  /** Constructs and dispatches `rc-select-change` with current `value`, `selectedValues`, and `selectedOptions`. */
503
399
  _dispatchChange() {
@@ -544,8 +440,8 @@ const u = class u extends b {
544
440
  return this.placeholder;
545
441
  if (this._selectedValues.size === 1)
546
442
  return this._labelFor([...this._selectedValues][0]);
547
- const e = [...this._selectedValues];
548
- 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);
549
445
  }
550
446
  /**
551
447
  * APG Combobox keyboard handler on the trigger.
@@ -574,10 +470,11 @@ const u = class u extends b {
574
470
  case "Tab":
575
471
  this.open && this.closePopup(!1);
576
472
  break;
577
- case "ArrowLeft":
578
- !this.open && this.multiple && this._selectedValues.size > 0 && (e.preventDefault(), this._enterChipNav());
579
- break;
580
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
+ }
581
478
  e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && this._handleTypeAhead(e.key);
582
479
  }
583
480
  }
@@ -604,9 +501,9 @@ const u = class u extends b {
604
501
  );
605
502
  if (i)
606
503
  if (!this.open)
607
- this.multiple || (this._selectionInitialized = !0, this._applySelection([i.value]), this._dispatchChange());
504
+ this.multiple || (this._selectionInitialized = !0, this._applySelection([i.value], !0), this._dispatchChange());
608
505
  else {
609
- 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);
610
507
  r && this._activeDescendantCtrl.navigateToItem(r);
611
508
  }
612
509
  }
@@ -629,12 +526,12 @@ const u = class u extends b {
629
526
  * and return focus to the trigger.
630
527
  */
631
528
  _handleChipKeyDown(e, t) {
632
- const i = this._$chipButtons();
529
+ const i = this._$chipButtons(), s = g(f(this));
633
530
  switch (e.key) {
634
- case "ArrowLeft":
531
+ case s.prev:
635
532
  e.preventDefault(), this._chipNavIndex > 0 && (this._chipNavIndex--, i[this._chipNavIndex]?.focus());
636
533
  break;
637
- case "ArrowRight":
534
+ case s.next:
638
535
  e.preventDefault(), this._chipNavIndex < i.length - 1 ? (this._chipNavIndex++, i[this._chipNavIndex]?.focus()) : (this._chipNavIndex = -1, this._$trigger?.focus());
639
536
  break;
640
537
  case "Escape":
@@ -652,24 +549,145 @@ const u = class u extends b {
652
549
  _handleTriggerClick() {
653
550
  this.open ? this.closePopup() : this.openPopup();
654
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
+ }
655
652
  render() {
656
- const e = this.multiple && this._effectiveDisplay === "chips" && this._selectedValues.size > 0;
657
- 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`
658
675
  <div id="anchor" part="anchor">
659
676
  <div
660
677
  id="trigger"
661
678
  part="trigger"
662
679
  role="combobox"
663
680
  tabindex=${this.disabled ? "-1" : "0"}
664
- aria-haspopup="listbox"
681
+ aria-haspopup=${t ? "dialog" : "listbox"}
665
682
  aria-expanded=${this.open ? "true" : "false"}
666
- aria-controls="listbox"
683
+ aria-controls=${t ? "dialog" : "listbox"}
667
684
  aria-disabled=${this.disabled ? "true" : "false"}
668
- aria-label=${this.placeholder || _}
685
+ aria-required=${this.required ? "true" : "false"}
686
+ aria-label=${this.placeholder || p}
669
687
  @click=${this._handleTriggerClick}
670
688
  @keydown=${this._handleTriggerKeyDown}
671
689
  >
672
- ${e ? this._renderChips() : _}
690
+ ${e ? this._renderChips() : p}
673
691
 
674
692
  <span part="value-display">${this._displayLabel}</span>
675
693
 
@@ -691,43 +709,33 @@ const u = class u extends b {
691
709
  </span>
692
710
  </div>
693
711
  </div>
694
-
695
- <rc-listbox
696
- id="listbox"
697
- part="listbox"
698
- popover="manual"
699
- ?multiple=${this.multiple}
700
- checkmark
701
- @rc-listbox-change=${this._handleListboxChange}
702
- ></rc-listbox>
703
-
704
- <slot @slotchange=${this._handleSelectSlotChange}></slot>
705
712
  `;
706
713
  }
707
714
  /** Renders selected values as removable input chips inside the shared chip-group layout. */
708
- _renderChips() {
709
- return h`
710
- <rc-chip-group part="chips" kind="generic" layout="wrap" aria-label="Selected items">
711
- ${[...this._selectedValues].map((e) => {
712
- const t = this._labelFor(e);
713
- return h`
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`
714
722
  <rc-chip
715
723
  variant="input"
716
724
  removable
717
- data-value=${e}
718
- @rc-chip-remove=${(i) => {
719
- i.stopPropagation(), this._removeValue(e);
725
+ data-value=${s}
726
+ @rc-chip-remove=${(a) => {
727
+ a.stopPropagation(), t ? this._removePendingValue(s) : this._removeValue(s);
720
728
  }}
721
729
  >
722
730
  <button
723
731
  type="button"
724
732
  part="chip"
725
733
  tabindex="-1"
726
- aria-label=${`Remove ${t}`}
727
- @click=${(i) => i.stopPropagation()}
728
- @keydown=${(i) => this._handleChipKeyDown(i, e)}
734
+ aria-label=${i.remove(r)}
735
+ @click=${(a) => a.stopPropagation()}
736
+ @keydown=${(a) => this._handleChipKeyDown(a, s)}
729
737
  >
730
- <span part="chip-label">${t}</span>
738
+ <span part="chip-label">${r}</span>
731
739
  </button>
732
740
  <span slot="remove-icon" part="chip-remove" aria-hidden="true">&#215;</span>
733
741
  </rc-chip>
@@ -737,42 +745,64 @@ const u = class u extends b {
737
745
  `;
738
746
  }
739
747
  };
740
- u.styles = x;
741
- let o = u;
742
- a([
743
- 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 })
744
755
  ], o.prototype, "open");
745
- a([
746
- n({ type: Boolean, reflect: !0 })
756
+ l([
757
+ d({ type: Boolean, reflect: !0 })
747
758
  ], o.prototype, "multiple");
748
- a([
749
- n({ type: Boolean, reflect: !0 })
759
+ l([
760
+ d({ type: Boolean, reflect: !0 })
750
761
  ], o.prototype, "disabled");
751
- a([
752
- n()
762
+ l([
763
+ d({ type: Boolean, reflect: !0 })
764
+ ], o.prototype, "required");
765
+ l([
766
+ d()
753
767
  ], o.prototype, "placeholder");
754
- a([
755
- n()
768
+ l([
769
+ d()
756
770
  ], o.prototype, "display");
757
- a([
758
- p("#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")
759
779
  ], o.prototype, "_$anchor");
760
- a([
761
- p("#trigger")
780
+ l([
781
+ _("#trigger")
762
782
  ], o.prototype, "_$trigger");
763
- a([
764
- p("#listbox")
783
+ l([
784
+ _("rc-listbox")
765
785
  ], o.prototype, "_$listbox");
766
- a([
767
- d()
786
+ l([
787
+ _("#dialog-host")
788
+ ], o.prototype, "_$dialogHost");
789
+ l([
790
+ u()
768
791
  ], o.prototype, "_selectedValues");
769
- a([
770
- d()
792
+ l([
793
+ u()
794
+ ], o.prototype, "_pendingSelectedValues");
795
+ l([
796
+ u()
797
+ ], o.prototype, "_accessibleName");
798
+ l([
799
+ u()
771
800
  ], o.prototype, "_chipNavIndex");
772
- a([
773
- d()
801
+ l([
802
+ u()
774
803
  ], o.prototype, "_options");
775
804
  export {
805
+ z as D,
776
806
  o as R
777
807
  };
778
- //# sourceMappingURL=rc-select-D_H5Pb2t.js.map
808
+ //# sourceMappingURL=rc-select-DX-fKYeR.js.map