forge-select 0.3.0 → 0.5.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,10 +22,26 @@ 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;
35
+ /** Load the initial empty query when the dropdown opens. Default true. */
36
+ loadOnOpen?: boolean;
37
+ /** Cache successful pages for this many milliseconds. Set 0 to disable. Default 30000. */
38
+ cacheTtl?: number;
39
+ /** Number of retries after a failed request. Default 0. */
40
+ retry?: number;
41
+ /** Base delay for exponential retry backoff. Default 250ms. */
42
+ retryDelay?: number;
43
+ /** Queries to warm in the background after construction. */
44
+ prefetch?: string[];
29
45
  /**
30
46
  * Opt in to loading additional pages as the user scrolls near the bottom
31
47
  * of the dropdown, instead of only reloading on search. Default false.
@@ -49,6 +65,8 @@ interface ForgeSelectPlugin {
49
65
  onDestroy?(select: ForgeSelect): void;
50
66
  }
51
67
  type TemplateFn = (option: Option) => string | Node;
68
+ type SearchField = "label" | "description" | `meta.${string}`;
69
+ type SearchScorer = (option: Option, query: string, normalizedQuery: string) => number;
52
70
  interface ForgeSelectOptions {
53
71
  placeholder?: string;
54
72
  searchable?: boolean;
@@ -62,28 +80,118 @@ interface ForgeSelectOptions {
62
80
  * behavior and tag markup are unchanged when this is left off.
63
81
  */
64
82
  sortable?: boolean;
83
+ /**
84
+ * Multi-select only: close the dropdown immediately after each pick
85
+ * instead of staying open for further selections. Default false —
86
+ * existing multi-select behavior (stays open) is unchanged.
87
+ */
88
+ closeOnSelect?: boolean;
89
+ /**
90
+ * Multi-select only: caps the number of selected values. Once reached,
91
+ * further picks (including via allowCreate) are ignored until one is
92
+ * removed. Only gates interactive selection — setValue() is not clamped.
93
+ * Default undefined (no limit).
94
+ */
95
+ maxSelections?: number;
65
96
  theme?: string;
66
97
  disabled?: boolean;
98
+ /**
99
+ * Marks the field as required for native form validation. When mounted on
100
+ * a real <select>, an empty selection blocks form submission and shows
101
+ * inline invalid styling, mirroring native <select required> behavior.
102
+ * On a plain-element mount this only sets aria-required (no native form
103
+ * to hook into). Default false.
104
+ */
105
+ required?: boolean;
67
106
  data?: DataItem[];
68
107
  ajax?: AjaxConfig;
69
108
  templateResult?: TemplateFn;
70
109
  templateSelection?: TemplateFn;
110
+ /**
111
+ * Custom match predicate, replacing the built-in label/description
112
+ * substring match. Receives the trimmed (not lowercased) query.
113
+ */
114
+ filterOption?: (option: Option, query: string) => boolean;
115
+ /** Fields used by built-in search. Default: label and description. */
116
+ searchFields?: SearchField[];
117
+ /** Split the query into tokens which may match across fields. Default true. */
118
+ tokenSearch?: boolean;
119
+ /** Match text without case or diacritics. Default true. */
120
+ accentInsensitive?: boolean;
121
+ /** Optional relevance scorer. Values <= 0 exclude an option. */
122
+ searchScorer?: SearchScorer;
123
+ /** Highlight built-in label matches with <mark>. Default false. */
124
+ highlightSearch?: boolean;
125
+ /**
126
+ * Hides results (showing a hint row instead) until the trimmed search
127
+ * query reaches this length. Also delays ajax requests until the
128
+ * threshold is met. Default 0 (no gate).
129
+ */
130
+ minSearchLength?: number;
131
+ /**
132
+ * Hides the search field when a local list contains fewer options than
133
+ * this threshold. AJAX-backed lists always keep search visible. Default 0.
134
+ */
135
+ minResultsForSearch?: number;
136
+ /**
137
+ * Dynamically disables an option, in addition to its static `disabled`
138
+ * field. Re-evaluated on every render, so it can react to external state
139
+ * (e.g. a quota) without rebuilding `data` via setData().
140
+ */
141
+ isOptionDisabled?: (option: Option) => boolean;
71
142
  /**
72
143
  * false = never virtualize. true or unset = virtualize automatically
73
144
  * once the list exceeds ~100 rows.
74
145
  */
75
146
  virtualScroll?: boolean;
76
- /** Row height in px used by the virtual scroller. Default 36; raise for rich items. */
77
- itemHeight?: number;
147
+ /** Row height in px, or "auto" to measure variable-height rows. Default 36. */
148
+ itemHeight?: number | "auto";
78
149
  language?: string | Record<string, string>;
79
150
  plugins?: ForgeSelectPlugin[];
151
+ /**
152
+ * Opens the dropdown when the control receives keyboard focus (e.g. via
153
+ * Tab). Default false — focusing alone still requires Enter/Space/ArrowDown
154
+ * to open, matching existing behavior.
155
+ */
156
+ openOnFocus?: boolean;
157
+ /**
158
+ * Optional portal container for the dropdown, useful inside overflow-hidden
159
+ * modals and drawers. Accepts an element or selector. Default: the root.
160
+ */
161
+ dropdownParent?: HTMLElement | string;
80
162
  }
81
163
  type ForgeSelectValue = string | string[] | null;
82
164
  interface SetValueOptions {
83
165
  /** Emit Forge Select's `change` event after updating. Default true. */
84
166
  emitChange?: boolean;
85
167
  }
86
- type ForgeSelectEvent = "change" | "open" | "close" | "search" | "clear" | "error";
168
+ interface SetSearchQueryOptions {
169
+ /** Emit the `search` event. Default true. */
170
+ emitSearch?: boolean;
171
+ }
172
+ /** Runtime-updateable options. Structural mode/plugin/portal changes still require remounting. */
173
+ type ForgeSelectUpdateOptions = Omit<Partial<ForgeSelectOptions>, "multiple" | "searchable" | "plugins" | "dropdownParent">;
174
+ interface MaximumSelectionEvent {
175
+ limit: number;
176
+ option: Option;
177
+ }
178
+ interface ForgeSelectEventMap {
179
+ change: ForgeSelectValue;
180
+ open: void;
181
+ close: void;
182
+ search: string;
183
+ clear: void;
184
+ error: Error;
185
+ loading: boolean;
186
+ invalid: string;
187
+ select: Option;
188
+ unselect: Option;
189
+ create: Option;
190
+ reorder: string[];
191
+ maximum: MaximumSelectionEvent;
192
+ }
193
+ type ForgeSelectEvent = keyof ForgeSelectEventMap;
194
+ type ForgeSelectEventHandler<E extends ForgeSelectEvent> = ForgeSelectEventMap[E] extends void ? () => void : (payload: ForgeSelectEventMap[E]) => void;
87
195
 
88
196
  declare class ForgeSelect {
89
197
  /** The original element ForgeSelect was mounted on. */
@@ -105,6 +213,7 @@ declare class ForgeSelect {
105
213
  private searchInput;
106
214
  private list;
107
215
  private liveRegion;
216
+ private portalHost;
108
217
  private isOpen;
109
218
  private isDisabled;
110
219
  private destroyed;
@@ -113,6 +222,8 @@ declare class ForgeSelect {
113
222
  private navItems;
114
223
  private highlightedIndex;
115
224
  private rowContentCache;
225
+ private rowHeightCache;
226
+ private searchIndex;
116
227
  private expandedValues;
117
228
  private loading;
118
229
  private loadingMore;
@@ -122,26 +233,66 @@ declare class ForgeSelect {
122
233
  private ajaxRequestId;
123
234
  private ajaxController;
124
235
  private remoteLoaded;
236
+ private remoteCache;
125
237
  private loadError;
126
238
  private originalDisplay;
127
239
  private originalDisabled;
128
240
  private nativeSelect;
129
241
  private nativeForm;
130
242
  private syncingNative;
243
+ /** Combines the static `disabled` field with the dynamic `isOptionDisabled` callback. */
244
+ private isOptionDisabled;
245
+ private pointerDownOnControl;
131
246
  private onDocumentMouseDown;
247
+ private onWindowResize;
248
+ private onAncestorScroll;
249
+ private onNativeInvalid;
132
250
  private onNativeChange;
133
251
  private applyNativeValues;
134
252
  private onFormReset;
135
253
  constructor(target: string | HTMLElement, options?: ForgeSelectOptions);
136
254
  open(): void;
137
255
  close(): void;
256
+ /**
257
+ * Flips the dropdown above the control when there isn't enough room below
258
+ * but there is above. Recomputed on open() and on window resize — the
259
+ * dropdown is positioned absolutely inside the relatively-positioned root,
260
+ * so it already tracks the control correctly on page scroll without
261
+ * needing a scroll listener.
262
+ */
263
+ private positionDropdown;
138
264
  destroy(): void;
139
265
  getValue(): ForgeSelectValue;
266
+ getSearchQuery(): string;
267
+ setSearchQuery(query: string, options?: SetSearchQueryOptions): void;
268
+ isDropdownOpen(): boolean;
269
+ updateOptions(options: ForgeSelectUpdateOptions): void;
270
+ validate(): boolean;
271
+ setCustomValidity(message: string): void;
272
+ reportValidity(): boolean;
273
+ reload(): void;
274
+ clearRemoteCache(): void;
140
275
  setValue(value: ForgeSelectValue, options?: SetValueOptions): void;
276
+ /**
277
+ * Replaces the option list after construction. An open dropdown re-renders
278
+ * immediately; a selection whose value isn't in the new data stays
279
+ * selected (rendered via the already-selected option's own label/avatar,
280
+ * the same fallback used for values selected from a stale ajax page).
281
+ */
282
+ setData(data: DataItem[]): void;
283
+ /**
284
+ * Multi-select only: selects every currently non-disabled option, including
285
+ * nested tree descendants and options inside groups. If `maxSelections` is
286
+ * set, stops once the cap is reached rather than exceeding it. A no-op for
287
+ * single-select.
288
+ */
289
+ selectAll(): void;
290
+ /** Clears every selection. Equivalent to `setValue(null)`. */
291
+ clearAll(): void;
141
292
  enable(): void;
142
293
  disable(): void;
143
- on(event: ForgeSelectEvent, handler: Handler): void;
144
- off(event: ForgeSelectEvent, handler: Handler): void;
294
+ on<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
295
+ off<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
145
296
  /**
146
297
  * The original target (a hidden native <select> or a plain mount div) can
147
298
  * carry an accessible name via aria-label/aria-labelledby, or via a
@@ -151,9 +302,15 @@ declare class ForgeSelect {
151
302
  * interactive `this.control` ourselves.
152
303
  */
153
304
  private applyAccessibleName;
305
+ private shouldShowSearch;
306
+ private updateSearchVisibility;
154
307
  private buildDom;
155
308
  private bindEvents;
309
+ private applySearchQuery;
156
310
  private handleKeydown;
311
+ private canSelectOption;
312
+ private hasReachedMaximum;
313
+ private announceMaximum;
157
314
  private selectValue;
158
315
  private deselectValue;
159
316
  /**
@@ -164,9 +321,13 @@ declare class ForgeSelect {
164
321
  */
165
322
  private syncTreeAncestors;
166
323
  private clearSelection;
324
+ private allSelectableValues;
167
325
  private afterSelectionChange;
168
326
  private syncNativeSelect;
169
327
  private findOption;
328
+ private findOptionByLabel;
329
+ /** Selects an existing option matching `label` exactly, or creates and selects a new one. */
330
+ private createTag;
170
331
  private createFromQuery;
171
332
  private activateNavItem;
172
333
  private renderValue;
@@ -187,6 +348,10 @@ declare class ForgeSelect {
187
348
  private buildRows;
188
349
  private hasExactMatch;
189
350
  private usesVirtualScroll;
351
+ private rowKey;
352
+ private measuredRowHeight;
353
+ private rowOffset;
354
+ private rowOffsets;
190
355
  private renderList;
191
356
  private announceStatus;
192
357
  private renderRows;
@@ -203,6 +368,10 @@ declare class ForgeSelect {
203
368
  private navigateTree;
204
369
  private updateActiveDescendant;
205
370
  private scheduleRemoteLoad;
371
+ private setLoading;
372
+ private remoteCacheKey;
373
+ private requestRemote;
374
+ private prefetchRemote;
206
375
  /**
207
376
  * Fires on every list scroll. Only acts when pagination is opted into via
208
377
  * `ajax.pagination`; reads real scroll geometry rather than row counts so
@@ -212,4 +381,4 @@ declare class ForgeSelect {
212
381
  private loadRemote;
213
382
  }
214
383
 
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 };
384
+ export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectEventHandler, type ForgeSelectEventMap, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectUpdateOptions, type ForgeSelectValue, type MaximumSelectionEvent, type Option, type OptionGroup, type SearchField, type SearchScorer, type SetSearchQueryOptions, 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,10 +22,26 @@ 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;
35
+ /** Load the initial empty query when the dropdown opens. Default true. */
36
+ loadOnOpen?: boolean;
37
+ /** Cache successful pages for this many milliseconds. Set 0 to disable. Default 30000. */
38
+ cacheTtl?: number;
39
+ /** Number of retries after a failed request. Default 0. */
40
+ retry?: number;
41
+ /** Base delay for exponential retry backoff. Default 250ms. */
42
+ retryDelay?: number;
43
+ /** Queries to warm in the background after construction. */
44
+ prefetch?: string[];
29
45
  /**
30
46
  * Opt in to loading additional pages as the user scrolls near the bottom
31
47
  * of the dropdown, instead of only reloading on search. Default false.
@@ -49,6 +65,8 @@ interface ForgeSelectPlugin {
49
65
  onDestroy?(select: ForgeSelect): void;
50
66
  }
51
67
  type TemplateFn = (option: Option) => string | Node;
68
+ type SearchField = "label" | "description" | `meta.${string}`;
69
+ type SearchScorer = (option: Option, query: string, normalizedQuery: string) => number;
52
70
  interface ForgeSelectOptions {
53
71
  placeholder?: string;
54
72
  searchable?: boolean;
@@ -62,28 +80,118 @@ interface ForgeSelectOptions {
62
80
  * behavior and tag markup are unchanged when this is left off.
63
81
  */
64
82
  sortable?: boolean;
83
+ /**
84
+ * Multi-select only: close the dropdown immediately after each pick
85
+ * instead of staying open for further selections. Default false —
86
+ * existing multi-select behavior (stays open) is unchanged.
87
+ */
88
+ closeOnSelect?: boolean;
89
+ /**
90
+ * Multi-select only: caps the number of selected values. Once reached,
91
+ * further picks (including via allowCreate) are ignored until one is
92
+ * removed. Only gates interactive selection — setValue() is not clamped.
93
+ * Default undefined (no limit).
94
+ */
95
+ maxSelections?: number;
65
96
  theme?: string;
66
97
  disabled?: boolean;
98
+ /**
99
+ * Marks the field as required for native form validation. When mounted on
100
+ * a real <select>, an empty selection blocks form submission and shows
101
+ * inline invalid styling, mirroring native <select required> behavior.
102
+ * On a plain-element mount this only sets aria-required (no native form
103
+ * to hook into). Default false.
104
+ */
105
+ required?: boolean;
67
106
  data?: DataItem[];
68
107
  ajax?: AjaxConfig;
69
108
  templateResult?: TemplateFn;
70
109
  templateSelection?: TemplateFn;
110
+ /**
111
+ * Custom match predicate, replacing the built-in label/description
112
+ * substring match. Receives the trimmed (not lowercased) query.
113
+ */
114
+ filterOption?: (option: Option, query: string) => boolean;
115
+ /** Fields used by built-in search. Default: label and description. */
116
+ searchFields?: SearchField[];
117
+ /** Split the query into tokens which may match across fields. Default true. */
118
+ tokenSearch?: boolean;
119
+ /** Match text without case or diacritics. Default true. */
120
+ accentInsensitive?: boolean;
121
+ /** Optional relevance scorer. Values <= 0 exclude an option. */
122
+ searchScorer?: SearchScorer;
123
+ /** Highlight built-in label matches with <mark>. Default false. */
124
+ highlightSearch?: boolean;
125
+ /**
126
+ * Hides results (showing a hint row instead) until the trimmed search
127
+ * query reaches this length. Also delays ajax requests until the
128
+ * threshold is met. Default 0 (no gate).
129
+ */
130
+ minSearchLength?: number;
131
+ /**
132
+ * Hides the search field when a local list contains fewer options than
133
+ * this threshold. AJAX-backed lists always keep search visible. Default 0.
134
+ */
135
+ minResultsForSearch?: number;
136
+ /**
137
+ * Dynamically disables an option, in addition to its static `disabled`
138
+ * field. Re-evaluated on every render, so it can react to external state
139
+ * (e.g. a quota) without rebuilding `data` via setData().
140
+ */
141
+ isOptionDisabled?: (option: Option) => boolean;
71
142
  /**
72
143
  * false = never virtualize. true or unset = virtualize automatically
73
144
  * once the list exceeds ~100 rows.
74
145
  */
75
146
  virtualScroll?: boolean;
76
- /** Row height in px used by the virtual scroller. Default 36; raise for rich items. */
77
- itemHeight?: number;
147
+ /** Row height in px, or "auto" to measure variable-height rows. Default 36. */
148
+ itemHeight?: number | "auto";
78
149
  language?: string | Record<string, string>;
79
150
  plugins?: ForgeSelectPlugin[];
151
+ /**
152
+ * Opens the dropdown when the control receives keyboard focus (e.g. via
153
+ * Tab). Default false — focusing alone still requires Enter/Space/ArrowDown
154
+ * to open, matching existing behavior.
155
+ */
156
+ openOnFocus?: boolean;
157
+ /**
158
+ * Optional portal container for the dropdown, useful inside overflow-hidden
159
+ * modals and drawers. Accepts an element or selector. Default: the root.
160
+ */
161
+ dropdownParent?: HTMLElement | string;
80
162
  }
81
163
  type ForgeSelectValue = string | string[] | null;
82
164
  interface SetValueOptions {
83
165
  /** Emit Forge Select's `change` event after updating. Default true. */
84
166
  emitChange?: boolean;
85
167
  }
86
- type ForgeSelectEvent = "change" | "open" | "close" | "search" | "clear" | "error";
168
+ interface SetSearchQueryOptions {
169
+ /** Emit the `search` event. Default true. */
170
+ emitSearch?: boolean;
171
+ }
172
+ /** Runtime-updateable options. Structural mode/plugin/portal changes still require remounting. */
173
+ type ForgeSelectUpdateOptions = Omit<Partial<ForgeSelectOptions>, "multiple" | "searchable" | "plugins" | "dropdownParent">;
174
+ interface MaximumSelectionEvent {
175
+ limit: number;
176
+ option: Option;
177
+ }
178
+ interface ForgeSelectEventMap {
179
+ change: ForgeSelectValue;
180
+ open: void;
181
+ close: void;
182
+ search: string;
183
+ clear: void;
184
+ error: Error;
185
+ loading: boolean;
186
+ invalid: string;
187
+ select: Option;
188
+ unselect: Option;
189
+ create: Option;
190
+ reorder: string[];
191
+ maximum: MaximumSelectionEvent;
192
+ }
193
+ type ForgeSelectEvent = keyof ForgeSelectEventMap;
194
+ type ForgeSelectEventHandler<E extends ForgeSelectEvent> = ForgeSelectEventMap[E] extends void ? () => void : (payload: ForgeSelectEventMap[E]) => void;
87
195
 
88
196
  declare class ForgeSelect {
89
197
  /** The original element ForgeSelect was mounted on. */
@@ -105,6 +213,7 @@ declare class ForgeSelect {
105
213
  private searchInput;
106
214
  private list;
107
215
  private liveRegion;
216
+ private portalHost;
108
217
  private isOpen;
109
218
  private isDisabled;
110
219
  private destroyed;
@@ -113,6 +222,8 @@ declare class ForgeSelect {
113
222
  private navItems;
114
223
  private highlightedIndex;
115
224
  private rowContentCache;
225
+ private rowHeightCache;
226
+ private searchIndex;
116
227
  private expandedValues;
117
228
  private loading;
118
229
  private loadingMore;
@@ -122,26 +233,66 @@ declare class ForgeSelect {
122
233
  private ajaxRequestId;
123
234
  private ajaxController;
124
235
  private remoteLoaded;
236
+ private remoteCache;
125
237
  private loadError;
126
238
  private originalDisplay;
127
239
  private originalDisabled;
128
240
  private nativeSelect;
129
241
  private nativeForm;
130
242
  private syncingNative;
243
+ /** Combines the static `disabled` field with the dynamic `isOptionDisabled` callback. */
244
+ private isOptionDisabled;
245
+ private pointerDownOnControl;
131
246
  private onDocumentMouseDown;
247
+ private onWindowResize;
248
+ private onAncestorScroll;
249
+ private onNativeInvalid;
132
250
  private onNativeChange;
133
251
  private applyNativeValues;
134
252
  private onFormReset;
135
253
  constructor(target: string | HTMLElement, options?: ForgeSelectOptions);
136
254
  open(): void;
137
255
  close(): void;
256
+ /**
257
+ * Flips the dropdown above the control when there isn't enough room below
258
+ * but there is above. Recomputed on open() and on window resize — the
259
+ * dropdown is positioned absolutely inside the relatively-positioned root,
260
+ * so it already tracks the control correctly on page scroll without
261
+ * needing a scroll listener.
262
+ */
263
+ private positionDropdown;
138
264
  destroy(): void;
139
265
  getValue(): ForgeSelectValue;
266
+ getSearchQuery(): string;
267
+ setSearchQuery(query: string, options?: SetSearchQueryOptions): void;
268
+ isDropdownOpen(): boolean;
269
+ updateOptions(options: ForgeSelectUpdateOptions): void;
270
+ validate(): boolean;
271
+ setCustomValidity(message: string): void;
272
+ reportValidity(): boolean;
273
+ reload(): void;
274
+ clearRemoteCache(): void;
140
275
  setValue(value: ForgeSelectValue, options?: SetValueOptions): void;
276
+ /**
277
+ * Replaces the option list after construction. An open dropdown re-renders
278
+ * immediately; a selection whose value isn't in the new data stays
279
+ * selected (rendered via the already-selected option's own label/avatar,
280
+ * the same fallback used for values selected from a stale ajax page).
281
+ */
282
+ setData(data: DataItem[]): void;
283
+ /**
284
+ * Multi-select only: selects every currently non-disabled option, including
285
+ * nested tree descendants and options inside groups. If `maxSelections` is
286
+ * set, stops once the cap is reached rather than exceeding it. A no-op for
287
+ * single-select.
288
+ */
289
+ selectAll(): void;
290
+ /** Clears every selection. Equivalent to `setValue(null)`. */
291
+ clearAll(): void;
141
292
  enable(): void;
142
293
  disable(): void;
143
- on(event: ForgeSelectEvent, handler: Handler): void;
144
- off(event: ForgeSelectEvent, handler: Handler): void;
294
+ on<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
295
+ off<E extends ForgeSelectEvent>(event: E, handler: ForgeSelectEventHandler<E>): void;
145
296
  /**
146
297
  * The original target (a hidden native <select> or a plain mount div) can
147
298
  * carry an accessible name via aria-label/aria-labelledby, or via a
@@ -151,9 +302,15 @@ declare class ForgeSelect {
151
302
  * interactive `this.control` ourselves.
152
303
  */
153
304
  private applyAccessibleName;
305
+ private shouldShowSearch;
306
+ private updateSearchVisibility;
154
307
  private buildDom;
155
308
  private bindEvents;
309
+ private applySearchQuery;
156
310
  private handleKeydown;
311
+ private canSelectOption;
312
+ private hasReachedMaximum;
313
+ private announceMaximum;
157
314
  private selectValue;
158
315
  private deselectValue;
159
316
  /**
@@ -164,9 +321,13 @@ declare class ForgeSelect {
164
321
  */
165
322
  private syncTreeAncestors;
166
323
  private clearSelection;
324
+ private allSelectableValues;
167
325
  private afterSelectionChange;
168
326
  private syncNativeSelect;
169
327
  private findOption;
328
+ private findOptionByLabel;
329
+ /** Selects an existing option matching `label` exactly, or creates and selects a new one. */
330
+ private createTag;
170
331
  private createFromQuery;
171
332
  private activateNavItem;
172
333
  private renderValue;
@@ -187,6 +348,10 @@ declare class ForgeSelect {
187
348
  private buildRows;
188
349
  private hasExactMatch;
189
350
  private usesVirtualScroll;
351
+ private rowKey;
352
+ private measuredRowHeight;
353
+ private rowOffset;
354
+ private rowOffsets;
190
355
  private renderList;
191
356
  private announceStatus;
192
357
  private renderRows;
@@ -203,6 +368,10 @@ declare class ForgeSelect {
203
368
  private navigateTree;
204
369
  private updateActiveDescendant;
205
370
  private scheduleRemoteLoad;
371
+ private setLoading;
372
+ private remoteCacheKey;
373
+ private requestRemote;
374
+ private prefetchRemote;
206
375
  /**
207
376
  * Fires on every list scroll. Only acts when pagination is opted into via
208
377
  * `ajax.pagination`; reads real scroll geometry rather than row counts so
@@ -212,4 +381,4 @@ declare class ForgeSelect {
212
381
  private loadRemote;
213
382
  }
214
383
 
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 };
384
+ export { type AjaxConfig, type DataItem, ForgeSelect, type ForgeSelectEvent, type ForgeSelectEventHandler, type ForgeSelectEventMap, type ForgeSelectOptions, type ForgeSelectPlugin, type ForgeSelectUpdateOptions, type ForgeSelectValue, type MaximumSelectionEvent, type Option, type OptionGroup, type SearchField, type SearchScorer, type SetSearchQueryOptions, type SetValueOptions, type TemplateFn, ForgeSelect as default };