@astryxdesign/core 0.5.0-canary.4e1b670 → 0.5.0-canary.51aa5eb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../src/TextArea/TextArea.tsx"],"names":[],"mappings":"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"}
@@ -317,13 +317,13 @@ export function TextArea({
317
317
  "aria-required": isEffectivelyRequired ? 'true' : undefined,
318
318
  "aria-invalid": status?.type === 'error' || maxLength != null && valueLength > maxLength ? 'true' : undefined,
319
319
  "aria-busy": isBusy || undefined,
320
- ...stylex.props(styles.textarea, textareaSizeStyles[size], isDisabled && styles.textareaDisabled, Boolean(startIcon) && styles.textareaWithStartIcon,
320
+ ...mergeProps(themeProps('text-area-control'), stylex.props(styles.textarea, textareaSizeStyles[size], isDisabled && styles.textareaDisabled, Boolean(startIcon) && styles.textareaWithStartIcon,
321
321
  // Reserve trailing space only when the end slot actually renders
322
322
  // something (spinner or on-field status icon). The `detached`
323
- // status variant suppresses the on-field icon — its glyph lives in
324
- // the message box below — so reserving here would inset the text
325
- // for an icon that never appears.
326
- (isBusy || statusIcon != null) && styles.textareaWithStatus, isBusy && statusIcon != null && styles.textareaWithBusyStatus, maxLength != null && styles.textareaWithCounter)
323
+ // status variant suppresses the on-field icon — its glyph lives
324
+ // in the message box below — so reserving here would inset the
325
+ // text for an icon that never appears.
326
+ (isBusy || statusIcon != null) && styles.textareaWithStatus, isBusy && statusIcon != null && styles.textareaWithBusyStatus, maxLength != null && styles.textareaWithCounter))
327
327
  }), (isBusy || statusIcon) && /*#__PURE__*/_jsxs("span", {
328
328
  ...{
329
329
  className: "x10l6tqk xctzyg x13oyzgk x47corl x78zum5 x6s0dn4 x1txdalj"
@@ -333,14 +333,14 @@ export function TextArea({
333
333
  }), statusIcon]
334
334
  }), maxLength != null && /*#__PURE__*/_jsxs("div", {
335
335
  id: counterID,
336
- ...{
336
+ ...mergeProps(themeProps('text-area-counter'), {
337
337
  0: {
338
338
  className: "x10l6tqk x112dmjn x13oyzgk x47corl x78zum5 x6s0dn4 xzye2dw x9ynric x141an7d x1ltkj2j xv1l7n4"
339
339
  },
340
340
  1: {
341
341
  className: "x10l6tqk x112dmjn x13oyzgk x47corl x78zum5 x6s0dn4 xzye2dw x9ynric x141an7d x1ltkj2j xjt36v0"
342
342
  }
343
- }[!!(valueLength > maxLength) << 0],
343
+ }[!!(valueLength > maxLength) << 0]),
344
344
  children: [valueLength > maxLength &&
345
345
  /*#__PURE__*/
346
346
  // Non-color cue so the over-limit state isn't conveyed by the red
@@ -107,6 +107,17 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<BaseProps
107
107
  maxMenuItems?: number;
108
108
  /** Fixed dropdown width in pixels. Never shrinks below the input width. */
109
109
  menuWidth?: number;
110
+ /**
111
+ * Minimum query length before the search source is queried. Below it no
112
+ * search runs and the menu stays closed — useful for remote sources where
113
+ * one or two characters match too much to be worth fetching.
114
+ *
115
+ * With `hasCreate`, the "Create" entry rides on the search results, so it
116
+ * also waits for the threshold.
117
+ *
118
+ * @default 1
119
+ */
120
+ minQueryLength?: number;
110
121
  /** Text shown when no results found. @default 'No results found' */
111
122
  emptySearchResultsText?: string;
112
123
  /** Whether the input is disabled. @default false */
@@ -203,7 +214,7 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<BaseProps
203
214
  * />
204
215
  * ```
205
216
  */
206
- export declare function Tokenizer<T extends SearchableItem>({ label, isLabelHidden, description, isRequired, isOptional, status, statusVariant, startIcon, labelTooltip, searchSource, value, onChange, renderItem, renderToken, maxEntries, placeholder, hasEntriesOnFocus, maxMenuItems, menuWidth, emptySearchResultsText, isDisabled, htmlName, disabledMessage, hasClear, endContent, hasAutoFocus, size: sizeProp, tokenOverflowBehavior, debounceMs, hasCreate, onChangeQuery, onFocus, onBlur, width, xstyle, className, style, 'data-testid': testId, ref, handleRef, }: TokenizerProps<T>): React.JSX.Element;
217
+ export declare function Tokenizer<T extends SearchableItem>({ label, isLabelHidden, description, isRequired, isOptional, status, statusVariant, startIcon, labelTooltip, searchSource, value, onChange, renderItem, renderToken, maxEntries, placeholder, hasEntriesOnFocus, maxMenuItems, menuWidth, minQueryLength, emptySearchResultsText, isDisabled, htmlName, disabledMessage, hasClear, endContent, hasAutoFocus, size: sizeProp, tokenOverflowBehavior, debounceMs, hasCreate, onChangeQuery, onFocus, onBlur, width, xstyle, className, style, 'data-testid': testId, ref, handleRef, }: TokenizerProps<T>): React.JSX.Element;
207
218
  export declare namespace Tokenizer {
208
219
  var displayName: string;
209
220
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Tokenizer.d.ts","sourceRoot":"","sources":["../../src/Tokenizer/Tokenizer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Tokenizer.d.ts","sourceRoot":"","sources":["../../src/Tokenizer/Tokenizer.tsx"],"names":[],"mappings":"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"}
@@ -237,6 +237,7 @@ export function Tokenizer({
237
237
  hasEntriesOnFocus,
238
238
  maxMenuItems,
239
239
  menuWidth,
240
+ minQueryLength,
240
241
  emptySearchResultsText,
241
242
  isDisabled = false,
242
243
  htmlName,
@@ -357,32 +358,39 @@ export function Tokenizer({
357
358
  const filteredSource = useMemo(() => ({
358
359
  search: async query => {
359
360
  const results = await searchSource.search(query);
360
- const filtered = results.filter(item => !selectedIds.has(item.id));
361
-
362
- // Append a "Create: X" synthetic item when hasCreate is true,
363
- // the user has typed something, and it doesn't exactly match an
364
- // existing result.
365
- if (hasCreate && query.trim()) {
366
- const trimmed = query.trim();
367
- const alreadyExists = selectedIds.has(trimmed) || filtered.some(item => item.label.toLowerCase() === trimmed.toLowerCase());
368
- if (!alreadyExists) {
369
- const creatableItem = {
370
- id: `${CREATABLE_ID_PREFIX}${trimmed}`,
371
- label: `Create "${trimmed}"`,
372
- auxiliaryData: {
373
- __createdValue: trimmed
374
- }
375
- };
376
- filtered.push(creatableItem);
377
- }
378
- }
379
- return filtered;
361
+ return results.filter(item => !selectedIds.has(item.id));
380
362
  },
381
363
  bootstrap: async () => {
382
364
  const results = await searchSource.bootstrap();
383
365
  return results.filter(item => !selectedIds.has(item.id));
384
366
  }
385
- }), [searchSource, selectedIds, hasCreate]);
367
+ }), [searchSource, selectedIds]);
368
+
369
+ /**
370
+ * The "Create X" entry. It is derived from the typed text, not fetched for
371
+ * it, so it is offered through `__queryEntries` rather than appended to the
372
+ * search results — which is what keeps it available when the query is too
373
+ * short to search. `minQueryLength` is there to avoid a fetch too broad to
374
+ * be worth making; creating `QA` costs no fetch, and a field that can
375
+ * create it should not stop being able to.
376
+ */
377
+ const createEntries = useCallback((query, results) => {
378
+ const trimmed = query.trim();
379
+ if (!hasCreate || trimmed === '') {
380
+ return [];
381
+ }
382
+ const alreadyExists = selectedIds.has(trimmed) || results.some(item => item.label.toLowerCase() === trimmed.toLowerCase());
383
+ if (alreadyExists) {
384
+ return [];
385
+ }
386
+ return [{
387
+ id: `${CREATABLE_ID_PREFIX}${trimmed}`,
388
+ label: `Create "${trimmed}"`,
389
+ auxiliaryData: {
390
+ __createdValue: trimmed
391
+ }
392
+ }];
393
+ }, [hasCreate, selectedIds]);
386
394
  const emptySource = useMemo(() => ({
387
395
  search: async () => [],
388
396
  bootstrap: async () => []
@@ -571,6 +579,7 @@ export function Tokenizer({
571
579
  hasEntriesOnFocus: isAtMax ? false : hasEntriesOnFocus,
572
580
  maxMenuItems: maxMenuItems,
573
581
  menuWidth: menuWidth,
582
+ minQueryLength: minQueryLength,
574
583
  emptySearchResultsText: emptySearchResultsText,
575
584
  isDisabled: isDisabled,
576
585
  isFocusableDisabled: showsDisabledMessage,
@@ -578,6 +587,7 @@ export function Tokenizer({
578
587
  inputId: inputId,
579
588
  ariaDescribedBy: ariaDescribedBy,
580
589
  onChangeQuery: onChangeQuery,
590
+ __queryEntries: createEntries,
581
591
  debounceMs: debounceMs,
582
592
  onKeyDown: handleKeyDown,
583
593
  anchorRef: wrapperRef,
@@ -50,6 +50,18 @@ export interface BaseTypeaheadProps<T extends SearchableItem> extends Omit<BaseP
50
50
  maxMenuItems?: number;
51
51
  /** Fixed dropdown width in pixels. Never shrinks below the anchor width. */
52
52
  menuWidth?: number;
53
+ /**
54
+ * Minimum query length before the search source is queried. Below it no
55
+ * search runs and the menu stays closed, so a remote source is not asked
56
+ * for a result set that cannot be meaningful yet — and the user does not
57
+ * see "no results" for a query that was never searched.
58
+ *
59
+ * Measured with `String.length` (UTF-16 code units), like every other
60
+ * length check in the library.
61
+ *
62
+ * @default 1 — every non-empty query is searched.
63
+ */
64
+ minQueryLength?: number;
53
65
  /**
54
66
  * Text shown when no results found.
55
67
  * @default 'No results found'
@@ -82,6 +94,27 @@ export interface BaseTypeaheadProps<T extends SearchableItem> extends Omit<BaseP
82
94
  * Callback when dropdown opens/closes.
83
95
  */
84
96
  onOpenChange?: (isOpen: boolean) => void;
97
+ /**
98
+ * Entries derived from the query text rather than fetched for it — today,
99
+ * Tokenizer's "Create ...".
100
+ *
101
+ * They are appended to whatever the search returned, and they are offered
102
+ * whatever `minQueryLength` says: that threshold exists to avoid a fetch
103
+ * that is too broad to be worth making, and these cost no fetch. A field
104
+ * that can create `QA` should not stop being able to just because a search
105
+ * for `QA` would match too much.
106
+ *
107
+ * Receives the results they will be appended to, so a caller can decline to
108
+ * offer an entry that duplicates one.
109
+ *
110
+ * Underscored and `@internal`: `BaseTypeaheadProps` is re-exported from the
111
+ * package entry point, so anything named on it ships as public API at the
112
+ * next cut. This is a wiring detail between Tokenizer and the base — the
113
+ * same reason `DefinedTheme.__inputTokens` carries its prefix.
114
+ *
115
+ * @internal
116
+ */
117
+ __queryEntries?: (query: string, results: T[]) => T[];
85
118
  /**
86
119
  * Debounce delay in ms before triggering search after typing.
87
120
  * Set to 0 for synchronous/local search sources that don't need debouncing.
@@ -1 +1 @@
1
- {"version":3,"file":"BaseTypeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/BaseTypeahead.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAenD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,SAAS,CAAC;AAS1D,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,cAAc,CAAE,SAAQ,IAAI,CACxE,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IAEpC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;IAE3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAE/D;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAkHD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,EA6lBrB,CAAC,CAAC,SAAS,cAAc,EAC5B,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KACzB,KAAK,CAAC,YAAY,CAAC"}
1
+ {"version":3,"file":"BaseTypeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/BaseTypeahead.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAenD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,cAAc,EAAE,YAAY,EAAC,MAAM,SAAS,CAAC;AAS1D,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,cAAc,CAAE,SAAQ,IAAI,CACxE,SAAS,CAAC,WAAW,CAAC,EACtB,UAAU,CACX;IACC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IAEpC;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExC;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAEtD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,WAAW,CAAC,EAAE,YAAY,CAAC;IAE3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE1C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAE/D;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AA+HD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,EAuoBrB,CAAC,CAAC,SAAS,cAAc,EAC5B,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KACzB,KAAK,CAAC,YAAY,CAAC"}
@@ -113,6 +113,19 @@ const itemSizeStyles = {
113
113
  }
114
114
  };
115
115
 
116
+ // =============================================================================
117
+ // Helpers
118
+ // =============================================================================
119
+
120
+ /**
121
+ * A query that has been typed but is still shorter than the caller's
122
+ * threshold. An empty query is not "below the minimum" — it is the
123
+ * untouched state, and `hasEntriesOnFocus` owns what happens there.
124
+ */
125
+ function isBelowMinQueryLength(query, minQueryLength) {
126
+ return query.length > 0 && query.length < minQueryLength;
127
+ }
128
+
116
129
  // =============================================================================
117
130
  // Component
118
131
  // =============================================================================
@@ -145,12 +158,14 @@ export const BaseTypeahead = function BaseTypeahead({
145
158
  hasEntriesOnFocus = false,
146
159
  maxMenuItems = 10,
147
160
  menuWidth,
161
+ minQueryLength = 1,
148
162
  emptySearchResultsText: emptySearchResultsTextFromProps,
149
163
  isDisabled = false,
150
164
  isFocusableDisabled = false,
151
165
  hasAutoFocus = false,
152
166
  onChangeQuery,
153
167
  onOpenChange,
168
+ __queryEntries,
154
169
  inputId: externalInputId,
155
170
  ariaDescribedBy,
156
171
  ariaLabelledBy,
@@ -259,7 +274,8 @@ export const BaseTypeahead = function BaseTypeahead({
259
274
  return;
260
275
  }
261
276
  resultsGenRef.current = gen;
262
- const shown = searchResults.slice(0, maxMenuItems);
277
+ const fetched = searchResults.slice(0, maxMenuItems);
278
+ const shown = [...fetched, ...(__queryEntries?.(searchQuery, fetched) ?? [])];
263
279
  setResults(shown);
264
280
  setHighlightedIndex(shown.length > 0 ? 0 : -1);
265
281
  if (searchResults.length > 0 || searchQuery.length > 0) {
@@ -283,7 +299,7 @@ export const BaseTypeahead = function BaseTypeahead({
283
299
  setIsLoading(false);
284
300
  }
285
301
  }
286
- }, [searchSource, maxMenuItems, showLayer, announce, emptySearchResultsText, t]);
302
+ }, [searchSource, maxMenuItems, showLayer, announce, emptySearchResultsText, __queryEntries, t]);
287
303
 
288
304
  // Perform bootstrap
289
305
  const performBootstrap = useCallback(async () => {
@@ -320,14 +336,33 @@ export const BaseTypeahead = function BaseTypeahead({
320
336
  if (searchTimeoutRef.current) {
321
337
  clearTimeout(searchTimeoutRef.current);
322
338
  }
323
- if (newQuery.length === 0 && !hasEntriesOnFocus) {
339
+
340
+ // Nothing to search: either the field was emptied, or the query is
341
+ // still shorter than `minQueryLength`. Both drop stale results and
342
+ // close the menu — showing the empty state for a query that was never
343
+ // searched would report "no results" that nobody looked for.
344
+ if (newQuery.length === 0 && !hasEntriesOnFocus || isBelowMinQueryLength(newQuery, minQueryLength)) {
324
345
  searchGenRef.current++;
325
346
  searchSource.cancel?.();
326
- setResults([]);
347
+ // A query too short to search can still carry entries derived from
348
+ // the text itself. `hasSearched` stays false either way, so the menu
349
+ // never reports "no results" for a query nobody looked for.
350
+ const derived = __queryEntries?.(newQuery, []) ?? [];
351
+ setResults(derived);
352
+ setHighlightedIndex(derived.length > 0 ? 0 : -1);
327
353
  setHasSearched(false);
354
+ // Bumping the generation abandons any in-flight search, which means
355
+ // its own `finally` will decline to clear this — so clear it here or
356
+ // the field spins forever. Backspacing below the threshold on a remote
357
+ // source is the everyday way to hit that.
358
+ setIsLoading(false);
328
359
  // Clear any lingering result-count / no-results announcement.
329
360
  announce('');
330
- popover.hide();
361
+ if (derived.length > 0) {
362
+ showLayer();
363
+ } else {
364
+ popover.hide();
365
+ }
331
366
  return;
332
367
  }
333
368
  const triggerSearch = () => {
@@ -342,7 +377,7 @@ export const BaseTypeahead = function BaseTypeahead({
342
377
  } else {
343
378
  searchTimeoutRef.current = setTimeout(triggerSearch, debounceMs);
344
379
  }
345
- }, [onChangeQuery, hasEntriesOnFocus, performSearch, performBootstrap, popover, debounceMs, searchSource, announce]);
380
+ }, [onChangeQuery, hasEntriesOnFocus, minQueryLength, __queryEntries, showLayer, performSearch, performBootstrap, popover, debounceMs, searchSource, announce]);
346
381
 
347
382
  // Handle input change
348
383
  const handleInputChange = useCallback(e => {
@@ -362,6 +397,10 @@ export const BaseTypeahead = function BaseTypeahead({
362
397
  setQuery('');
363
398
  setResults([]);
364
399
  setHasSearched(false);
400
+ // Same reason as in handleQueryChange: the invalidated search will not
401
+ // clear this itself. Selecting a stale result while the next search is
402
+ // still in flight would otherwise leave the field spinning.
403
+ setIsLoading(false);
365
404
  popover.hide();
366
405
  inputRef.current?.focus();
367
406
  }, [onChange, popover, searchSource]);
@@ -428,7 +467,11 @@ export const BaseTypeahead = function BaseTypeahead({
428
467
  if (results.length > 0) {
429
468
  popover.show();
430
469
  setHighlightedIndex(0);
431
- } else if (hasEntriesOnFocus) {
470
+ } else if (hasEntriesOnFocus &&
471
+ // A below-threshold query was never searched; falling back to the
472
+ // bootstrap entries here would open a menu of suggestions that
473
+ // ignore what the user has already typed.
474
+ !isBelowMinQueryLength(query, minQueryLength)) {
432
475
  void performBootstrap();
433
476
  }
434
477
  }
@@ -482,7 +525,7 @@ export const BaseTypeahead = function BaseTypeahead({
482
525
  }
483
526
  break;
484
527
  }
485
- }, [popover, results, highlightedIndex, handleSelect, hasEntriesOnFocus, query.length, performBootstrap, externalOnKeyDown]);
528
+ }, [popover, results, highlightedIndex, handleSelect, hasEntriesOnFocus, query, minQueryLength, performBootstrap, externalOnKeyDown]);
486
529
 
487
530
  // Generate item ID for accessibility
488
531
  const getItemId = useCallback(index => `${listboxId}-option-${index}`, [listboxId]);
@@ -69,6 +69,13 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<BaseProps
69
69
  hasEntriesOnFocus?: boolean;
70
70
  /** Max dropdown items. @default 10 */
71
71
  maxMenuItems?: number;
72
+ /**
73
+ * Minimum query length before the search source is queried. Below it no
74
+ * search runs and the menu stays closed — useful for remote sources where
75
+ * one or two characters match too much to be worth fetching.
76
+ * @default 1
77
+ */
78
+ minQueryLength?: number;
72
79
  /** Text shown when no results found. @default 'No results found' */
73
80
  emptySearchResultsText?: string;
74
81
  /** Whether the input is disabled. @default false */
@@ -134,7 +141,7 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<BaseProps
134
141
  * />
135
142
  * ```
136
143
  */
137
- export declare function Typeahead<T extends SearchableItem>({ ref, label, isLabelHidden, description, isRequired, isOptional, status, statusVariant, startIcon, labelTooltip, searchSource, value, onChange, renderItem, placeholder, hasEntriesOnFocus, maxMenuItems, emptySearchResultsText, isDisabled, disabledMessage, hasClear, hasAutoFocus, size: sizeProp, debounceMs, onChangeQuery, onOpenChange, width, xstyle, className, style, 'data-testid': testId, }: TypeaheadProps<T>): React.JSX.Element;
144
+ export declare function Typeahead<T extends SearchableItem>({ ref, label, isLabelHidden, description, isRequired, isOptional, status, statusVariant, startIcon, labelTooltip, searchSource, value, onChange, renderItem, placeholder, hasEntriesOnFocus, maxMenuItems, minQueryLength, emptySearchResultsText, isDisabled, disabledMessage, hasClear, hasAutoFocus, size: sizeProp, debounceMs, onChangeQuery, onOpenChange, width, xstyle, className, style, 'data-testid': testId, }: TypeaheadProps<T>): React.JSX.Element;
138
145
  export declare namespace Typeahead {
139
146
  var displayName: string;
140
147
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/Typeahead.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../src/Typeahead/Typeahead.tsx"],"names":[],"mappings":"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"}
@@ -132,6 +132,7 @@ export function Typeahead({
132
132
  placeholder,
133
133
  hasEntriesOnFocus,
134
134
  maxMenuItems,
135
+ minQueryLength,
135
136
  emptySearchResultsText,
136
137
  isDisabled = false,
137
138
  disabledMessage,
@@ -312,6 +313,7 @@ export function Typeahead({
312
313
  placeholder: showToken ? undefined : placeholder,
313
314
  hasEntriesOnFocus: hasEntriesOnFocus,
314
315
  maxMenuItems: maxMenuItems,
316
+ minQueryLength: minQueryLength,
315
317
  emptySearchResultsText: emptySearchResultsText,
316
318
  isDisabled: isDisabled,
317
319
  hasAutoFocus: hasAutoFocus,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.5.0-canary.4e1b670",
3
+ "version": "0.5.0-canary.51aa5eb",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -661,7 +661,7 @@
661
661
  "react-dom": ">=19.0.0"
662
662
  },
663
663
  "devDependencies": {
664
- "@astryxdesign/cli": "0.5.0-canary.4e1b670",
664
+ "@astryxdesign/cli": "0.5.0-canary.51aa5eb",
665
665
  "@babel/cli": "^8.0.4",
666
666
  "@babel/core": "^7.29.7",
667
667
  "@babel/preset-react": "^8.0.1",
@@ -109,7 +109,7 @@ export const docs = {
109
109
  {
110
110
  name: 'children',
111
111
  type: 'ReactNode',
112
- description: 'Children mode: compose the table yourself from TableHeader / TableBody / TableFooter, each holding TableRow and TableCell, instead of using data-driven rendering. The children are passed straight to the <table>, so the section is yours to supply — a TableRow placed directly in Table emits <table><tr>, which is invalid HTML and mismatches on hydration (the parser inserts an implied <tbody> for server-rendered markup; React does not on the client). Data-driven mode renders the sections for you.',
112
+ description: 'Children mode: compose the table yourself from TableHeader / TableBody / TableFooter, each holding TableRow and TableCell, instead of using data-driven rendering. The children are passed straight to the <table>, so the section is yours to supply. A TableRow placed directly in Table emits <table><tr>, which is invalid HTML and mismatches on hydration (the parser inserts an implied <tbody> for server-rendered markup; React does not on the client). Data-driven mode renders the sections for you.',
113
113
  },
114
114
  {
115
115
  name: 'xstyle',
@@ -189,7 +189,7 @@ export const docsDense = {
189
189
  bestPractices: [
190
190
  { guidance: true, description: 'Use density and divider variants to match the information density and scanning needs of your data.' },
191
191
  { guidance: true, description: 'Compose rich cell content with Astryx components like Badge, StatusDot, and Avatar via renderCell.' },
192
- { guidance: true, description: 'Children mode: wrap rows in TableHeader/TableBody/TableFooter. <table> cannot hold a <tr> directly — parser adds an implied <tbody> for SSR markup, React does not on the client, so unwrapped rows mismatch on hydration.' },
192
+ { guidance: true, description: 'Children mode: wrap rows in TableHeader/TableBody/TableFooter. <table> cannot hold a <tr> directly; the parser adds an implied <tbody> for SSR markup, React does not on the client, so unwrapped rows mismatch on hydration.' },
193
193
  { guidance: true, description: 'Set explicit width on every column via proportional() or pixel(). proportional(1) = equal flex w/ 120px min preventing collapse on narrow viewports. Omitting width skips the minimum.' },
194
194
  { guidance: true, description: 'Data-driven API is RSC-safe: proportional(), pixel(), column defs w/o function props work in Server Components. renderCell (any function prop) requires a "use client" wrapper.' },
195
195
  { guidance: false, description: 'Use a table for data without consistent columns. Use a list or card layout for heterogeneous content.' },
@@ -8,7 +8,7 @@ export const docs = {
8
8
  displayName: 'Table Footer',
9
9
  isHiddenFromOverview: true,
10
10
  description:
11
- '<tfoot> wrapper for children mode. Holds summary or total rows beneath the body. A row must sit inside a section — <table> cannot contain a <tr> directly, because the HTML parser inserts an implied <tbody> when it parses server-rendered markup and React does not when it renders on the client, so the two trees mismatch on hydration. Children mode only: the data-driven data={...} path renders a header and a body, never a footer.',
11
+ '<tfoot> wrapper for children mode. Holds summary or total rows beneath the body. A row must sit inside a section: <table> cannot contain a <tr> directly, because the HTML parser inserts an implied <tbody> when it parses server-rendered markup and React does not when it renders on the client, so the two trees mismatch on hydration. Children mode only: the data-driven data={...} path renders a header and a body, never a footer.',
12
12
  props: [
13
13
  {
14
14
  name: 'children',
@@ -65,7 +65,7 @@ export const docs = {
65
65
  name: 'hasRowHighlight',
66
66
  type: 'boolean',
67
67
  description:
68
- 'Paints checked rows with the accent wash. Set false when the surrounding UI already uses row background to mean something else (a row open in a detail panel, say) — the wash is an inline style, so it cannot be overridden from userland. Only the background is dropped: aria-selected is still set on checked rows either way.',
68
+ 'Paints checked rows with the accent wash. Set false when the surrounding UI already uses row background to mean something else (a row open in a detail panel, say). The wash is an inline style, so it cannot be overridden from userland. Only the background is dropped: aria-selected is still set on checked rows either way.',
69
69
  default: 'true',
70
70
  },
71
71
  ],
@@ -187,6 +187,8 @@ export const docs = {
187
187
  theming: {
188
188
  targets: [
189
189
  {className: 'astryx-text-area', visualProps: ['size', 'status'], states: ['disabled', 'readonly']},
190
+ {className: 'astryx-text-area-control'},
191
+ {className: 'astryx-text-area-counter'},
190
192
  // Still emitted beside the names above, so themes written against
191
193
  // them keep working. Drop in the next major.
192
194
  {className: 'astryx-textarea', visualProps: ['size', 'status'], states: ['disabled', 'readonly'], deprecatedFor: 'text-area'},
@@ -385,6 +387,8 @@ export const docsZh = {
385
387
  theming: {
386
388
  targets: [
387
389
  {className: 'astryx-text-area', visualProps: ['size', 'status'], states: ['disabled', 'readonly']},
390
+ {className: 'astryx-text-area-control'},
391
+ {className: 'astryx-text-area-counter'},
388
392
  // Still emitted beside the names above, so themes written against
389
393
  // them keep working. Drop in the next major.
390
394
  {className: 'astryx-textarea', visualProps: ['size', 'status'], states: ['disabled', 'readonly'], deprecatedFor: 'text-area'},
@@ -615,19 +615,22 @@ export function TextArea({
615
615
  : undefined
616
616
  }
617
617
  aria-busy={isBusy || undefined}
618
- {...stylex.props(
619
- styles.textarea,
620
- textareaSizeStyles[size],
621
- isDisabled && styles.textareaDisabled,
622
- Boolean(startIcon) && styles.textareaWithStartIcon,
623
- // Reserve trailing space only when the end slot actually renders
624
- // something (spinner or on-field status icon). The `detached`
625
- // status variant suppresses the on-field icon — its glyph lives in
626
- // the message box below — so reserving here would inset the text
627
- // for an icon that never appears.
628
- (isBusy || statusIcon != null) && styles.textareaWithStatus,
629
- isBusy && statusIcon != null && styles.textareaWithBusyStatus,
630
- maxLength != null && styles.textareaWithCounter,
618
+ {...mergeProps(
619
+ themeProps('text-area-control'),
620
+ stylex.props(
621
+ styles.textarea,
622
+ textareaSizeStyles[size],
623
+ isDisabled && styles.textareaDisabled,
624
+ Boolean(startIcon) && styles.textareaWithStartIcon,
625
+ // Reserve trailing space only when the end slot actually renders
626
+ // something (spinner or on-field status icon). The `detached`
627
+ // status variant suppresses the on-field icon — its glyph lives
628
+ // in the message box below — so reserving here would inset the
629
+ // text for an icon that never appears.
630
+ (isBusy || statusIcon != null) && styles.textareaWithStatus,
631
+ isBusy && statusIcon != null && styles.textareaWithBusyStatus,
632
+ maxLength != null && styles.textareaWithCounter,
633
+ ),
631
634
  )}
632
635
  />
633
636
  {(isBusy || statusIcon) && (
@@ -639,9 +642,12 @@ export function TextArea({
639
642
  {maxLength != null && (
640
643
  <div
641
644
  id={counterID}
642
- {...stylex.props(
643
- styles.counter,
644
- valueLength > maxLength && styles.counterError,
645
+ {...mergeProps(
646
+ themeProps('text-area-counter'),
647
+ stylex.props(
648
+ styles.counter,
649
+ valueLength > maxLength && styles.counterError,
650
+ ),
645
651
  )}>
646
652
  {valueLength > maxLength && (
647
653
  // Non-color cue so the over-limit state isn't conveyed by the red
@@ -132,7 +132,7 @@ export const docs = {
132
132
  {
133
133
  name: 'maxMenuItems',
134
134
  type: 'number',
135
- description: 'Maximum number of dropdown items to display.',
135
+ description: 'Maximum number of search results to display. The hasCreate entry is offered on top of them, so a menu can show one more than this.',
136
136
  default: '10',
137
137
  },
138
138
  {
@@ -140,6 +140,12 @@ export const docs = {
140
140
  type: 'number',
141
141
  description: 'Fixed dropdown width in pixels. The menu never shrinks below its anchor width.',
142
142
  },
143
+ {
144
+ name: 'minQueryLength',
145
+ type: 'number',
146
+ description: 'Minimum query length before the search source is queried. Below it no search runs, and the menu stays closed — unless hasCreate is set, in which case the "Create ..." entry is still offered, being derived from the typed text rather than fetched for it.',
147
+ default: '1',
148
+ },
143
149
  {
144
150
  name: 'emptySearchResultsText',
145
151
  type: 'string',
@@ -386,7 +392,7 @@ export const docsZh = {
386
392
  {
387
393
  name: 'maxMenuItems',
388
394
  type: 'number',
389
- description: '\u4e0b\u62c9\u5217\u8868\u663e\u793a\u7684\u6700\u5927\u9879\u76ee\u6570\u3002',
395
+ description: '下拉列表显示的最大搜索结果数。“创建 ...”条目会在此之外额外提供,因此菜单可能比该数量多显示一项。',
390
396
  default: '10',
391
397
  },
392
398
  {
@@ -394,6 +400,12 @@ export const docsZh = {
394
400
  type: 'number',
395
401
  description: '下拉菜单的固定像素宽度。菜单不会小于其锚点宽度。',
396
402
  },
403
+ {
404
+ name: 'minQueryLength',
405
+ type: 'number',
406
+ description: '查询搜索源前的最小查询长度。低于该长度不会发起搜索,菜单保持关闭;但设置 hasCreate 时仍会提供“创建 ...”条目——该条目由输入的文本推导而来,并非通过搜索获取。',
407
+ default: '1',
408
+ },
397
409
  {
398
410
  name: 'emptySearchResultsText',
399
411
  type: 'string',
@@ -515,8 +527,9 @@ export const docsDense = {
515
527
  isOptional: 'Shows optional indicator on label.',
516
528
  labelTooltip: 'Tooltip on label.',
517
529
  hasEntriesOnFocus: 'Show bootstrap results on focus before typing.',
518
- maxMenuItems: 'Max dropdown items to display.',
530
+ maxMenuItems: 'Max search results shown; the hasCreate entry sits on top of them.',
519
531
  menuWidth: 'Fixed dropdown width in pixels.',
532
+ minQueryLength: 'Min query length before searching. Menu stays closed below it, except the hasCreate entry.',
520
533
  emptySearchResultsText: 'Text when search returns no results.',
521
534
  hasAutoFocus: 'Auto-focus input on mount.',
522
535
  size: 'Input+token size.',