forge-select 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,3 @@
1
- type Handler = (...args: unknown[]) => void;
2
-
3
1
  interface Option {
4
2
  value: string;
5
3
  label: string;
@@ -10,6 +8,8 @@ interface Option {
10
8
  description?: string;
11
9
  /** Arbitrary payload for custom templates; ForgeSelect never reads it. */
12
10
  meta?: Record<string, unknown>;
11
+ /** Extra CSS class(es) applied to this option's rendered <li>. */
12
+ className?: string;
13
13
  /**
14
14
  * Nested options, making this a tree node. Purely additive: lists where
15
15
  * no option has `children` render and behave exactly as a flat list.
@@ -22,7 +22,13 @@ interface OptionGroup {
22
22
  }
23
23
  type DataItem = Option | OptionGroup;
24
24
  interface AjaxConfig {
25
- url: string | ((query: string, page: number) => string);
25
+ /** GET endpoint. Optional when `request` supplies a custom transport. */
26
+ url?: string | ((query: string, page: number) => string);
27
+ /**
28
+ * Custom transport for POST/authenticated/GraphQL requests. Takes precedence
29
+ * over `url`; the returned payload is passed through `transform`.
30
+ */
31
+ request?: (query: string, page: number, signal: AbortSignal) => Promise<unknown>;
26
32
  params?: (query: string, page: number) => Record<string, unknown>;
27
33
  /** Debounce in milliseconds. Default 250. */
28
34
  debounce?: number;
@@ -62,12 +68,55 @@ interface ForgeSelectOptions {
62
68
  * behavior and tag markup are unchanged when this is left off.
63
69
  */
64
70
  sortable?: boolean;
71
+ /**
72
+ * Multi-select only: close the dropdown immediately after each pick
73
+ * instead of staying open for further selections. Default false —
74
+ * existing multi-select behavior (stays open) is unchanged.
75
+ */
76
+ closeOnSelect?: boolean;
77
+ /**
78
+ * Multi-select only: caps the number of selected values. Once reached,
79
+ * further picks (including via allowCreate) are ignored until one is
80
+ * removed. Only gates interactive selection — setValue() is not clamped.
81
+ * Default undefined (no limit).
82
+ */
83
+ maxSelections?: number;
65
84
  theme?: string;
66
85
  disabled?: boolean;
86
+ /**
87
+ * Marks the field as required for native form validation. When mounted on
88
+ * a real <select>, an empty selection blocks form submission and shows
89
+ * inline invalid styling, mirroring native <select required> behavior.
90
+ * On a plain-element mount this only sets aria-required (no native form
91
+ * to hook into). Default false.
92
+ */
93
+ required?: boolean;
67
94
  data?: DataItem[];
68
95
  ajax?: AjaxConfig;
69
96
  templateResult?: TemplateFn;
70
97
  templateSelection?: TemplateFn;
98
+ /**
99
+ * Custom match predicate, replacing the built-in label/description
100
+ * substring match. Receives the trimmed (not lowercased) query.
101
+ */
102
+ filterOption?: (option: Option, query: string) => boolean;
103
+ /**
104
+ * Hides results (showing a hint row instead) until the trimmed search
105
+ * query reaches this length. Also delays ajax requests until the
106
+ * threshold is met. Default 0 (no gate).
107
+ */
108
+ minSearchLength?: number;
109
+ /**
110
+ * Hides the search field when a local list contains fewer options than
111
+ * this threshold. AJAX-backed lists always keep search visible. Default 0.
112
+ */
113
+ minResultsForSearch?: number;
114
+ /**
115
+ * Dynamically disables an option, in addition to its static `disabled`
116
+ * field. Re-evaluated on every render, so it can react to external state
117
+ * (e.g. a quota) without rebuilding `data` via setData().
118
+ */
119
+ isOptionDisabled?: (option: Option) => boolean;
71
120
  /**
72
121
  * false = never virtualize. true or unset = virtualize automatically
73
122
  * once the list exceeds ~100 rows.
@@ -77,13 +126,42 @@ interface ForgeSelectOptions {
77
126
  itemHeight?: number;
78
127
  language?: string | Record<string, string>;
79
128
  plugins?: ForgeSelectPlugin[];
129
+ /**
130
+ * Opens the dropdown when the control receives keyboard focus (e.g. via
131
+ * Tab). Default false — focusing alone still requires Enter/Space/ArrowDown
132
+ * to open, matching existing behavior.
133
+ */
134
+ openOnFocus?: boolean;
135
+ /**
136
+ * Optional portal container for the dropdown, useful inside overflow-hidden
137
+ * modals and drawers. Accepts an element or selector. Default: the root.
138
+ */
139
+ dropdownParent?: HTMLElement | string;
80
140
  }
81
141
  type ForgeSelectValue = string | string[] | null;
82
142
  interface SetValueOptions {
83
143
  /** Emit Forge Select's `change` event after updating. Default true. */
84
144
  emitChange?: boolean;
85
145
  }
86
- type ForgeSelectEvent = "change" | "open" | "close" | "search" | "clear" | "error";
146
+ interface MaximumSelectionEvent {
147
+ limit: number;
148
+ option: Option;
149
+ }
150
+ interface ForgeSelectEventMap {
151
+ change: ForgeSelectValue;
152
+ open: void;
153
+ close: void;
154
+ search: string;
155
+ clear: void;
156
+ error: Error;
157
+ select: Option;
158
+ unselect: Option;
159
+ create: Option;
160
+ reorder: string[];
161
+ maximum: MaximumSelectionEvent;
162
+ }
163
+ type ForgeSelectEvent = keyof ForgeSelectEventMap;
164
+ type ForgeSelectEventHandler<E extends ForgeSelectEvent> = ForgeSelectEventMap[E] extends void ? () => void : (payload: ForgeSelectEventMap[E]) => void;
87
165
 
88
166
  declare class ForgeSelect {
89
167
  /** The original element ForgeSelect was mounted on. */
@@ -105,6 +183,7 @@ declare class ForgeSelect {
105
183
  private searchInput;
106
184
  private list;
107
185
  private liveRegion;
186
+ private portalHost;
108
187
  private isOpen;
109
188
  private isDisabled;
110
189
  private destroyed;
@@ -128,20 +207,50 @@ declare class ForgeSelect {
128
207
  private nativeSelect;
129
208
  private nativeForm;
130
209
  private syncingNative;
210
+ /** Combines the static `disabled` field with the dynamic `isOptionDisabled` callback. */
211
+ private isOptionDisabled;
212
+ private pointerDownOnControl;
131
213
  private onDocumentMouseDown;
214
+ private onWindowResize;
215
+ private onAncestorScroll;
216
+ private onNativeInvalid;
132
217
  private onNativeChange;
133
218
  private applyNativeValues;
134
219
  private onFormReset;
135
220
  constructor(target: string | HTMLElement, options?: ForgeSelectOptions);
136
221
  open(): void;
137
222
  close(): void;
223
+ /**
224
+ * Flips the dropdown above the control when there isn't enough room below
225
+ * but there is above. Recomputed on open() and on window resize — the
226
+ * dropdown is positioned absolutely inside the relatively-positioned root,
227
+ * so it already tracks the control correctly on page scroll without
228
+ * needing a scroll listener.
229
+ */
230
+ private positionDropdown;
138
231
  destroy(): void;
139
232
  getValue(): ForgeSelectValue;
140
233
  setValue(value: ForgeSelectValue, options?: SetValueOptions): void;
234
+ /**
235
+ * Replaces the option list after construction. An open dropdown re-renders
236
+ * immediately; a selection whose value isn't in the new data stays
237
+ * selected (rendered via the already-selected option's own label/avatar,
238
+ * the same fallback used for values selected from a stale ajax page).
239
+ */
240
+ setData(data: DataItem[]): void;
241
+ /**
242
+ * Multi-select only: selects every currently non-disabled option, including
243
+ * nested tree descendants and options inside groups. If `maxSelections` is
244
+ * set, stops once the cap is reached rather than exceeding it. A no-op for
245
+ * single-select.
246
+ */
247
+ selectAll(): void;
248
+ /** Clears every selection. Equivalent to `setValue(null)`. */
249
+ clearAll(): void;
141
250
  enable(): void;
142
251
  disable(): void;
143
- on(event: ForgeSelectEvent, handler: Handler): void;
144
- off(event: ForgeSelectEvent, handler: Handler): void;
252
+ on<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
253
+ off<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
145
254
  /**
146
255
  * The original target (a hidden native <select> or a plain mount div) can
147
256
  * carry an accessible name via aria-label/aria-labelledby, or via a
@@ -151,9 +260,14 @@ declare class ForgeSelect {
151
260
  * interactive `this.control` ourselves.
152
261
  */
153
262
  private applyAccessibleName;
263
+ private shouldShowSearch;
264
+ private updateSearchVisibility;
154
265
  private buildDom;
155
266
  private bindEvents;
156
267
  private handleKeydown;
268
+ private canSelectOption;
269
+ private hasReachedMaximum;
270
+ private announceMaximum;
157
271
  private selectValue;
158
272
  private deselectValue;
159
273
  /**
@@ -164,9 +278,13 @@ declare class ForgeSelect {
164
278
  */
165
279
  private syncTreeAncestors;
166
280
  private clearSelection;
281
+ private allSelectableValues;
167
282
  private afterSelectionChange;
168
283
  private syncNativeSelect;
169
284
  private findOption;
285
+ private findOptionByLabel;
286
+ /** Selects an existing option matching `label` exactly, or creates and selects a new one. */
287
+ private createTag;
170
288
  private createFromQuery;
171
289
  private activateNavItem;
172
290
  private renderValue;
@@ -212,4 +330,4 @@ declare class ForgeSelect {
212
330
  private loadRemote;
213
331
  }
214
332
 
215
- export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectValue, type Option, type OptionGroup, type SetValueOptions, type TemplateFn, ForgeSelect as default };
333
+ export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectEventHandler, type ForgeSelectEventMap, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectValue, type MaximumSelectionEvent, type Option, type OptionGroup, type SetValueOptions, type TemplateFn, ForgeSelect as default };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- type Handler = (...args: unknown[]) => void;
2
-
3
1
  interface Option {
4
2
  value: string;
5
3
  label: string;
@@ -10,6 +8,8 @@ interface Option {
10
8
  description?: string;
11
9
  /** Arbitrary payload for custom templates; ForgeSelect never reads it. */
12
10
  meta?: Record<string, unknown>;
11
+ /** Extra CSS class(es) applied to this option's rendered <li>. */
12
+ className?: string;
13
13
  /**
14
14
  * Nested options, making this a tree node. Purely additive: lists where
15
15
  * no option has `children` render and behave exactly as a flat list.
@@ -22,7 +22,13 @@ interface OptionGroup {
22
22
  }
23
23
  type DataItem = Option | OptionGroup;
24
24
  interface AjaxConfig {
25
- url: string | ((query: string, page: number) => string);
25
+ /** GET endpoint. Optional when `request` supplies a custom transport. */
26
+ url?: string | ((query: string, page: number) => string);
27
+ /**
28
+ * Custom transport for POST/authenticated/GraphQL requests. Takes precedence
29
+ * over `url`; the returned payload is passed through `transform`.
30
+ */
31
+ request?: (query: string, page: number, signal: AbortSignal) => Promise<unknown>;
26
32
  params?: (query: string, page: number) => Record<string, unknown>;
27
33
  /** Debounce in milliseconds. Default 250. */
28
34
  debounce?: number;
@@ -62,12 +68,55 @@ interface ForgeSelectOptions {
62
68
  * behavior and tag markup are unchanged when this is left off.
63
69
  */
64
70
  sortable?: boolean;
71
+ /**
72
+ * Multi-select only: close the dropdown immediately after each pick
73
+ * instead of staying open for further selections. Default false —
74
+ * existing multi-select behavior (stays open) is unchanged.
75
+ */
76
+ closeOnSelect?: boolean;
77
+ /**
78
+ * Multi-select only: caps the number of selected values. Once reached,
79
+ * further picks (including via allowCreate) are ignored until one is
80
+ * removed. Only gates interactive selection — setValue() is not clamped.
81
+ * Default undefined (no limit).
82
+ */
83
+ maxSelections?: number;
65
84
  theme?: string;
66
85
  disabled?: boolean;
86
+ /**
87
+ * Marks the field as required for native form validation. When mounted on
88
+ * a real <select>, an empty selection blocks form submission and shows
89
+ * inline invalid styling, mirroring native <select required> behavior.
90
+ * On a plain-element mount this only sets aria-required (no native form
91
+ * to hook into). Default false.
92
+ */
93
+ required?: boolean;
67
94
  data?: DataItem[];
68
95
  ajax?: AjaxConfig;
69
96
  templateResult?: TemplateFn;
70
97
  templateSelection?: TemplateFn;
98
+ /**
99
+ * Custom match predicate, replacing the built-in label/description
100
+ * substring match. Receives the trimmed (not lowercased) query.
101
+ */
102
+ filterOption?: (option: Option, query: string) => boolean;
103
+ /**
104
+ * Hides results (showing a hint row instead) until the trimmed search
105
+ * query reaches this length. Also delays ajax requests until the
106
+ * threshold is met. Default 0 (no gate).
107
+ */
108
+ minSearchLength?: number;
109
+ /**
110
+ * Hides the search field when a local list contains fewer options than
111
+ * this threshold. AJAX-backed lists always keep search visible. Default 0.
112
+ */
113
+ minResultsForSearch?: number;
114
+ /**
115
+ * Dynamically disables an option, in addition to its static `disabled`
116
+ * field. Re-evaluated on every render, so it can react to external state
117
+ * (e.g. a quota) without rebuilding `data` via setData().
118
+ */
119
+ isOptionDisabled?: (option: Option) => boolean;
71
120
  /**
72
121
  * false = never virtualize. true or unset = virtualize automatically
73
122
  * once the list exceeds ~100 rows.
@@ -77,13 +126,42 @@ interface ForgeSelectOptions {
77
126
  itemHeight?: number;
78
127
  language?: string | Record<string, string>;
79
128
  plugins?: ForgeSelectPlugin[];
129
+ /**
130
+ * Opens the dropdown when the control receives keyboard focus (e.g. via
131
+ * Tab). Default false — focusing alone still requires Enter/Space/ArrowDown
132
+ * to open, matching existing behavior.
133
+ */
134
+ openOnFocus?: boolean;
135
+ /**
136
+ * Optional portal container for the dropdown, useful inside overflow-hidden
137
+ * modals and drawers. Accepts an element or selector. Default: the root.
138
+ */
139
+ dropdownParent?: HTMLElement | string;
80
140
  }
81
141
  type ForgeSelectValue = string | string[] | null;
82
142
  interface SetValueOptions {
83
143
  /** Emit Forge Select's `change` event after updating. Default true. */
84
144
  emitChange?: boolean;
85
145
  }
86
- type ForgeSelectEvent = "change" | "open" | "close" | "search" | "clear" | "error";
146
+ interface MaximumSelectionEvent {
147
+ limit: number;
148
+ option: Option;
149
+ }
150
+ interface ForgeSelectEventMap {
151
+ change: ForgeSelectValue;
152
+ open: void;
153
+ close: void;
154
+ search: string;
155
+ clear: void;
156
+ error: Error;
157
+ select: Option;
158
+ unselect: Option;
159
+ create: Option;
160
+ reorder: string[];
161
+ maximum: MaximumSelectionEvent;
162
+ }
163
+ type ForgeSelectEvent = keyof ForgeSelectEventMap;
164
+ type ForgeSelectEventHandler<E extends ForgeSelectEvent> = ForgeSelectEventMap[E] extends void ? () => void : (payload: ForgeSelectEventMap[E]) => void;
87
165
 
88
166
  declare class ForgeSelect {
89
167
  /** The original element ForgeSelect was mounted on. */
@@ -105,6 +183,7 @@ declare class ForgeSelect {
105
183
  private searchInput;
106
184
  private list;
107
185
  private liveRegion;
186
+ private portalHost;
108
187
  private isOpen;
109
188
  private isDisabled;
110
189
  private destroyed;
@@ -128,20 +207,50 @@ declare class ForgeSelect {
128
207
  private nativeSelect;
129
208
  private nativeForm;
130
209
  private syncingNative;
210
+ /** Combines the static `disabled` field with the dynamic `isOptionDisabled` callback. */
211
+ private isOptionDisabled;
212
+ private pointerDownOnControl;
131
213
  private onDocumentMouseDown;
214
+ private onWindowResize;
215
+ private onAncestorScroll;
216
+ private onNativeInvalid;
132
217
  private onNativeChange;
133
218
  private applyNativeValues;
134
219
  private onFormReset;
135
220
  constructor(target: string | HTMLElement, options?: ForgeSelectOptions);
136
221
  open(): void;
137
222
  close(): void;
223
+ /**
224
+ * Flips the dropdown above the control when there isn't enough room below
225
+ * but there is above. Recomputed on open() and on window resize — the
226
+ * dropdown is positioned absolutely inside the relatively-positioned root,
227
+ * so it already tracks the control correctly on page scroll without
228
+ * needing a scroll listener.
229
+ */
230
+ private positionDropdown;
138
231
  destroy(): void;
139
232
  getValue(): ForgeSelectValue;
140
233
  setValue(value: ForgeSelectValue, options?: SetValueOptions): void;
234
+ /**
235
+ * Replaces the option list after construction. An open dropdown re-renders
236
+ * immediately; a selection whose value isn't in the new data stays
237
+ * selected (rendered via the already-selected option's own label/avatar,
238
+ * the same fallback used for values selected from a stale ajax page).
239
+ */
240
+ setData(data: DataItem[]): void;
241
+ /**
242
+ * Multi-select only: selects every currently non-disabled option, including
243
+ * nested tree descendants and options inside groups. If `maxSelections` is
244
+ * set, stops once the cap is reached rather than exceeding it. A no-op for
245
+ * single-select.
246
+ */
247
+ selectAll(): void;
248
+ /** Clears every selection. Equivalent to `setValue(null)`. */
249
+ clearAll(): void;
141
250
  enable(): void;
142
251
  disable(): void;
143
- on(event: ForgeSelectEvent, handler: Handler): void;
144
- off(event: ForgeSelectEvent, handler: Handler): void;
252
+ on<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
253
+ off<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
145
254
  /**
146
255
  * The original target (a hidden native <select> or a plain mount div) can
147
256
  * carry an accessible name via aria-label/aria-labelledby, or via a
@@ -151,9 +260,14 @@ declare class ForgeSelect {
151
260
  * interactive `this.control` ourselves.
152
261
  */
153
262
  private applyAccessibleName;
263
+ private shouldShowSearch;
264
+ private updateSearchVisibility;
154
265
  private buildDom;
155
266
  private bindEvents;
156
267
  private handleKeydown;
268
+ private canSelectOption;
269
+ private hasReachedMaximum;
270
+ private announceMaximum;
157
271
  private selectValue;
158
272
  private deselectValue;
159
273
  /**
@@ -164,9 +278,13 @@ declare class ForgeSelect {
164
278
  */
165
279
  private syncTreeAncestors;
166
280
  private clearSelection;
281
+ private allSelectableValues;
167
282
  private afterSelectionChange;
168
283
  private syncNativeSelect;
169
284
  private findOption;
285
+ private findOptionByLabel;
286
+ /** Selects an existing option matching `label` exactly, or creates and selects a new one. */
287
+ private createTag;
170
288
  private createFromQuery;
171
289
  private activateNavItem;
172
290
  private renderValue;
@@ -212,4 +330,4 @@ declare class ForgeSelect {
212
330
  private loadRemote;
213
331
  }
214
332
 
215
- export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectValue, type Option, type OptionGroup, type SetValueOptions, type TemplateFn, ForgeSelect as default };
333
+ export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectEventHandler, type ForgeSelectEventMap, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectValue, type MaximumSelectionEvent, type Option, type OptionGroup, type SetValueOptions, type TemplateFn, ForgeSelect as default };
@@ -1,2 +1,2 @@
1
- "use strict";var ForgeSelectBundle=(()=>{var I=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var q=(r,e)=>{for(var t in e)I(r,t,{get:e[t],enumerable:!0})},P=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!j.call(r,s)&&s!==t&&I(r,s,{get:()=>e[s],enumerable:!(i=V(e,s))||i.enumerable});return r};var B=r=>P(I({},"__esModule",{value:!0}),r);var X={};q(X,{ForgeSelect:()=>p,default:()=>p});var v=class{constructor(){this.handlers=new Map}on(e,t){let i=this.handlers.get(e);i||(i=new Set,this.handlers.set(e,i)),i.add(t)}off(e,t){this.handlers.get(e)?.delete(t)}emit(e,...t){let i=this.handlers.get(e);if(i)for(let s of[...i])s(...t)}clear(){this.handlers.clear()}};var L={en:{noResults:"No results found",loading:"Loading\u2026",loadingMore:"Loading more\u2026",errorLoading:"Could not load options",createOption:'Create "{query}"',clearSelection:"Clear selection",removeItem:"Remove {label}",search:"Search",reorderHint:"{label}. Press Alt+Left or Alt+Right to reorder."},vi:{noResults:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",loading:"\u0110ang t\u1EA3i\u2026",loadingMore:"\u0110ang t\u1EA3i th\xEAm\u2026",errorLoading:"Kh\xF4ng th\u1EC3 t\u1EA3i t\xF9y ch\u1ECDn",createOption:'T\u1EA1o "{query}"',clearSelection:"X\xF3a l\u1EF1a ch\u1ECDn",removeItem:"X\xF3a {label}",search:"T\xECm ki\u1EBFm",reorderHint:"{label}. Nh\u1EA5n Alt+Tr\xE1i ho\u1EB7c Alt+Ph\u1EA3i \u0111\u1EC3 s\u1EAFp x\u1EBFp l\u1EA1i."}};function C(r){return typeof r=="string"?L[r]??L.en:{...L.en,...r}}function b(r,e){return r.replace(/\{(\w+)\}/g,(t,i)=>e[i]??t)}function T(r){let e=[];for(let t of Array.from(r.children))t instanceof HTMLOptGroupElement?e.push({label:t.label,options:Array.from(t.querySelectorAll("option")).map(O)}):t instanceof HTMLOptionElement&&e.push(O(t));return e}function O(r){let e=r.parentElement instanceof HTMLOptGroupElement&&r.parentElement.disabled;return{value:r.value,label:r.textContent?.trim()??r.value,disabled:r.disabled||e||void 0}}function x(r,e,t,i="row"){if(t){let s=t(e);typeof s=="string"?r.innerHTML=s:r.append(s);return}if(!e.avatar&&!e.description){r.textContent=e.label;return}if(e.avatar){let s=document.createElement("img");s.className=i==="row"?"forge-select__option-avatar":"forge-select__inline-avatar",s.src=e.avatar,s.alt="",s.setAttribute("loading","lazy"),s.setAttribute("decoding","async"),r.append(s)}if(i==="row"&&e.description){let s=document.createElement("span");s.className="forge-select__option-body";let n=document.createElement("span");n.className="forge-select__option-label",n.textContent=e.label;let a=document.createElement("span");a.className="forge-select__option-desc",a.textContent=e.description,s.append(n,a),r.append(s)}else{let s=document.createElement("span");s.className="forge-select__option-label",s.textContent=e.label,r.append(s)}}function _(r,e,t){if(typeof r.url=="function")return r.url(e,t);if(!r.params)return r.url;let i=new URLSearchParams;for(let[n,a]of Object.entries(r.params(e,t)))i.set(n,String(a));let s=r.url.includes("?")?"&":"?";return`${r.url}${s}${i.toString()}`}function R(r,e){let t=r.transform?r.transform(e):e;if(Array.isArray(t))return{options:t,hasMore:!1};if(!t||!Array.isArray(t.options))throw new Error("ForgeSelect: ajax.transform must return an array of options, or an object shaped like { options: Option[], hasMore?: boolean }.");return{options:t.options,hasMore:r.pagination?!!t.hasMore:!1}}function u(r){return r.options!==void 0}function y(r){if(!r.children)return[];let e=[];for(let t of r.children)t.disabled||e.push(t.value),e.push(...y(t));return e}function E(r,e){if(!r.children?.length)return e.includes(r.value)?"all":"none";let t=r.children.filter(i=>!i.disabled).map(i=>E(i,e));return t.length===0?"none":t.every(i=>i==="all")?"all":t.every(i=>i==="none")?"none":"some"}function k(r,e){let t=i=>{for(let s of i){if(s.value===e)return s;let n=s.children?t(s.children):void 0;if(n)return n}};for(let i of r){let s=t(u(i)?i.options:[i]);if(s)return s}}function M(r,e){let t=i=>{if(!i.children?.length)return;for(let a of i.children)t(a);let s=E(i,e),n=e.indexOf(i.value);s==="all"&&n===-1?e.push(i.value):s!=="all"&&n!==-1&&e.splice(n,1)};for(let i of r)(u(i)?i.options:[i]).forEach(t)}function N(r){let e=new Set,t=i=>{e.add(i.value),i.children?.forEach(t)};for(let i of r)(u(i)?i.options:[i]).forEach(t);return e}function H(r,e){return r.length===e.length&&r.every((t,i)=>t===e[i])}var $=36,D=5,G=100,K=2e3,U=0,p=class{constructor(e,t={}){this.selected=[];this.selectedOptions=new Map;this.suppressNextTagClick=!1;this.emitter=new v;this.uid=`forge-select-${++U}`;this.searchInput=null;this.isOpen=!1;this.isDisabled=!1;this.destroyed=!1;this.query="";this.rows=[];this.navItems=[];this.highlightedIndex=-1;this.rowContentCache=new Map;this.expandedValues=new Set;this.loading=!1;this.loadingMore=!1;this.page=0;this.hasMore=!0;this.ajaxTimer=null;this.ajaxRequestId=0;this.ajaxController=null;this.remoteLoaded=!1;this.loadError=null;this.originalDisplay="";this.originalDisabled=!1;this.nativeSelect=null;this.nativeForm=null;this.syncingNative=!1;this.onDocumentMouseDown=e=>{this.root.contains(e.target)||this.close()};this.onNativeChange=()=>{if(!this.nativeSelect||this.destroyed||this.syncingNative)return;let e=Array.from(this.nativeSelect.selectedOptions,t=>t.value);this.applyNativeValues(e)};this.onFormReset=()=>{if(!this.nativeSelect||this.destroyed)return;let e=Array.from(this.nativeSelect.options).filter(t=>t.defaultSelected).map(t=>t.value);this.applyNativeValues(e)};let i=typeof e=="string"?document.querySelector(e):e;if(!i)throw new Error(`ForgeSelect: target element not found: ${String(e)}`);this.el=i;let s=i instanceof HTMLSelectElement?i:null;if(this.nativeSelect=s,this.nativeForm=s?.form??null,this.originalDisplay=i.style.display,this.originalDisabled=s?.disabled??!1,this.opts={placeholder:t.placeholder??"",searchable:t.searchable??!0,multiple:t.multiple??s?.multiple??!1,clearable:t.clearable??!1,allowCreate:t.allowCreate??!1,sortable:t.sortable??!1,theme:t.theme??"default",disabled:t.disabled??s?.disabled??!1,data:t.data,ajax:t.ajax,templateResult:t.templateResult,templateSelection:t.templateSelection,virtualScroll:t.virtualScroll,itemHeight:t.itemHeight??$,language:t.language??"en",plugins:t.plugins??[]},this.strings=C(this.opts.language),this.plugins=this.opts.plugins,this.data=this.opts.data??(s?T(s):[]),s&&!this.opts.data){let n=Array.from(s.options),a=s.multiple||s.selectedIndex>0||n.some(o=>o.defaultSelected);for(let o of n)a&&o.selected&&this.selectValue(o.value,!1)}this.buildDom(),this.renderValue(),this.opts.disabled&&this.disable(),s?.addEventListener("change",this.onNativeChange),this.nativeForm?.addEventListener("reset",this.onFormReset);for(let n of this.plugins)n.onInit?.(this)}applyNativeValues(e){this.selected=[];for(let t of this.opts.multiple?e:e.slice(0,1))this.selectValue(t,!1);this.renderValue(),this.isOpen&&this.renderList(),this.emitter.emit("change",this.getValue())}open(){if(!(this.isOpen||this.isDisabled||this.destroyed)){this.isOpen=!0,this.dropdown.hidden=!1,this.root.classList.add("forge-select--open"),this.control.setAttribute("aria-expanded","true"),document.addEventListener("mousedown",this.onDocumentMouseDown),this.opts.ajax&&!this.remoteLoaded&&this.scheduleRemoteLoad(this.query,0),this.renderList(),this.searchInput&&this.searchInput.focus(),this.emitter.emit("open");for(let e of this.plugins)e.onOpen?.(this)}}close(){if(this.isOpen){this.isOpen=!1,this.dropdown.hidden=!0,this.root.classList.remove("forge-select--open"),this.control.setAttribute("aria-expanded","false"),document.removeEventListener("mousedown",this.onDocumentMouseDown),this.highlightedIndex=-1,this.searchInput&&(this.searchInput.value="",this.query=""),this.emitter.emit("close");for(let e of this.plugins)e.onClose?.(this)}}destroy(){if(!this.destroyed){this.close();for(let e of this.plugins)e.onDestroy?.(this);this.destroyed=!0,this.ajaxTimer&&clearTimeout(this.ajaxTimer),this.ajaxController?.abort(),this.nativeSelect?.removeEventListener("change",this.onNativeChange),this.nativeForm?.removeEventListener("reset",this.onFormReset),this.rowContentCache.clear(),this.root.remove(),this.el.style.display=this.originalDisplay,this.nativeSelect&&(this.nativeSelect.disabled=this.originalDisabled),this.emitter.clear()}}getValue(){return this.opts.multiple?[...this.selected]:this.selected[0]??null}setValue(e,t={}){let i=e==null?[]:Array.isArray(e)?e:[e],s=this.opts.multiple?i:i.slice(0,1);if(!H(s,this.selected)){this.selected=[];for(let n of s)this.selectValue(n,!1);this.afterSelectionChange(t.emitChange??!0)}}enable(){this.isDisabled=!1,this.root.classList.remove("forge-select--disabled"),this.control.tabIndex=0,this.control.setAttribute("aria-disabled","false"),this.nativeSelect&&(this.nativeSelect.disabled=!1)}disable(){this.close(),this.isDisabled=!0,this.root.classList.add("forge-select--disabled"),this.control.tabIndex=-1,this.control.setAttribute("aria-disabled","true"),this.nativeSelect&&(this.nativeSelect.disabled=!0)}on(e,t){this.emitter.on(e,t)}off(e,t){this.emitter.off(e,t)}applyAccessibleName(){let e=this.el.getAttribute("aria-labelledby"),t=this.el.getAttribute("aria-label");if(e)this.control.setAttribute("aria-labelledby",e);else if(t)this.control.setAttribute("aria-label",t);else if(this.el.id){let i=Array.from(document.getElementsByTagName("label")).find(s=>s.htmlFor===this.el.id);i&&(i.id||(i.id=`${this.uid}-label`),this.control.setAttribute("aria-labelledby",i.id))}}buildDom(){this.root=document.createElement("div"),this.root.className="forge-select",this.root.dataset.theme=this.opts.theme,this.root.style.setProperty("--fs-item-height",`${this.opts.itemHeight}px`),this.opts.sortable&&this.opts.multiple&&this.root.classList.add("forge-select--sortable"),this.control=document.createElement("div"),this.control.className="forge-select__control",this.control.setAttribute("role","combobox"),this.control.setAttribute("aria-haspopup","listbox"),this.control.setAttribute("aria-expanded","false"),this.control.setAttribute("aria-controls",`${this.uid}-list`),this.control.tabIndex=0,this.applyAccessibleName(),this.valueEl=document.createElement("div"),this.valueEl.className="forge-select__value",this.clearBtn=document.createElement("button"),this.clearBtn.type="button",this.clearBtn.className="forge-select__clear",this.clearBtn.setAttribute("aria-label",this.strings.clearSelection),this.clearBtn.textContent="\xD7",this.clearBtn.hidden=!0;let e=document.createElement("span");e.className="forge-select__arrow",e.setAttribute("aria-hidden","true"),this.control.append(this.valueEl,this.clearBtn,e),this.dropdown=document.createElement("div"),this.dropdown.className="forge-select__dropdown",this.dropdown.hidden=!0,this.opts.searchable&&(this.searchInput=document.createElement("input"),this.searchInput.type="search",this.searchInput.className="forge-select__search",this.searchInput.setAttribute("aria-label",this.strings.search),this.searchInput.setAttribute("aria-autocomplete","list"),this.searchInput.setAttribute("aria-controls",`${this.uid}-list`),this.dropdown.append(this.searchInput)),this.list=document.createElement("ul"),this.list.className="forge-select__list",this.list.id=`${this.uid}-list`,this.list.setAttribute("role","listbox"),this.opts.multiple&&this.list.setAttribute("aria-multiselectable","true"),this.dropdown.append(this.list),this.liveRegion=document.createElement("div"),this.liveRegion.className="forge-select__sr-only",this.liveRegion.setAttribute("role","status"),this.liveRegion.setAttribute("aria-live","polite"),this.root.append(this.control,this.dropdown,this.liveRegion),this.el.style.display="none",this.el.insertAdjacentElement("afterend",this.root),this.bindEvents()}bindEvents(){this.control.addEventListener("click",e=>{if(e.target!==this.clearBtn){if(this.suppressNextTagClick){this.suppressNextTagClick=!1;return}this.isDisabled||(this.isOpen?this.close():this.open())}}),this.control.addEventListener("keydown",e=>this.handleKeydown(e)),this.clearBtn.addEventListener("click",e=>{e.stopPropagation(),this.clearSelection()}),this.searchInput&&(this.searchInput.addEventListener("input",()=>{this.query=this.searchInput.value,this.highlightedIndex=-1,this.list.scrollTop=0,this.emitter.emit("search",this.query),this.opts.ajax?this.scheduleRemoteLoad(this.query,this.opts.ajax.debounce??250):this.renderList()}),this.searchInput.addEventListener("keydown",e=>this.handleKeydown(e))),this.list.addEventListener("click",e=>{let t=e.target,i=t.closest("[data-twisty]");if(i){let a=i.dataset.twisty;this.expandedValues.has(a)?this.expandedValues.delete(a):this.expandedValues.add(a),this.renderList();return}let s=t.closest("li[data-nav-index]");if(!s)return;let n=Number(s.dataset.navIndex);this.activateNavItem(n)}),this.list.addEventListener("scroll",()=>{this.usesVirtualScroll()&&this.renderRows(),this.maybeLoadNextPage()})}handleKeydown(e){if(!this.isDisabled)switch(e.key){case"Enter":e.preventDefault(),this.isOpen?this.highlightedIndex>=0&&this.activateNavItem(this.highlightedIndex):this.open();break;case" ":e.target===this.control&&(e.preventDefault(),this.isOpen||this.open());break;case"ArrowDown":e.preventDefault(),this.isOpen?this.moveHighlight(1):this.open();break;case"ArrowUp":e.preventDefault(),this.isOpen&&this.moveHighlight(-1);break;case"Escape":this.isOpen&&(e.preventDefault(),this.close(),this.control.focus());break;case"ArrowRight":this.isOpen&&this.navigateTree("right")&&e.preventDefault();break;case"ArrowLeft":this.isOpen&&this.navigateTree("left")&&e.preventDefault();break;case"Tab":this.close();break}}selectValue(e,t){if(this.selected.includes(e))return;let i=this.findOption(e)??this.selectedOptions.get(e)??{value:e,label:e};if(this.selectedOptions.set(e,i),this.opts.multiple){this.selected.push(e);for(let s of y(i))this.selected.includes(s)||this.selected.push(s);this.syncTreeAncestors()}else this.selected=[e];t&&this.afterSelectionChange()}deselectValue(e,t){let i=this.selected.indexOf(e);if(i!==-1){if(this.selected.splice(i,1),this.opts.multiple){let s=this.findOption(e)??this.selectedOptions.get(e);if(s)for(let n of y(s)){let a=this.selected.indexOf(n);a!==-1&&this.selected.splice(a,1)}this.syncTreeAncestors()}t&&this.afterSelectionChange()}}syncTreeAncestors(){M(this.data,this.selected)}clearSelection(){this.selected.length!==0&&(this.selected=[],this.emitter.emit("clear"),this.afterSelectionChange())}afterSelectionChange(e=!0){this.renderValue(),this.syncNativeSelect(e),this.isOpen&&this.renderList(),e&&this.emitter.emit("change",this.getValue())}syncNativeSelect(e=!0){if(!(this.el instanceof HTMLSelectElement))return;let t=new Set;for(let i of Array.from(this.el.options))t.add(i.value),i.selected=this.selected.includes(i.value);for(let i of this.selected){if(t.has(i))continue;let s=document.createElement("option");s.value=i,s.textContent=this.selectedOptions.get(i)?.label??i,s.selected=!0,this.el.append(s)}if(this.opts.sortable&&this.opts.multiple)for(let i of this.selected){let s=Array.from(this.el.options).find(n=>n.value===i);s&&this.el.append(s)}if(e){this.syncingNative=!0;try{this.el.dispatchEvent(new Event("change",{bubbles:!0}))}finally{this.syncingNative=!1}}}findOption(e){return k(this.data,e)}createFromQuery(){let e=this.query.trim();if(!e)return;let t={value:e,label:e};this.data.push(t),this.searchInput&&(this.searchInput.value="",this.query=""),this.selectValue(t.value,!0),this.opts.multiple||this.close()}activateNavItem(e){let t=this.navItems[e];if(!t)return;if(t.kind==="create"){this.createFromQuery();return}let{value:i}=t.option;this.opts.multiple?this.selected.includes(i)?this.deselectValue(i,!0):this.selectValue(i,!0):(this.selectValue(i,!0),this.close(),this.control.focus())}renderValue(){this.valueEl.textContent="";let e=this.selected.length>0;if(this.clearBtn.hidden=!(this.opts.clearable&&e),!e){let t=document.createElement("span");t.className="forge-select__placeholder",t.textContent=this.opts.placeholder,this.valueEl.append(t);return}if(this.opts.multiple)for(let t of this.selected){let i=this.selectedOptions.get(t)??{value:t,label:t},s=document.createElement("span");s.className="forge-select__tag";let n=document.createElement("span");n.className="forge-select__tag-label",x(n,i,this.opts.templateSelection,"inline");let a=document.createElement("button");a.type="button",a.className="forge-select__tag-remove",a.setAttribute("aria-label",b(this.strings.removeItem,{label:i.label})),a.textContent="\xD7",a.addEventListener("click",o=>{o.stopPropagation(),this.isDisabled||this.deselectValue(t,!0)}),s.append(n,a),this.opts.sortable&&(s.dataset.value=t,s.tabIndex=0,s.setAttribute("aria-roledescription","draggable item"),s.setAttribute("aria-label",b(this.strings.reorderHint,{label:i.label})),s.addEventListener("keydown",o=>this.handleTagKeydown(o,t)),this.bindTagDrag(s,t)),this.valueEl.append(s)}else{let t=this.selectedOptions.get(this.selected[0])??{value:this.selected[0],label:this.selected[0]},i=document.createElement("span");i.className="forge-select__single-value",x(i,t,this.opts.templateSelection,"inline"),this.valueEl.append(i)}}bindTagDrag(e,t){let s=0,n=!1,a=[],o=h=>{if(!n){if(Math.abs(h.clientX-s)<4)return;n=!0,a=[...this.selected],typeof this.valueEl.setPointerCapture=="function"&&this.valueEl.setPointerCapture(h.pointerId),e.classList.add("forge-select__tag--dragging")}h.preventDefault();let f=a.indexOf(t),d=Array.from(this.valueEl.querySelectorAll(".forge-select__tag"));for(let c of d){if(c===e)continue;let m=c.dataset.value;if(!m)continue;let g=a.indexOf(m);if(g===-1)continue;let S=c.getBoundingClientRect(),w=S.left+S.width/2,A=f<g;if(A?h.clientX>w:h.clientX<w){a.splice(f,1),a.splice(g,0,t),A?this.valueEl.insertBefore(e,c.nextSibling):this.valueEl.insertBefore(e,c);break}}},l=h=>{this.valueEl.removeEventListener("pointermove",o),this.valueEl.removeEventListener("pointerup",l),this.valueEl.removeEventListener("pointercancel",l),n&&(typeof this.valueEl.releasePointerCapture=="function"&&this.valueEl.releasePointerCapture(h.pointerId),e.classList.remove("forge-select__tag--dragging"),this.selected=a,this.suppressNextTagClick=!0,this.afterSelectionChange())};e.addEventListener("pointerdown",h=>{this.isDisabled||h.button!==0||h.target.closest(".forge-select__tag-remove")||(s=h.clientX,n=!1,this.valueEl.addEventListener("pointermove",o),this.valueEl.addEventListener("pointerup",l),this.valueEl.addEventListener("pointercancel",l))})}handleTagKeydown(e,t){if(!e.altKey||e.key!=="ArrowLeft"&&e.key!=="ArrowRight")return;let i=this.selected.indexOf(t),s=e.key==="ArrowLeft"?i-1:i+1;if(i===-1||s<0||s>=this.selected.length)return;e.preventDefault(),e.stopPropagation();let n=[...this.selected];[n[i],n[s]]=[n[s],n[i]],this.selected=n,this.afterSelectionChange(),this.focusTagByValue(t)}focusTagByValue(e){for(let t of Array.from(this.valueEl.querySelectorAll(".forge-select__tag")))if(t.dataset.value===e){t.focus();return}}buildRows(){this.rows=[],this.navItems=[];let e=this.query.trim().toLowerCase(),t=n=>e===""||n.label.toLowerCase().includes(e)||(n.description?.toLowerCase().includes(e)??!1),i=n=>e===""||t(n)||(n.children??[]).some(i),s=(n,a,o)=>{let l=-1;n.disabled||(l=this.navItems.length,this.navItems.push({kind:"option",option:n,parentValue:o}));let h=!!n.children&&n.children.length>0;if(this.rows.push({kind:"option",option:n,navIndex:l,depth:a,hasChildren:h}),h&&(e!==""||this.expandedValues.has(n.value)))for(let d of n.children)i(d)&&s(d,a+1,n.value)};if(this.loading){this.rows.push({kind:"loading"});return}if(this.loadError){this.rows.push({kind:"error"});return}for(let n of this.data)if(u(n)){let a=n.options.filter(i);if(a.length===0)continue;this.rows.push({kind:"group",label:n.label}),a.forEach(o=>s(o,0))}else i(n)&&s(n,0);if(this.opts.allowCreate&&e!==""&&!this.hasExactMatch(e)){let n=this.navItems.length;this.navItems.push({kind:"create"}),this.rows.push({kind:"create",navIndex:n})}this.rows.length===0?this.rows.push({kind:"empty"}):this.loadingMore&&this.rows.push({kind:"loading-more"})}hasExactMatch(e){let t=i=>i.label.toLowerCase()===e||(i.children??[]).some(t);for(let i of this.data)if((u(i)?i.options:[i]).some(t))return!0;return!1}usesVirtualScroll(){return this.opts.virtualScroll!==!1&&this.rows.length>G}renderList(){this.buildRows(),this.renderRows(),this.announceStatus()}announceStatus(){let e=this.rows[0],t=e?.kind==="loading"?this.strings.loading:e?.kind==="error"?this.strings.errorLoading:e?.kind==="empty"?this.strings.noResults:"";this.liveRegion.textContent!==t&&(this.liveRegion.textContent=t)}renderRows(){let e=this.list.scrollTop,t=this.list.clientHeight,i=this.usesVirtualScroll();this.list.textContent="";let s=this.opts.itemHeight,n=0,a=this.rows.length;if(i){let o=t||s*8;n=Math.max(0,Math.floor(e/s)-D),a=Math.min(this.rows.length,n+Math.ceil(o/s)+D*2);let l=document.createElement("li");l.className="forge-select__spacer",l.setAttribute("aria-hidden","true"),l.style.height=`${n*s}px`,this.list.append(l)}for(let o=n;o<a;o++)this.list.append(this.renderRow(this.rows[o]));if(i){let o=document.createElement("li");o.className="forge-select__spacer",o.setAttribute("aria-hidden","true"),o.style.height=`${(this.rows.length-a)*s}px`,this.list.append(o),this.list.scrollTop!==e&&(this.list.scrollTop=e)}this.updateActiveDescendant()}renderRow(e){let t=document.createElement("li");switch(e.kind){case"group":t.className="forge-select__group-label",t.setAttribute("role","presentation"),t.textContent=e.label;break;case"empty":t.className="forge-select__empty",t.setAttribute("role","option"),t.setAttribute("aria-disabled","true"),t.setAttribute("aria-selected","false"),t.textContent=this.strings.noResults;break;case"error":t.className="forge-select__error",t.setAttribute("role","option"),t.setAttribute("aria-disabled","true"),t.setAttribute("aria-selected","false"),t.textContent=this.strings.errorLoading;break;case"loading":t.className="forge-select__loading",t.setAttribute("role","option"),t.setAttribute("aria-disabled","true"),t.setAttribute("aria-selected","false"),t.textContent=this.strings.loading;break;case"loading-more":t.className="forge-select__loading-more",t.setAttribute("aria-hidden","true"),t.textContent=this.strings.loadingMore;break;case"create":t.className="forge-select__option forge-select__option--create",t.setAttribute("role","option"),t.id=`${this.uid}-nav-${e.navIndex}`,t.dataset.navIndex=String(e.navIndex),t.textContent=b(this.strings.createOption,{query:this.query.trim()}),e.navIndex===this.highlightedIndex&&t.classList.add("forge-select__option--highlighted");break;case"option":{t.className="forge-select__option",t.setAttribute("role","option");let i=this.selected.includes(e.option.value);if(t.setAttribute("aria-selected",String(i)),i&&t.classList.add("forge-select__option--selected"),this.opts.multiple&&e.hasChildren&&E(e.option,this.selected)==="some"&&t.classList.add("forge-select__option--indeterminate"),e.depth>0&&(t.style.paddingLeft=`calc(12px + ${e.depth} * var(--fs-tree-indent, 18px))`),e.option.disabled?(t.classList.add("forge-select__option--disabled"),t.setAttribute("aria-disabled","true")):(t.id=`${this.uid}-nav-${e.navIndex}`,t.dataset.navIndex=String(e.navIndex),e.navIndex===this.highlightedIndex&&t.classList.add("forge-select__option--highlighted")),e.hasChildren){let s=this.query!==""||this.expandedValues.has(e.option.value);t.setAttribute("aria-expanded",String(s));let n=document.createElement("span");n.className="forge-select__twisty",n.dataset.twisty=e.option.value,n.setAttribute("aria-hidden","true"),n.textContent=s?"\u25BC":"\u25B6",t.append(n)}t.append(this.optionContent(e.option));break}}return t}optionContent(e){let t=this.rowContentCache.get(e.value);if(!t){let i=document.createElement("span");if(i.className="forge-select__option-content",x(i,e,this.opts.templateResult),this.rowContentCache.size>=K){let s=this.rowContentCache.keys().next().value;this.rowContentCache.delete(s)}this.rowContentCache.set(e.value,i),t=i}return t.cloneNode(!0)}moveHighlight(e){if(this.navItems.length===0)return;let t=this.highlightedIndex===-1?e>0?0:this.navItems.length-1:(this.highlightedIndex+e+this.navItems.length)%this.navItems.length;this.focusNavIndex(t)}focusNavIndex(e){if(this.highlightedIndex=e,this.usesVirtualScroll()){let t=this.rows.findIndex(i=>(i.kind==="option"||i.kind==="create")&&i.navIndex===e);if(t>=0){let i=this.opts.itemHeight,s=t*i,n=this.list.clientHeight||i*8,a=this.list.scrollTop;s<a?a=s:s+i>a+n&&(a=s+i-n),a!==this.list.scrollTop&&(this.list.scrollTop=a)}this.renderRows()}else this.renderRows(),this.list.querySelector(".forge-select__option--highlighted")?.scrollIntoView?.({block:"nearest"})}navigateTree(e){let t=this.navItems[this.highlightedIndex];if(!t||t.kind!=="option")return!1;let{option:i,parentValue:s}=t,n=!!i.children?.length,a=this.query!==""||this.expandedValues.has(i.value);if(e==="right"){if(n&&!a)return this.expandedValues.add(i.value),this.renderList(),!0;if(n){let o=this.navItems.findIndex(l=>l.kind==="option"&&l.parentValue===i.value);if(o>=0)return this.focusNavIndex(o),!0}return!1}if(n&&a&&this.query==="")return this.expandedValues.delete(i.value),this.renderList(),!0;if(s){let o=this.navItems.findIndex(l=>l.kind==="option"&&l.option.value===s);if(o>=0)return this.focusNavIndex(o),!0}return!1}updateActiveDescendant(){let e=this.searchInput??this.control;this.highlightedIndex>=0?e.setAttribute("aria-activedescendant",`${this.uid}-nav-${this.highlightedIndex}`):e.removeAttribute("aria-activedescendant")}scheduleRemoteLoad(e,t){this.ajaxTimer&&clearTimeout(this.ajaxTimer);let i=++this.ajaxRequestId;this.ajaxController?.abort(),this.ajaxController=null,this.page=0,this.hasMore=!0,this.loading=!0,this.loadingMore=!1,this.loadError=null,this.renderList(),this.ajaxTimer=setTimeout(()=>{this.ajaxTimer=null,this.loadRemote(e,{requestId:i})},t)}maybeLoadNextPage(){if(!this.opts.ajax?.pagination||!this.hasMore||this.loading||this.loadingMore)return;let{scrollHeight:t,scrollTop:i,clientHeight:s}=this.list,n=this.opts.itemHeight*2;t-i-s>=n||(this.loadingMore=!0,this.renderList(),this.loadRemote(this.query,{append:!0}))}async loadRemote(e,{append:t=!1,requestId:i}={}){let s=this.opts.ajax,n=i??++this.ajaxRequestId;if(n!==this.ajaxRequestId)return;this.ajaxController?.abort();let a=new AbortController;this.ajaxController=a;let o=t?this.page+1:0;try{let l=_(s,e,o),h=await fetch(l,{signal:a.signal});if(h.ok===!1)throw new Error(`ForgeSelect: remote request failed with HTTP ${h.status}`);let f=await h.json();if(n!==this.ajaxRequestId||this.destroyed)return;let{options:d,hasMore:c}=R(s,f);if(t){let m=N(this.data);this.data=[...this.data,...d.filter(g=>!m.has(g.value))]}else this.data=d,this.rowContentCache.clear();this.page=o,this.hasMore=c,this.remoteLoaded=!0,this.loadError=null}catch(l){if(n!==this.ajaxRequestId||this.destroyed||a.signal.aborted)return;let h=l instanceof Error?l:new Error(String(l));t||(this.data=[],this.rowContentCache.clear()),this.hasMore=!1,this.loadError=h,this.emitter.emit("error",h)}finally{n===this.ajaxRequestId&&!this.destroyed&&(this.ajaxController=null,this.loading=!1,this.loadingMore=!1,this.isOpen&&this.renderList())}}};return B(X);})();
1
+ "use strict";var ForgeSelectBundle=(()=>{var E=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var B=(o,t)=>{for(var e in t)E(o,e,{get:t[e],enumerable:!0})},$=(o,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of j(t))!P.call(o,i)&&i!==e&&E(o,i,{get:()=>t[i],enumerable:!(s=F(t,i))||s.enumerable});return o};var U=o=>$(E({},"__esModule",{value:!0}),o);var W={};B(W,{ForgeSelect:()=>f,default:()=>f});var b=class{constructor(){this.handlers=new Map}on(t,e){let s=this.handlers.get(t);s||(s=new Set,this.handlers.set(t,s)),s.add(e)}off(t,e){this.handlers.get(t)?.delete(e)}emit(t,...e){let s=this.handlers.get(t);if(s)for(let i of[...s])i(...e)}clear(){this.handlers.clear()}};function D(o,t,e,s=4){let i=e-o.bottom,r=o.top,n=t>i&&r>i;return{dropUp:n,top:n?o.top-t-s:o.bottom+s}}var O={en:{noResults:"No results found",loading:"Loading\u2026",loadingMore:"Loading more\u2026",errorLoading:"Could not load options",createOption:'Create "{query}"',clearSelection:"Clear selection",removeItem:"Remove {label}",search:"Search",reorderHint:"{label}. Press Alt+Left or Alt+Right to reorder.",minSearchLength:"Type {count} or more characters to search",maximumSelected:"Maximum of {count} selections reached"},vi:{noResults:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3",loading:"\u0110ang t\u1EA3i\u2026",loadingMore:"\u0110ang t\u1EA3i th\xEAm\u2026",errorLoading:"Kh\xF4ng th\u1EC3 t\u1EA3i t\xF9y ch\u1ECDn",createOption:'T\u1EA1o "{query}"',clearSelection:"X\xF3a l\u1EF1a ch\u1ECDn",removeItem:"X\xF3a {label}",search:"T\xECm ki\u1EBFm",reorderHint:"{label}. Nh\u1EA5n Alt+Tr\xE1i ho\u1EB7c Alt+Ph\u1EA3i \u0111\u1EC3 s\u1EAFp x\u1EBFp l\u1EA1i.",minSearchLength:"Nh\u1EADp th\xEAm {count} k\xFD t\u1EF1 \u0111\u1EC3 t\xECm ki\u1EBFm",maximumSelected:"\u0110\xE3 \u0111\u1EA1t t\u1ED1i \u0111a {count} l\u1EF1a ch\u1ECDn"}};function _(o){return typeof o=="string"?O[o]??O.en:{...O.en,...o}}function u(o,t){return o.replace(/\{(\w+)\}/g,(e,s)=>t[s]??e)}function H(o){let t=[];for(let e of Array.from(o.children))e instanceof HTMLOptGroupElement?t.push({label:e.label,options:Array.from(e.querySelectorAll("option")).map(M)}):e instanceof HTMLOptionElement&&t.push(M(e));return t}function M(o){let t=o.parentElement instanceof HTMLOptGroupElement&&o.parentElement.disabled;return{value:o.value,label:o.textContent?.trim()??o.value,disabled:o.disabled||t||void 0}}function x(o,t,e,s="row"){if(e){let i=e(t);typeof i=="string"?o.innerHTML=i:o.append(i);return}if(!t.avatar&&!t.description){o.textContent=t.label;return}if(t.avatar){let i=document.createElement("img");i.className=s==="row"?"forge-select__option-avatar":"forge-select__inline-avatar",i.src=t.avatar,i.alt="",i.setAttribute("loading","lazy"),i.setAttribute("decoding","async"),o.append(i)}if(s==="row"&&t.description){let i=document.createElement("span");i.className="forge-select__option-body";let r=document.createElement("span");r.className="forge-select__option-label",r.textContent=t.label;let n=document.createElement("span");n.className="forge-select__option-desc",n.textContent=t.description,i.append(r,n),o.append(i)}else{let i=document.createElement("span");i.className="forge-select__option-label",i.textContent=t.label,o.append(i)}}function R(o,t,e){if(!o.url)throw new Error("ForgeSelect: ajax requires either url or request.");if(typeof o.url=="function")return o.url(t,e);if(!o.params)return o.url;let s=new URLSearchParams;for(let[r,n]of Object.entries(o.params(t,e)))s.set(r,String(n));let i=o.url.includes("?")?"&":"?";return`${o.url}${i}${s.toString()}`}function k(o,t){let e=o.transform?o.transform(t):t;if(Array.isArray(e))return{options:e,hasMore:!1};if(!e||!Array.isArray(e.options))throw new Error("ForgeSelect: ajax.transform must return an array of options, or an object shaped like { options: Option[], hasMore?: boolean }.");return{options:e.options,hasMore:o.pagination?!!e.hasMore:!1}}function p(o){return o.options!==void 0}var w=o=>!!o.disabled;function v(o,t=w){if(!o.children)return[];let e=[];for(let s of o.children)t(s)||e.push(s.value),e.push(...v(s,t));return e}function y(o,t,e=w){if(!o.children?.length)return t.includes(o.value)?"all":"none";let s=o.children.filter(i=>!e(i)).map(i=>y(i,t,e));return s.length===0?"none":s.every(i=>i==="all")?"all":s.every(i=>i==="none")?"none":"some"}function N(o,t){let e=s=>{for(let i of s){if(i.value===t)return i;let r=i.children?e(i.children):void 0;if(r)return r}};for(let s of o){let i=e(p(s)?s.options:[s]);if(i)return i}}function L(o,t,e=w){let s=i=>{if(!i.children?.length)return;for(let a of i.children)s(a);let r=y(i,t,e),n=t.indexOf(i.value);r==="all"&&n===-1?t.push(i.value):r!=="all"&&n!==-1&&t.splice(n,1)};for(let i of o)(p(i)?i.options:[i]).forEach(s)}function I(o){let t=new Set,e=s=>{t.add(s.value),s.children?.forEach(e)};for(let s of o)(p(s)?s.options:[s]).forEach(e);return t}function V(o,t){return o.length===t.length&&o.every((e,s)=>e===t[s])}var G=36,q=5,z=100,K=2e3,X=0,f=class{constructor(t,e={}){this.selected=[];this.selectedOptions=new Map;this.suppressNextTagClick=!1;this.emitter=new b;this.uid=`forge-select-${++X}`;this.searchInput=null;this.portalHost=null;this.isOpen=!1;this.isDisabled=!1;this.destroyed=!1;this.query="";this.rows=[];this.navItems=[];this.highlightedIndex=-1;this.rowContentCache=new Map;this.expandedValues=new Set;this.loading=!1;this.loadingMore=!1;this.page=0;this.hasMore=!0;this.ajaxTimer=null;this.ajaxRequestId=0;this.ajaxController=null;this.remoteLoaded=!1;this.loadError=null;this.originalDisplay="";this.originalDisabled=!1;this.nativeSelect=null;this.nativeForm=null;this.syncingNative=!1;this.isOptionDisabled=t=>t.disabled===!0||(this.opts.isOptionDisabled?.(t)??!1);this.pointerDownOnControl=!1;this.onDocumentMouseDown=t=>{let e=t.target;!this.root.contains(e)&&!this.portalHost?.contains(e)&&this.close()};this.onWindowResize=()=>{this.positionDropdown()};this.onAncestorScroll=()=>{this.portalHost&&this.positionDropdown()};this.onNativeInvalid=t=>{t.preventDefault(),this.control.classList.add("forge-select__control--invalid"),this.control.setAttribute("aria-invalid","true"),this.isOpen||this.open(),this.control.focus()};this.onNativeChange=()=>{if(!this.nativeSelect||this.destroyed||this.syncingNative)return;let t=Array.from(this.nativeSelect.selectedOptions,e=>e.value);this.applyNativeValues(t)};this.onFormReset=()=>{if(!this.nativeSelect||this.destroyed)return;let t=Array.from(this.nativeSelect.options).filter(e=>e.defaultSelected).map(e=>e.value);this.applyNativeValues(t)};let s=typeof t=="string"?document.querySelector(t):t;if(!s)throw new Error(`ForgeSelect: target element not found: ${String(t)}`);this.el=s;let i=s instanceof HTMLSelectElement?s:null;if(this.nativeSelect=i,this.nativeForm=i?.form??null,this.originalDisplay=s.style.display,this.originalDisabled=i?.disabled??!1,this.opts={placeholder:e.placeholder??"",searchable:e.searchable??!0,multiple:e.multiple??i?.multiple??!1,clearable:e.clearable??!1,allowCreate:e.allowCreate??!1,sortable:e.sortable??!1,closeOnSelect:e.closeOnSelect??!1,maxSelections:e.maxSelections==null||!Number.isFinite(e.maxSelections)?void 0:Math.max(0,Math.floor(e.maxSelections)),theme:e.theme??"default",disabled:e.disabled??i?.disabled??!1,required:e.required??i?.required??!1,data:e.data,ajax:e.ajax,templateResult:e.templateResult,templateSelection:e.templateSelection,filterOption:e.filterOption,minSearchLength:Math.max(0,Math.floor(e.minSearchLength??0)),minResultsForSearch:Math.max(0,Math.floor(e.minResultsForSearch??0)),isOptionDisabled:e.isOptionDisabled,virtualScroll:e.virtualScroll,itemHeight:e.itemHeight??G,language:e.language??"en",plugins:e.plugins??[],openOnFocus:e.openOnFocus??!1,dropdownParent:e.dropdownParent},this.strings=_(this.opts.language),this.plugins=this.opts.plugins,i&&(i.required=this.opts.required),this.data=this.opts.data??(i?H(i):[]),i&&!this.opts.data){let r=Array.from(i.options),n=i.multiple||i.selectedIndex>0||r.some(a=>a.defaultSelected);for(let a of r)n&&a.selected&&this.selectValue(a.value,!1)}this.buildDom(),this.renderValue(),this.opts.disabled&&this.disable(),i?.addEventListener("change",this.onNativeChange),i?.addEventListener("invalid",this.onNativeInvalid),this.nativeForm?.addEventListener("reset",this.onFormReset);for(let r of this.plugins)r.onInit?.(this)}applyNativeValues(t){this.selected=[];for(let e of this.opts.multiple?t:t.slice(0,1))this.selectValue(e,!1);this.renderValue(),this.isOpen&&this.renderList(),this.emitter.emit("change",this.getValue())}open(){if(!(this.isOpen||this.isDisabled||this.destroyed)){this.isOpen=!0,this.dropdown.hidden=!1,this.root.classList.add("forge-select--open"),this.control.setAttribute("aria-expanded","true"),document.addEventListener("mousedown",this.onDocumentMouseDown),this.opts.ajax&&!this.remoteLoaded&&this.scheduleRemoteLoad(this.query,0),this.renderList(),this.positionDropdown(),window.addEventListener("resize",this.onWindowResize),document.addEventListener("scroll",this.onAncestorScroll,!0),this.searchInput&&!this.searchInput.hidden&&this.searchInput.focus(),this.emitter.emit("open");for(let t of this.plugins)t.onOpen?.(this)}}close(){if(this.isOpen){this.isOpen=!1,this.dropdown.hidden=!0,this.root.classList.remove("forge-select--open"),this.root.classList.remove("forge-select--drop-up"),this.control.setAttribute("aria-expanded","false"),document.removeEventListener("mousedown",this.onDocumentMouseDown),window.removeEventListener("resize",this.onWindowResize),document.removeEventListener("scroll",this.onAncestorScroll,!0),this.highlightedIndex=-1,this.searchInput&&(this.searchInput.value="",this.query=""),this.emitter.emit("close");for(let t of this.plugins)t.onClose?.(this)}}positionDropdown(){let t=this.control.getBoundingClientRect(),e=D(t,this.dropdown.offsetHeight,window.innerHeight);this.root.classList.toggle("forge-select--drop-up",e.dropUp),this.portalHost&&(this.portalHost.style.top=`${e.top}px`,this.portalHost.style.left=`${t.left}px`,this.portalHost.style.width=`${t.width}px`)}destroy(){if(!this.destroyed){this.close();for(let t of this.plugins)t.onDestroy?.(this);this.destroyed=!0,this.ajaxTimer&&clearTimeout(this.ajaxTimer),this.ajaxController?.abort(),this.nativeSelect?.removeEventListener("change",this.onNativeChange),this.nativeSelect?.removeEventListener("invalid",this.onNativeInvalid),this.nativeForm?.removeEventListener("reset",this.onFormReset),this.rowContentCache.clear(),this.portalHost?.remove(),this.root.remove(),this.el.style.display=this.originalDisplay,this.nativeSelect&&(this.nativeSelect.disabled=this.originalDisabled),this.emitter.clear()}}getValue(){return this.opts.multiple?[...this.selected]:this.selected[0]??null}setValue(t,e={}){let s=t==null?[]:Array.isArray(t)?t:[t],i=this.opts.multiple?s:s.slice(0,1);if(!V(i,this.selected)){this.selected=[];for(let r of i)this.selectValue(r,!1);this.afterSelectionChange(e.emitChange??!0)}}setData(t){this.ajaxTimer&&(clearTimeout(this.ajaxTimer),this.ajaxTimer=null),this.ajaxController?.abort(),this.ajaxController=null,this.ajaxRequestId+=1,this.loading=!1,this.loadingMore=!1,this.loadError=null,this.remoteLoaded=!0,this.page=0,this.hasMore=!1,this.data=t,this.opts.data=t,this.updateSearchVisibility(),this.rowContentCache.clear(),this.highlightedIndex=-1,this.isOpen&&this.renderList()}selectAll(){if(this.opts.multiple){this.selected=[];for(let t of this.allSelectableValues()){let e=this.findOption(t);e&&this.canSelectOption(e)&&this.selectValue(t,!1)}this.afterSelectionChange()}}clearAll(){this.clearSelection()}enable(){this.isDisabled=!1,this.root.classList.remove("forge-select--disabled"),this.control.tabIndex=0,this.control.setAttribute("aria-disabled","false"),this.nativeSelect&&(this.nativeSelect.disabled=!1)}disable(){this.close(),this.isDisabled=!0,this.root.classList.add("forge-select--disabled"),this.control.tabIndex=-1,this.control.setAttribute("aria-disabled","true"),this.nativeSelect&&(this.nativeSelect.disabled=!0)}on(t,e){this.emitter.on(t,e)}off(t,e){this.emitter.off(t,e)}applyAccessibleName(){let t=this.el.getAttribute("aria-labelledby"),e=this.el.getAttribute("aria-label");if(t)this.control.setAttribute("aria-labelledby",t);else if(e)this.control.setAttribute("aria-label",e);else if(this.el.id){let s=Array.from(document.getElementsByTagName("label")).find(i=>i.htmlFor===this.el.id);s&&(s.id||(s.id=`${this.uid}-label`),this.control.setAttribute("aria-labelledby",s.id))}}shouldShowSearch(){return this.opts.searchable&&(this.opts.ajax!=null||I(this.data).size>=this.opts.minResultsForSearch)}updateSearchVisibility(){this.searchInput&&(this.searchInput.hidden=!this.shouldShowSearch(),this.searchInput.hidden&&(this.searchInput.value="",this.query=""))}buildDom(){let t=typeof this.opts.dropdownParent=="string"?document.querySelector(this.opts.dropdownParent):this.opts.dropdownParent;if(this.opts.dropdownParent&&!t)throw new Error(`ForgeSelect: dropdown parent not found: ${String(this.opts.dropdownParent)}`);this.root=document.createElement("div"),this.root.className="forge-select",this.root.dataset.theme=this.opts.theme,this.root.style.setProperty("--fs-item-height",`${this.opts.itemHeight}px`),this.opts.sortable&&this.opts.multiple&&this.root.classList.add("forge-select--sortable"),this.control=document.createElement("div"),this.control.className="forge-select__control",this.control.setAttribute("role","combobox"),this.control.setAttribute("aria-haspopup","listbox"),this.control.setAttribute("aria-expanded","false"),this.control.setAttribute("aria-controls",`${this.uid}-list`),this.opts.required&&this.control.setAttribute("aria-required","true"),this.control.tabIndex=0,this.applyAccessibleName(),this.valueEl=document.createElement("div"),this.valueEl.className="forge-select__value",this.clearBtn=document.createElement("button"),this.clearBtn.type="button",this.clearBtn.className="forge-select__clear",this.clearBtn.setAttribute("aria-label",this.strings.clearSelection),this.clearBtn.textContent="\xD7",this.clearBtn.hidden=!0;let e=document.createElement("span");e.className="forge-select__arrow",e.setAttribute("aria-hidden","true"),this.control.append(this.valueEl,this.clearBtn,e),this.dropdown=document.createElement("div"),this.dropdown.className="forge-select__dropdown",this.dropdown.hidden=!0,this.opts.searchable&&(this.searchInput=document.createElement("input"),this.searchInput.type="search",this.searchInput.className="forge-select__search",this.searchInput.setAttribute("aria-label",this.strings.search),this.searchInput.setAttribute("aria-autocomplete","list"),this.searchInput.setAttribute("aria-controls",`${this.uid}-list`),this.searchInput.hidden=!this.shouldShowSearch(),this.dropdown.append(this.searchInput)),this.list=document.createElement("ul"),this.list.className="forge-select__list",this.list.id=`${this.uid}-list`,this.list.setAttribute("role","listbox"),this.opts.multiple&&this.list.setAttribute("aria-multiselectable","true"),this.dropdown.append(this.list),this.liveRegion=document.createElement("div"),this.liveRegion.className="forge-select__sr-only",this.liveRegion.setAttribute("role","status"),this.liveRegion.setAttribute("aria-live","polite"),this.root.append(this.control,this.liveRegion),t||this.root.append(this.dropdown),this.el.style.display="none",this.el.insertAdjacentElement("afterend",this.root),t&&(this.portalHost=document.createElement("div"),this.portalHost.className="forge-select forge-select--portal-host",this.portalHost.dataset.theme=this.opts.theme,this.portalHost.style.setProperty("--fs-item-height",`${this.opts.itemHeight}px`),this.portalHost.append(this.dropdown),t.append(this.portalHost)),this.bindEvents()}bindEvents(){this.control.addEventListener("click",t=>{if(t.target!==this.clearBtn){if(this.suppressNextTagClick){this.suppressNextTagClick=!1;return}this.isDisabled||(this.isOpen?this.close():this.open())}}),this.control.addEventListener("keydown",t=>this.handleKeydown(t)),this.control.addEventListener("mousedown",()=>{this.pointerDownOnControl=!0}),this.control.addEventListener("focus",()=>{this.opts.openOnFocus&&!this.pointerDownOnControl&&!this.isOpen&&!this.isDisabled&&this.open(),this.pointerDownOnControl=!1}),this.clearBtn.addEventListener("click",t=>{t.stopPropagation(),this.clearSelection()}),this.searchInput&&(this.searchInput.addEventListener("input",()=>{this.query=this.searchInput.value,this.highlightedIndex=-1,this.list.scrollTop=0,this.emitter.emit("search",this.query);let t=this.query.trim(),e=t!==""&&t.length<this.opts.minSearchLength;this.opts.ajax&&!e?this.scheduleRemoteLoad(this.query,this.opts.ajax.debounce??250):(e&&(this.ajaxTimer&&(clearTimeout(this.ajaxTimer),this.ajaxTimer=null),this.ajaxController?.abort(),this.loading=!1),this.renderList())}),this.searchInput.addEventListener("keydown",t=>this.handleKeydown(t)),this.searchInput.addEventListener("paste",t=>{if(!this.opts.multiple||!this.opts.allowCreate)return;let s=(t.clipboardData?.getData("text")??"").split(/[,\n]+/).map(r=>r.trim()).filter(Boolean);if(s.length<2)return;t.preventDefault();let i=[];for(let r of s){let n=this.createTag(r);n&&i.push(n)}if(i.length!==0){this.searchInput.value="",this.query="",this.afterSelectionChange();for(let r of i)r.created&&this.emitter.emit("create",r.option),this.emitter.emit("select",r.option);this.opts.closeOnSelect?this.close():this.renderList()}})),this.list.addEventListener("click",t=>{let e=t.target,s=e.closest("[data-twisty]");if(s){let n=s.dataset.twisty;this.expandedValues.has(n)?this.expandedValues.delete(n):this.expandedValues.add(n),this.renderList();return}let i=e.closest("li[data-nav-index]");if(!i){let n=e.closest("li[data-option-value]"),a=n?this.findOption(n.dataset.optionValue):void 0;a&&this.hasReachedMaximum()&&!this.selected.includes(a.value)&&this.announceMaximum(a);return}let r=Number(i.dataset.navIndex);this.activateNavItem(r)}),this.list.addEventListener("scroll",()=>{this.usesVirtualScroll()&&this.renderRows(),this.maybeLoadNextPage()})}handleKeydown(t){if(!this.isDisabled)switch(t.key){case"Enter":t.preventDefault(),this.isOpen?this.highlightedIndex>=0&&this.activateNavItem(this.highlightedIndex):this.open();break;case" ":t.target===this.control&&(t.preventDefault(),this.isOpen||this.open());break;case"ArrowDown":t.preventDefault(),this.isOpen?this.moveHighlight(1):this.open();break;case"ArrowUp":t.preventDefault(),this.isOpen&&this.moveHighlight(-1);break;case"Escape":this.isOpen&&(t.preventDefault(),this.close(),this.control.focus());break;case"ArrowRight":this.isOpen&&this.navigateTree("right")&&t.preventDefault();break;case"ArrowLeft":this.isOpen&&this.navigateTree("left")&&t.preventDefault();break;case"Tab":this.close();break}}canSelectOption(t){if(this.opts.maxSelections==null)return!0;let e=[...this.selected];e.includes(t.value)||e.push(t.value);for(let s of v(t,this.isOptionDisabled))e.includes(s)||e.push(s);return L(this.data,e,this.isOptionDisabled),e.length<=this.opts.maxSelections}hasReachedMaximum(){return this.opts.maxSelections!=null&&this.selected.length>=this.opts.maxSelections}announceMaximum(t){let e=this.opts.maxSelections;e!=null&&(this.liveRegion.textContent=u(this.strings.maximumSelected,{count:String(e)}),this.emitter.emit("maximum",{limit:e,option:t}))}selectValue(t,e){if(this.selected.includes(t))return;let s=this.findOption(t)??this.selectedOptions.get(t)??{value:t,label:t};if(this.selectedOptions.set(t,s),this.opts.multiple){this.selected.push(t);for(let i of v(s,this.isOptionDisabled))this.selected.includes(i)||this.selected.push(i);this.syncTreeAncestors()}else this.selected=[t];e&&(this.afterSelectionChange(),this.emitter.emit("select",s))}deselectValue(t,e){let s=this.selected.indexOf(t);if(s===-1)return;let i=this.findOption(t)??this.selectedOptions.get(t);if(this.selected.splice(s,1),this.opts.multiple){if(i)for(let r of v(i,this.isOptionDisabled)){let n=this.selected.indexOf(r);n!==-1&&this.selected.splice(n,1)}this.syncTreeAncestors()}e&&(this.afterSelectionChange(),this.emitter.emit("unselect",i??{value:t,label:t}))}syncTreeAncestors(){L(this.data,this.selected,this.isOptionDisabled)}clearSelection(){this.selected.length!==0&&(this.selected=[],this.emitter.emit("clear"),this.afterSelectionChange())}allSelectableValues(){let t=[],e=s=>{this.isOptionDisabled(s)||t.push(s.value),s.children?.forEach(e)};for(let s of this.data)(p(s)?s.options:[s]).forEach(e);return t}afterSelectionChange(t=!0){this.renderValue(),this.syncNativeSelect(t),(!this.opts.required||this.selected.length>0)&&(this.control.classList.remove("forge-select__control--invalid"),this.control.removeAttribute("aria-invalid")),this.isOpen&&this.renderList(),t&&this.emitter.emit("change",this.getValue())}syncNativeSelect(t=!0){if(!(this.el instanceof HTMLSelectElement))return;let e=new Set;for(let s of Array.from(this.el.options))e.add(s.value),s.selected=this.selected.includes(s.value);for(let s of this.selected){if(e.has(s))continue;let i=document.createElement("option");i.value=s,i.textContent=this.selectedOptions.get(s)?.label??s,i.selected=!0,this.el.append(i)}if(this.opts.sortable&&this.opts.multiple)for(let s of this.selected){let i=Array.from(this.el.options).find(r=>r.value===s);i&&this.el.append(i)}if(t){this.syncingNative=!0;try{this.el.dispatchEvent(new Event("change",{bubbles:!0}))}finally{this.syncingNative=!1}}}findOption(t){return N(this.data,t)}findOptionByLabel(t){let e=t.toLowerCase(),s=i=>{for(let r of i){if(r.label.toLowerCase()===e)return r;let n=r.children?s(r.children):void 0;if(n)return n}};for(let i of this.data){let r=s(p(i)?i.options:[i]);if(r)return r}}createTag(t){let e=t.trim();if(!e)return;let s=this.findOptionByLabel(e);if(s){if(this.selected.includes(s.value))return;if(this.opts.multiple&&!this.canSelectOption(s)){this.announceMaximum(s);return}return this.selectValue(s.value,!1),{option:s,created:!1}}let i={value:e,label:e};if(this.opts.multiple&&!this.canSelectOption(i)){this.announceMaximum(i);return}return this.data.push(i),this.selectValue(i.value,!1),{option:i,created:!0}}createFromQuery(){let t=this.query.trim();if(!t)return;let e=this.createTag(t);e&&(this.searchInput&&(this.searchInput.value="",this.query=""),this.afterSelectionChange(),e.created&&this.emitter.emit("create",e.option),this.emitter.emit("select",e.option),(!this.opts.multiple||this.opts.closeOnSelect)&&this.close())}activateNavItem(t){let e=this.navItems[t];if(!e)return;if(e.kind==="create"){this.createFromQuery();return}let{value:s}=e.option;if(this.opts.multiple){let i=!1;this.selected.includes(s)?(this.deselectValue(s,!0),i=!0):this.canSelectOption(e.option)?(this.selectValue(s,!0),i=!0):this.announceMaximum(e.option),i&&this.opts.closeOnSelect&&this.close()}else this.selectValue(s,!0),this.close(),this.control.focus()}renderValue(){this.valueEl.textContent="";let t=this.selected.length>0;if(this.clearBtn.hidden=!(this.opts.clearable&&t),!t){let e=document.createElement("span");e.className="forge-select__placeholder",e.textContent=this.opts.placeholder,this.valueEl.append(e);return}if(this.opts.multiple)for(let e of this.selected){let s=this.selectedOptions.get(e)??{value:e,label:e},i=document.createElement("span");i.className="forge-select__tag";let r=document.createElement("span");r.className="forge-select__tag-label",x(r,s,this.opts.templateSelection,"inline");let n=document.createElement("button");n.type="button",n.className="forge-select__tag-remove",n.setAttribute("aria-label",u(this.strings.removeItem,{label:s.label})),n.textContent="\xD7",n.addEventListener("click",a=>{a.stopPropagation(),this.isDisabled||this.deselectValue(e,!0)}),i.append(r,n),this.opts.sortable&&(i.dataset.value=e,i.tabIndex=0,i.setAttribute("aria-roledescription","draggable item"),i.setAttribute("aria-label",u(this.strings.reorderHint,{label:s.label})),i.addEventListener("keydown",a=>this.handleTagKeydown(a,e)),this.bindTagDrag(i,e)),this.valueEl.append(i)}else{let e=this.selectedOptions.get(this.selected[0])??{value:this.selected[0],label:this.selected[0]},s=document.createElement("span");s.className="forge-select__single-value",x(s,e,this.opts.templateSelection,"inline"),this.valueEl.append(s)}}bindTagDrag(t,e){let i=0,r=!1,n=[],a=h=>{if(!r){if(Math.abs(h.clientX-i)<4)return;r=!0,n=[...this.selected],typeof this.valueEl.setPointerCapture=="function"&&this.valueEl.setPointerCapture(h.pointerId),t.classList.add("forge-select__tag--dragging")}h.preventDefault();let m=n.indexOf(e),d=Array.from(this.valueEl.querySelectorAll(".forge-select__tag"));for(let c of d){if(c===t)continue;let g=c.dataset.value;if(!g)continue;let S=n.indexOf(g);if(S===-1)continue;let C=c.getBoundingClientRect(),A=C.left+C.width/2,T=m<S;if(T?h.clientX>A:h.clientX<A){n.splice(m,1),n.splice(S,0,e),T?this.valueEl.insertBefore(t,c.nextSibling):this.valueEl.insertBefore(t,c);break}}},l=h=>{this.valueEl.removeEventListener("pointermove",a),this.valueEl.removeEventListener("pointerup",l),this.valueEl.removeEventListener("pointercancel",l),r&&(typeof this.valueEl.releasePointerCapture=="function"&&this.valueEl.releasePointerCapture(h.pointerId),t.classList.remove("forge-select__tag--dragging"),this.selected=n,this.suppressNextTagClick=!0,this.afterSelectionChange(),this.emitter.emit("reorder",[...this.selected]))};t.addEventListener("pointerdown",h=>{this.isDisabled||h.button!==0||h.target.closest(".forge-select__tag-remove")||(i=h.clientX,r=!1,this.valueEl.addEventListener("pointermove",a),this.valueEl.addEventListener("pointerup",l),this.valueEl.addEventListener("pointercancel",l))})}handleTagKeydown(t,e){if(!t.altKey||t.key!=="ArrowLeft"&&t.key!=="ArrowRight")return;let s=this.selected.indexOf(e),i=t.key==="ArrowLeft"?s-1:s+1;if(s===-1||i<0||i>=this.selected.length)return;t.preventDefault(),t.stopPropagation();let r=[...this.selected];[r[s],r[i]]=[r[i],r[s]],this.selected=r,this.afterSelectionChange(),this.emitter.emit("reorder",[...this.selected]),this.focusTagByValue(e)}focusTagByValue(t){for(let e of Array.from(this.valueEl.querySelectorAll(".forge-select__tag")))if(e.dataset.value===t){e.focus();return}}buildRows(){this.rows=[],this.navItems=[];let t=this.query.trim(),e=t.toLowerCase(),s=n=>e===""||(this.opts.filterOption?this.opts.filterOption(n,t):n.label.toLowerCase().includes(e)||(n.description?.toLowerCase().includes(e)??!1)),i=n=>e===""||s(n)||(n.children??[]).some(i),r=(n,a,l)=>{let h=-1;this.isOptionDisabled(n)||this.hasReachedMaximum()&&!this.selected.includes(n.value)||(h=this.navItems.length,this.navItems.push({kind:"option",option:n,parentValue:l}));let d=!!n.children&&n.children.length>0;if(this.rows.push({kind:"option",option:n,navIndex:h,depth:a,hasChildren:d}),d&&(e!==""||this.expandedValues.has(n.value)))for(let g of n.children)i(g)&&r(g,a+1,n.value)};if(t!==""&&t.length<this.opts.minSearchLength){this.rows.push({kind:"min-length"});return}if(this.loading){this.rows.push({kind:"loading"});return}if(this.loadError){this.rows.push({kind:"error"});return}for(let n of this.data)if(p(n)){let a=n.options.filter(i);if(a.length===0)continue;this.rows.push({kind:"group",label:n.label}),a.forEach(l=>r(l,0))}else i(n)&&r(n,0);if(this.opts.allowCreate&&e!==""&&!this.hasExactMatch(e)){let n=this.navItems.length;this.navItems.push({kind:"create"}),this.rows.push({kind:"create",navIndex:n})}this.rows.length===0?this.rows.push({kind:"empty"}):this.loadingMore&&this.rows.push({kind:"loading-more"})}hasExactMatch(t){return!!this.findOptionByLabel(t)}usesVirtualScroll(){return this.opts.virtualScroll!==!1&&this.rows.length>z}renderList(){this.buildRows(),this.renderRows(),this.announceStatus()}announceStatus(){let t=this.rows[0],e=this.hasReachedMaximum()?u(this.strings.maximumSelected,{count:String(this.opts.maxSelections)}):t?.kind==="loading"?this.strings.loading:t?.kind==="error"?this.strings.errorLoading:t?.kind==="empty"?this.strings.noResults:t?.kind==="min-length"?u(this.strings.minSearchLength,{count:String(this.opts.minSearchLength)}):"";this.liveRegion.textContent!==e&&(this.liveRegion.textContent=e)}renderRows(){let t=this.list.scrollTop,e=this.list.clientHeight,s=this.usesVirtualScroll();this.list.textContent="";let i=this.opts.itemHeight,r=0,n=this.rows.length;if(s){let a=e||i*8;r=Math.max(0,Math.floor(t/i)-q),n=Math.min(this.rows.length,r+Math.ceil(a/i)+q*2);let l=document.createElement("li");l.className="forge-select__spacer",l.setAttribute("aria-hidden","true"),l.style.height=`${r*i}px`,this.list.append(l)}for(let a=r;a<n;a++)this.list.append(this.renderRow(this.rows[a]));if(s){let a=document.createElement("li");a.className="forge-select__spacer",a.setAttribute("aria-hidden","true"),a.style.height=`${(this.rows.length-n)*i}px`,this.list.append(a),this.list.scrollTop!==t&&(this.list.scrollTop=t)}this.updateActiveDescendant()}renderRow(t){let e=document.createElement("li");switch(t.kind){case"group":e.className="forge-select__group-label",e.setAttribute("role","presentation"),e.textContent=t.label;break;case"empty":e.className="forge-select__empty",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.noResults;break;case"min-length":e.className="forge-select__min-length",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=u(this.strings.minSearchLength,{count:String(this.opts.minSearchLength)});break;case"error":e.className="forge-select__error",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.errorLoading;break;case"loading":e.className="forge-select__loading",e.setAttribute("role","option"),e.setAttribute("aria-disabled","true"),e.setAttribute("aria-selected","false"),e.textContent=this.strings.loading;break;case"loading-more":e.className="forge-select__loading-more",e.setAttribute("aria-hidden","true"),e.textContent=this.strings.loadingMore;break;case"create":e.className="forge-select__option forge-select__option--create",e.setAttribute("role","option"),e.id=`${this.uid}-nav-${t.navIndex}`,e.dataset.navIndex=String(t.navIndex),e.textContent=u(this.strings.createOption,{query:this.query.trim()}),t.navIndex===this.highlightedIndex&&e.classList.add("forge-select__option--highlighted");break;case"option":{e.className="forge-select__option",e.dataset.optionValue=t.option.value,t.option.className&&e.classList.add(...t.option.className.trim().split(/\s+/).filter(Boolean)),e.setAttribute("role","option");let s=this.selected.includes(t.option.value);if(e.setAttribute("aria-selected",String(s)),s&&e.classList.add("forge-select__option--selected"),this.opts.multiple&&t.hasChildren&&y(t.option,this.selected,this.isOptionDisabled)==="some"&&e.classList.add("forge-select__option--indeterminate"),t.depth>0&&(e.style.paddingLeft=`calc(12px + ${t.depth} * var(--fs-tree-indent, 18px))`),this.isOptionDisabled(t.option)||this.hasReachedMaximum()&&!this.selected.includes(t.option.value)?(e.classList.add("forge-select__option--disabled"),e.setAttribute("aria-disabled","true")):(e.id=`${this.uid}-nav-${t.navIndex}`,e.dataset.navIndex=String(t.navIndex),t.navIndex===this.highlightedIndex&&e.classList.add("forge-select__option--highlighted")),t.hasChildren){let i=this.query!==""||this.expandedValues.has(t.option.value);e.setAttribute("aria-expanded",String(i));let r=document.createElement("span");r.className="forge-select__twisty",r.dataset.twisty=t.option.value,r.setAttribute("aria-hidden","true"),r.textContent=i?"\u25BC":"\u25B6",e.append(r)}e.append(this.optionContent(t.option));break}}return e}optionContent(t){let e=this.rowContentCache.get(t.value);if(!e){let s=document.createElement("span");if(s.className="forge-select__option-content",x(s,t,this.opts.templateResult),this.rowContentCache.size>=K){let i=this.rowContentCache.keys().next().value;this.rowContentCache.delete(i)}this.rowContentCache.set(t.value,s),e=s}return e.cloneNode(!0)}moveHighlight(t){if(this.navItems.length===0)return;let e=this.highlightedIndex===-1?t>0?0:this.navItems.length-1:(this.highlightedIndex+t+this.navItems.length)%this.navItems.length;this.focusNavIndex(e)}focusNavIndex(t){if(this.highlightedIndex=t,this.usesVirtualScroll()){let e=this.rows.findIndex(s=>(s.kind==="option"||s.kind==="create")&&s.navIndex===t);if(e>=0){let s=this.opts.itemHeight,i=e*s,r=this.list.clientHeight||s*8,n=this.list.scrollTop;i<n?n=i:i+s>n+r&&(n=i+s-r),n!==this.list.scrollTop&&(this.list.scrollTop=n)}this.renderRows()}else this.renderRows(),this.list.querySelector(".forge-select__option--highlighted")?.scrollIntoView?.({block:"nearest"})}navigateTree(t){let e=this.navItems[this.highlightedIndex];if(!e||e.kind!=="option")return!1;let{option:s,parentValue:i}=e,r=!!s.children?.length,n=this.query!==""||this.expandedValues.has(s.value);if(t==="right"){if(r&&!n)return this.expandedValues.add(s.value),this.renderList(),!0;if(r){let a=this.navItems.findIndex(l=>l.kind==="option"&&l.parentValue===s.value);if(a>=0)return this.focusNavIndex(a),!0}return!1}if(r&&n&&this.query==="")return this.expandedValues.delete(s.value),this.renderList(),!0;if(i){let a=this.navItems.findIndex(l=>l.kind==="option"&&l.option.value===i);if(a>=0)return this.focusNavIndex(a),!0}return!1}updateActiveDescendant(){let t=this.searchInput??this.control;this.highlightedIndex>=0?t.setAttribute("aria-activedescendant",`${this.uid}-nav-${this.highlightedIndex}`):t.removeAttribute("aria-activedescendant")}scheduleRemoteLoad(t,e){this.ajaxTimer&&clearTimeout(this.ajaxTimer);let s=++this.ajaxRequestId;this.ajaxController?.abort(),this.ajaxController=null,this.page=0,this.hasMore=!0,this.loading=!0,this.loadingMore=!1,this.loadError=null,this.renderList(),this.ajaxTimer=setTimeout(()=>{this.ajaxTimer=null,this.loadRemote(t,{requestId:s})},e)}maybeLoadNextPage(){if(!this.opts.ajax?.pagination||!this.hasMore||this.loading||this.loadingMore)return;let{scrollHeight:e,scrollTop:s,clientHeight:i}=this.list,r=this.opts.itemHeight*2;e-s-i>=r||(this.loadingMore=!0,this.renderList(),this.loadRemote(this.query,{append:!0}))}async loadRemote(t,{append:e=!1,requestId:s}={}){let i=this.opts.ajax,r=s??++this.ajaxRequestId;if(r!==this.ajaxRequestId)return;this.ajaxController?.abort();let n=new AbortController;this.ajaxController=n;let a=e?this.page+1:0;try{let l;if(i.request)l=await i.request(t,a,n.signal);else{let d=R(i,t,a),c=await fetch(d,{signal:n.signal});if(c.ok===!1)throw new Error(`ForgeSelect: remote request failed with HTTP ${c.status}`);l=await c.json()}if(r!==this.ajaxRequestId||this.destroyed)return;let{options:h,hasMore:m}=k(i,l);if(e){let d=I(this.data);this.data=[...this.data,...h.filter(c=>!d.has(c.value))]}else this.data=h,this.rowContentCache.clear();this.page=a,this.hasMore=m,this.remoteLoaded=!0,this.loadError=null}catch(l){if(r!==this.ajaxRequestId||this.destroyed||n.signal.aborted)return;let h=l instanceof Error?l:new Error(String(l));e||(this.data=[],this.rowContentCache.clear()),this.hasMore=!1,this.loadError=h,this.emitter.emit("error",h)}finally{r===this.ajaxRequestId&&!this.destroyed&&(this.ajaxController=null,this.loading=!1,this.loadingMore=!1,this.isOpen&&this.renderList())}}};return U(W);})();
2
2
  //# sourceMappingURL=index.global.js.map