@agentero/design-system 0.32.0 → 0.32.1

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.
@@ -588,13 +588,13 @@
588
588
  <header>
589
589
  <div class="wrap">
590
590
  <h1>Manifest Debugger</h1>
591
- <div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">34 components ok</span><span class="filter-pill ok" aria-disabled="true">279 stories ok</span></div>
591
+ <div class="summary"><a class="filter-pill all" data-k="all" href="#filter-all">All</a><span class="filter-pill ok" aria-disabled="true">34 components ok</span><span class="filter-pill ok" aria-disabled="true">281 stories ok</span></div>
592
592
  </div>
593
593
  </header>
594
594
  <main>
595
595
  <div class="wrap">
596
596
  <div class="note ok" style="margin-bottom: 16px;">
597
- Using <code>react-component-meta</code>. Generation took <strong>5.2s</strong>.
597
+ Using <code>react-component-meta</code>. Generation took <strong>4.0s</strong>.
598
598
  </div>
599
599
  <h2 class="section-title">Components</h2>
600
600
  <div class="grid" role="list">
@@ -2855,9 +2855,9 @@ import { CheckList } from &#39;@agentero/design-system/check-list&#39;;
2855
2855
  <div class="title">
2856
2856
  <h2><span class="status-dot dot-ok"></span> Combobox.Root</h2>
2857
2857
  <div class="badges">
2858
- <label for="c-7-components-combobox-props" class="badge ok as-toggle">38 prop types</label>
2858
+ <label for="c-7-components-combobox-props" class="badge ok as-toggle">39 prop types</label>
2859
2859
 
2860
- <label for="c-7-components-combobox-stories" class="badge ok as-toggle">8 stories</label>
2860
+ <label for="c-7-components-combobox-stories" class="badge ok as-toggle">10 stories</label>
2861
2861
 
2862
2862
 
2863
2863
  </div>
@@ -2900,7 +2900,7 @@ import { Combobox } from &#39;@agentero/design-system/combobox&#39;;
2900
2900
  <div class="note ok">
2901
2901
  <div class="row">
2902
2902
  <span class="ex-name">Prop types <small>(react-component-meta)</small></span>
2903
- <span class="badge ok">38 prop types</span>
2903
+ <span class="badge ok">39 prop types</span>
2904
2904
  </div>
2905
2905
  <pre><code>Component: src/combobox/index.ts::Combobox</code></pre>
2906
2906
  <pre><code>Props:</code></pre>
@@ -3060,6 +3060,14 @@ locale?: LocalesArgument
3060
3060
  */
3061
3061
  loopFocus?: boolean = true
3062
3062
 
3063
+ /**
3064
+ * Characters the query needs, spaces aside, before the list opens. Below it
3065
+ * focusing, clicking or pressing the chevron leaves the list shut, and
3066
+ * deleting back under it closes the list. Defaults to `0`: the list opens on
3067
+ * focus.
3068
+ */
3069
+ minInputLength?: number = 0
3070
+
3063
3071
  /**
3064
3072
  * Determines if the popup enters a modal state when open.
3065
3073
  * - `true`: user interaction is limited to the popup: document page scroll is locked and pointer interactions on outside elements are disabled.
@@ -3149,17 +3157,7 @@ virtualized?: boolean = false</code></pre>
3149
3157
  <div class="row">
3150
3158
  <span class="ex-name">Imports</span>
3151
3159
  </div>
3152
- <pre><code>import {
3153
- Button,
3154
- Combobox,
3155
- Field,
3156
- FieldText,
3157
- IconSearch,
3158
- Label,
3159
- Modal,
3160
- Skeleton,
3161
- Tag,
3162
- } from "@agentero/design-system";
3160
+ <pre><code>import { Button, Combobox, Field, FieldText, Label, Modal, Skeleton, Tag } from "@agentero/design-system";
3163
3161
  import { Suspense } from "react";</code></pre>
3164
3162
  </div>
3165
3163
 
@@ -3169,9 +3167,11 @@ import { Suspense } from "react";</code></pre>
3169
3167
  <span class="ex-name">Default</span>
3170
3168
  <span class="badge ok">story ok</span>
3171
3169
  </div>
3172
-
3170
+ <div>Search field with a magnifier and clear button over an in-memory list</div>
3173
3171
  <div class="hint">The common case: options are already in memory and Base UI filters them against
3174
- what you type. Selecting a row fills the input and closes the list.</div>
3172
+ what you type. Selecting a row fills the input and closes the list. The field is
3173
+ a search out of the box — a magnifier in front, a clear button once something is
3174
+ picked — and the list hangs from the whole field.</div>
3175
3175
  <pre><code>const Default = () =&gt; (
3176
3176
  &lt;Combobox.Root items={STATES}&gt;
3177
3177
  &lt;Combobox.Input placeholder=&quot;Search states&quot; aria-label=&quot;Search states&quot; /&gt;
@@ -3186,6 +3186,83 @@ what you type. Selecting a row fills the input and closes the list.</div>
3186
3186
  &lt;/Combobox.List&gt;
3187
3187
  &lt;/Combobox.Content&gt;
3188
3188
  &lt;/Combobox.Root&gt;
3189
+ );</code></pre>
3190
+ </div>
3191
+ <div class="note ok">
3192
+ <div class="row">
3193
+ <span class="ex-name">Plain</span>
3194
+ <span class="badge ok">story ok</span>
3195
+ </div>
3196
+ <div>Bare input with no icon and no clear button</div>
3197
+ <div class="hint">`variant=&quot;plain&quot;`: the input alone, with no magnifier and no clear button, for a
3198
+ field that needs no affordance — a FEIN lookup, a template name. The list still
3199
+ matches the field&#39;s width.</div>
3200
+ <pre><code>const Plain = () =&gt; (
3201
+ &lt;Combobox.Root items={STATES}&gt;
3202
+ &lt;Combobox.Input variant=&quot;plain&quot; placeholder=&quot;State&quot; aria-label=&quot;State&quot; /&gt;
3203
+ &lt;Combobox.Content&gt;
3204
+ &lt;Combobox.Empty&gt;No matches found&lt;/Combobox.Empty&gt;
3205
+ &lt;Combobox.List&gt;
3206
+ {(state: string) =&gt; (
3207
+ &lt;Combobox.Item key={state} value={state}&gt;
3208
+ {state}
3209
+ &lt;/Combobox.Item&gt;
3210
+ )}
3211
+ &lt;/Combobox.List&gt;
3212
+ &lt;/Combobox.Content&gt;
3213
+ &lt;/Combobox.Root&gt;
3214
+ );</code></pre>
3215
+ </div>
3216
+ <div class="note ok">
3217
+ <div class="row">
3218
+ <span class="ex-name">Select</span>
3219
+ <span class="badge ok">story ok</span>
3220
+ </div>
3221
+ <div>Field with a chevron that toggles the list, for short known lists</div>
3222
+ <div class="hint">`variant=&quot;select&quot;`: a chevron after the input opens and closes the list, for a
3223
+ short known list the user may want to browse before typing. It has no clear
3224
+ button by default. The chevron is out of the tab order — the input already
3225
+ opens the list from the keyboard with the arrow keys.</div>
3226
+ <pre><code>const Select = () =&gt; (
3227
+ &lt;Combobox.Root items={STATES}&gt;
3228
+ &lt;Combobox.Input variant=&quot;select&quot; placeholder=&quot;Choose a state&quot; aria-label=&quot;State&quot; /&gt;
3229
+ &lt;Combobox.Content&gt;
3230
+ &lt;Combobox.Empty&gt;No matches found&lt;/Combobox.Empty&gt;
3231
+ &lt;Combobox.List&gt;
3232
+ {(state: string) =&gt; (
3233
+ &lt;Combobox.Item key={state} value={state}&gt;
3234
+ {state}
3235
+ &lt;/Combobox.Item&gt;
3236
+ )}
3237
+ &lt;/Combobox.List&gt;
3238
+ &lt;/Combobox.Content&gt;
3239
+ &lt;/Combobox.Root&gt;
3240
+ );</code></pre>
3241
+ </div>
3242
+ <div class="note ok">
3243
+ <div class="row">
3244
+ <span class="ex-name">Select With Clear</span>
3245
+ <span class="badge ok">story ok</span>
3246
+ </div>
3247
+ <div>Select field with a clear button added through showClear</div>
3248
+ <div class="hint">`showClear` overrides the variant&#39;s default: here it adds the clear button to a
3249
+ `select`, so a picked option can be removed without selecting the text and
3250
+ deleting it. While there is a value it takes the chevron&#39;s place; clearing
3251
+ brings the chevron back. Passing `false` drops it from `search`.</div>
3252
+ <pre><code>const SelectWithClear = () =&gt; (
3253
+ &lt;Combobox.Root items={STATES} defaultValue=&quot;Florida&quot;&gt;
3254
+ &lt;Combobox.Input variant=&quot;select&quot; showClear placeholder=&quot;Choose a state&quot; aria-label=&quot;State&quot; /&gt;
3255
+ &lt;Combobox.Content&gt;
3256
+ &lt;Combobox.Empty&gt;No matches found&lt;/Combobox.Empty&gt;
3257
+ &lt;Combobox.List&gt;
3258
+ {(state: string) =&gt; (
3259
+ &lt;Combobox.Item key={state} value={state}&gt;
3260
+ {state}
3261
+ &lt;/Combobox.Item&gt;
3262
+ )}
3263
+ &lt;/Combobox.List&gt;
3264
+ &lt;/Combobox.Content&gt;
3265
+ &lt;/Combobox.Root&gt;
3189
3266
  );</code></pre>
3190
3267
  </div>
3191
3268
  <div class="note ok">
@@ -3294,11 +3371,15 @@ out-of-order responses harmless. Drop a `useSuspenseQuery` where `use()` is.</di
3294
3371
  <span class="ex-name">Async Search With Status</span>
3295
3372
  <span class="badge ok">story ok</span>
3296
3373
  </div>
3297
-
3374
+ <div>API search that waits for two characters, with loading status</div>
3298
3375
  <div class="hint">The same search without Suspense, for a consumer that gets a `loading` flag from
3299
3376
  its data layer instead: `filter={null}`, the rows arrive through `filteredItems`,
3300
3377
  `Content` is marked busy and `Status` announces the wait. The list is cleared
3301
- before each request and a request that is no longer the latest is dropped.</div>
3378
+ before each request and a request that is no longer the latest is dropped.
3379
+
3380
+ `minInputLength` keeps the list shut until two characters are typed — focusing
3381
+ the field opens nothing, and deleting back under the minimum closes it — and
3382
+ no request goes out for a shorter query either.</div>
3302
3383
  <pre><code>const AsyncSearchWithStatus = () =&gt; {
3303
3384
  const [results, setResults] = useState&lt;Agency[]&gt;([]);
3304
3385
  const [isLoading, setIsLoading] = useState(false);
@@ -3307,6 +3388,11 @@ before each request and a request that is no longer the latest is dropped.</div>
3307
3388
 
3308
3389
  const onInputValueChange = async (query: string) =&gt; {
3309
3390
  const request = ++latestRequest.current;
3391
+ if (query.trim().length &lt; MIN_QUERY_LENGTH) {
3392
+ setResults([]);
3393
+ setIsLoading(false);
3394
+ return;
3395
+ }
3310
3396
 
3311
3397
  // The previous rows go before the wait starts: a list that no longer matches what is typed
3312
3398
  // must not sit under the loading state.
@@ -3325,6 +3411,7 @@ before each request and a request that is no longer the latest is dropped.</div>
3325
3411
  items={results}
3326
3412
  filteredItems={results}
3327
3413
  filter={null}
3414
+ minInputLength={MIN_QUERY_LENGTH}
3328
3415
  itemToStringLabel={(agency: Agency) =&gt; agency.name}
3329
3416
  isItemEqualToValue={(a: Agency, b: Agency) =&gt; a.id === b.id}
3330
3417
  onInputValueChange={onInputValueChange}&gt;
@@ -3379,39 +3466,6 @@ rest of the page from assistive technology, label included, and a
3379
3466
  &lt;Field.Description&gt;The agency this agent will be added to.&lt;/Field.Description&gt;
3380
3467
  &lt;Field.Error errors={[{ message: &#39;Pick an agency from the list&#39; }]} /&gt;
3381
3468
  &lt;/FieldText&gt;
3382
- );</code></pre>
3383
- </div>
3384
- <div class="note ok">
3385
- <div class="row">
3386
- <span class="ex-name">With Leading Icon</span>
3387
- <span class="badge ok">story ok</span>
3388
- </div>
3389
-
3390
- <div class="hint">A leading icon is composed around the input, not baked in: not every combobox
3391
- is a search (a FEIN lookup or a template picker are not), so the component
3392
- ships no icon of its own. Until `InputGroup` lands in the design system, the
3393
- marketplace search fields get theirs like this — the icon overlaid, the input
3394
- padded past it.</div>
3395
- <pre><code>const WithLeadingIcon = () =&gt; (
3396
- &lt;Combobox.Root items={STATES}&gt;
3397
- &lt;div className=&quot;relative&quot;&gt;
3398
- &lt;IconSearch
3399
- aria-hidden
3400
- className=&quot;pointer-events-none absolute top-1/2 left-3 -z-hide size-6 -translate-y-1/2 [&amp;&gt;path]:fill-icon-default-base-tertiary&quot;
3401
- /&gt;
3402
- &lt;Combobox.Input className=&quot;pl-11&quot; placeholder=&quot;Search states&quot; aria-label=&quot;Search states&quot; /&gt;
3403
- &lt;/div&gt;
3404
- &lt;Combobox.Content&gt;
3405
- &lt;Combobox.Empty&gt;No matches found&lt;/Combobox.Empty&gt;
3406
- &lt;Combobox.List&gt;
3407
- {(state: string) =&gt; (
3408
- &lt;Combobox.Item key={state} value={state}&gt;
3409
- {state}
3410
- &lt;/Combobox.Item&gt;
3411
- )}
3412
- &lt;/Combobox.List&gt;
3413
- &lt;/Combobox.Content&gt;
3414
- &lt;/Combobox.Root&gt;
3415
3469
  );</code></pre>
3416
3470
  </div>
3417
3471
  <div class="note ok">
@@ -2007,7 +2007,7 @@
2007
2007
  "displayName": "Combobox.Root",
2008
2008
  "exportName": "Combobox",
2009
2009
  "filePath": "/home/runner/work/design-system/design-system/src/combobox/index.ts",
2010
- "description": "Root of a combobox: a text input that filters a list of options and writes the\nchosen one back into itself. With `Input` as the only trigger, the list opens on\nfocus or on the first keystroke.\n\nPass the options as `items`. For a list that comes from the server, set\n`filter={null}`, hand the already-filtered rows to `filteredItems`, and request\nthem from `onInputValueChange`; `Status` then announces the wait.\n\nWith `multiple`, the value is an array and picking a row toggles it. The list\nstays open and keeps the typed query between picks, so several rows can be\nticked from one search; closing the list (Escape, a click outside) still resets\nthe query. Each row then shows whether it is selected — see `Item`. Nothing is\nwritten back into the input: render the selection next to the field.",
2010
+ "description": "Root of a combobox: a text input that filters a list of options and writes the\nchosen one back into itself. The list opens on focus, on the first keystroke\nor from the chevron of `Input`'s `select` variant.\n\nFor a search against an API, set `minInputLength` so the list stays shut until\nthe query is worth sending: a field that opens an empty list on focus reads as\nbroken. For a pick from a short fixed list with no typing at all, prefer a\nselect.\n\nPass the options as `items`. For a list that comes from the server, set\n`filter={null}`, hand the already-filtered rows to `filteredItems`, and request\nthem from `onInputValueChange`; `Status` then announces the wait.\n\nEvery Base UI root prop is accepted and forwarded — `items`, `filter`,\n`value`, `onValueChange` and so on. `open` stays the consumer's to control;\n`minInputLength` only refuses to open below the minimum.\n\nWith `multiple`, the value is an array and picking a row toggles it. The list\nstays open and keeps the typed query between picks, so several rows can be\nticked from one search; closing the list (Escape, a click outside) still resets\nthe query. Each row then shows whether it is selected — see `Item`. Nothing is\nwritten back into the input: render the selection next to the field.",
2011
2011
  "jsDocTags": {
2012
2012
  "summary": [
2013
2013
  "Root provider for an input-triggered filterable list"
@@ -2769,6 +2769,27 @@
2769
2769
  "fileName": "node_modules/@base-ui/react/combobox/root/ComboboxRoot.d.mts"
2770
2770
  }
2771
2771
  ]
2772
+ },
2773
+ "minInputLength": {
2774
+ "name": "minInputLength",
2775
+ "required": false,
2776
+ "type": {
2777
+ "name": "number"
2778
+ },
2779
+ "description": "Characters the query needs, spaces aside, before the list opens. Below it\nfocusing, clicking or pressing the chevron leaves the list shut, and\ndeleting back under it closes the list. Defaults to `0`: the list opens on\nfocus.",
2780
+ "defaultValue": {
2781
+ "value": "0"
2782
+ },
2783
+ "parent": {
2784
+ "name": "ComboboxRootProps",
2785
+ "fileName": "src/combobox/combobox.tsx"
2786
+ },
2787
+ "declarations": [
2788
+ {
2789
+ "name": "TypeLiteral",
2790
+ "fileName": "src/combobox/combobox.tsx"
2791
+ }
2792
+ ]
2772
2793
  }
2773
2794
  }
2774
2795
  },
@@ -2777,7 +2798,29 @@
2777
2798
  "id": "components-combobox--default",
2778
2799
  "name": "Default",
2779
2800
  "snippet": "const Default = () => (\n <Combobox.Root items={STATES}>\n <Combobox.Input placeholder=\"Search states\" aria-label=\"Search states\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
2780
- "description": "The common case: options are already in memory and Base UI filters them against\nwhat you type. Selecting a row fills the input and closes the list."
2801
+ "description": "The common case: options are already in memory and Base UI filters them against\nwhat you type. Selecting a row fills the input and closes the list. The field is\na search out of the box — a magnifier in front, a clear button once something is\npicked — and the list hangs from the whole field.",
2802
+ "summary": "Search field with a magnifier and clear button over an in-memory list"
2803
+ },
2804
+ {
2805
+ "id": "components-combobox--plain",
2806
+ "name": "Plain",
2807
+ "snippet": "const Plain = () => (\n <Combobox.Root items={STATES}>\n <Combobox.Input variant=\"plain\" placeholder=\"State\" aria-label=\"State\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
2808
+ "description": "`variant=\"plain\"`: the input alone, with no magnifier and no clear button, for a\nfield that needs no affordance — a FEIN lookup, a template name. The list still\nmatches the field's width.",
2809
+ "summary": "Bare input with no icon and no clear button"
2810
+ },
2811
+ {
2812
+ "id": "components-combobox--select",
2813
+ "name": "Select",
2814
+ "snippet": "const Select = () => (\n <Combobox.Root items={STATES}>\n <Combobox.Input variant=\"select\" placeholder=\"Choose a state\" aria-label=\"State\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
2815
+ "description": "`variant=\"select\"`: a chevron after the input opens and closes the list, for a\nshort known list the user may want to browse before typing. It has no clear\nbutton by default. The chevron is out of the tab order — the input already\nopens the list from the keyboard with the arrow keys.",
2816
+ "summary": "Field with a chevron that toggles the list, for short known lists"
2817
+ },
2818
+ {
2819
+ "id": "components-combobox--select-with-clear",
2820
+ "name": "Select With Clear",
2821
+ "snippet": "const SelectWithClear = () => (\n <Combobox.Root items={STATES} defaultValue=\"Florida\">\n <Combobox.Input variant=\"select\" showClear placeholder=\"Choose a state\" aria-label=\"State\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
2822
+ "description": "`showClear` overrides the variant's default: here it adds the clear button to a\n`select`, so a picked option can be removed without selecting the text and\ndeleting it. While there is a value it takes the chevron's place; clearing\nbrings the chevron back. Passing `false` drops it from `search`.",
2823
+ "summary": "Select field with a clear button added through showClear"
2781
2824
  },
2782
2825
  {
2783
2826
  "id": "components-combobox--no-matches",
@@ -2800,8 +2843,9 @@
2800
2843
  {
2801
2844
  "id": "components-combobox--async-search-with-status",
2802
2845
  "name": "Async Search With Status",
2803
- "snippet": "const AsyncSearchWithStatus = () => {\n const [results, setResults] = useState<Agency[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n // Only the latest request may land; fast typing returns responses out of order.\n const latestRequest = useRef(0);\n\n const onInputValueChange = async (query: string) => {\n const request = ++latestRequest.current;\n\n // The previous rows go before the wait starts: a list that no longer matches what is typed\n // must not sit under the loading state.\n setResults([]);\n setIsLoading(true);\n const agencies = await searchAgencies(query);\n if (request !== latestRequest.current) return;\n\n setResults(agencies);\n setIsLoading(false);\n };\n\n return (\n // `filter={null}` hands filtering to the server; `filteredItems` is what came back.\n <Combobox.Root\n items={results}\n filteredItems={results}\n filter={null}\n itemToStringLabel={(agency: Agency) => agency.name}\n isItemEqualToValue={(a: Agency, b: Agency) => a.id === b.id}\n onInputValueChange={onInputValueChange}>\n <Combobox.Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n <Combobox.Content aria-busy={isLoading || undefined}>\n {/* The rows show the wait; `Status` only announces it. */}\n <Combobox.Status className=\"sr-only\">\n {isLoading ? 'Searching…' : undefined}\n </Combobox.Status>\n {isLoading && <SkeletonRows />}\n <Combobox.Empty>{isLoading ? undefined : 'No agencies found'}</Combobox.Empty>\n <Combobox.List>\n {(agency: Agency) => (\n <Combobox.Item key={agency.id} value={agency}>\n {agency.name}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n );\n};",
2804
- "description": "The same search without Suspense, for a consumer that gets a `loading` flag from\nits data layer instead: `filter={null}`, the rows arrive through `filteredItems`,\n`Content` is marked busy and `Status` announces the wait. The list is cleared\nbefore each request and a request that is no longer the latest is dropped."
2846
+ "snippet": "const AsyncSearchWithStatus = () => {\n const [results, setResults] = useState<Agency[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n // Only the latest request may land; fast typing returns responses out of order.\n const latestRequest = useRef(0);\n\n const onInputValueChange = async (query: string) => {\n const request = ++latestRequest.current;\n if (query.trim().length < MIN_QUERY_LENGTH) {\n setResults([]);\n setIsLoading(false);\n return;\n }\n\n // The previous rows go before the wait starts: a list that no longer matches what is typed\n // must not sit under the loading state.\n setResults([]);\n setIsLoading(true);\n const agencies = await searchAgencies(query);\n if (request !== latestRequest.current) return;\n\n setResults(agencies);\n setIsLoading(false);\n };\n\n return (\n // `filter={null}` hands filtering to the server; `filteredItems` is what came back.\n <Combobox.Root\n items={results}\n filteredItems={results}\n filter={null}\n minInputLength={MIN_QUERY_LENGTH}\n itemToStringLabel={(agency: Agency) => agency.name}\n isItemEqualToValue={(a: Agency, b: Agency) => a.id === b.id}\n onInputValueChange={onInputValueChange}>\n <Combobox.Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n <Combobox.Content aria-busy={isLoading || undefined}>\n {/* The rows show the wait; `Status` only announces it. */}\n <Combobox.Status className=\"sr-only\">\n {isLoading ? 'Searching…' : undefined}\n </Combobox.Status>\n {isLoading && <SkeletonRows />}\n <Combobox.Empty>{isLoading ? undefined : 'No agencies found'}</Combobox.Empty>\n <Combobox.List>\n {(agency: Agency) => (\n <Combobox.Item key={agency.id} value={agency}>\n {agency.name}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n );\n};",
2847
+ "description": "The same search without Suspense, for a consumer that gets a `loading` flag from\nits data layer instead: `filter={null}`, the rows arrive through `filteredItems`,\n`Content` is marked busy and `Status` announces the wait. The list is cleared\nbefore each request and a request that is no longer the latest is dropped.\n\n`minInputLength` keeps the list shut until two characters are typed — focusing\nthe field opens nothing, and deleting back under the minimum closes it — and\nno request goes out for a shorter query either.",
2848
+ "summary": "API search that waits for two characters, with loading status"
2805
2849
  },
2806
2850
  {
2807
2851
  "id": "components-combobox--inside-field-text",
@@ -2809,12 +2853,6 @@
2809
2853
  "snippet": "const InsideFieldText = () => (\n <FieldText invalid required>\n <Label>Agency</Label>\n <Combobox.Root items={STATES}>\n <Combobox.Input placeholder=\"Search agencies\" />\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n <Field.Description>The agency this agent will be added to.</Field.Description>\n <Field.Error errors={[{ message: 'Pick an agency from the list' }]} />\n </FieldText>\n);",
2810
2854
  "description": "Dropped into a `FieldText`, the input takes the label, the description and\nthe error from the field with nothing wired by hand: `Combobox.Input` reads the\nsame `InputContext` the plain `Input` does. It also points `aria-labelledby`\nat the label, so the name survives the list opening: Base UI then hides the\nrest of the page from assistive technology, label included, and a\n`<label for>` alone would stop naming the input."
2811
2855
  },
2812
- {
2813
- "id": "components-combobox--with-leading-icon",
2814
- "name": "With Leading Icon",
2815
- "snippet": "const WithLeadingIcon = () => (\n <Combobox.Root items={STATES}>\n <div className=\"relative\">\n <IconSearch\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 left-3 -z-hide size-6 -translate-y-1/2 [&>path]:fill-icon-default-base-tertiary\"\n />\n <Combobox.Input className=\"pl-11\" placeholder=\"Search states\" aria-label=\"Search states\" />\n </div>\n <Combobox.Content>\n <Combobox.Empty>No matches found</Combobox.Empty>\n <Combobox.List>\n {(state: string) => (\n <Combobox.Item key={state} value={state}>\n {state}\n </Combobox.Item>\n )}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n);",
2816
- "description": "A leading icon is composed around the input, not baked in: not every combobox\nis a search (a FEIN lookup or a template picker are not), so the component\nships no icon of its own. Until `InputGroup` lands in the design system, the\nmarketplace search fields get theirs like this — the icon overlaid, the input\npadded past it."
2817
- },
2818
2856
  {
2819
2857
  "id": "components-combobox--inside-modal",
2820
2858
  "name": "Inside Modal",
@@ -2822,7 +2860,7 @@
2822
2860
  "description": "Inside a `Modal`, which is a Radix Dialog. The surface portals to the body, so\nthis checks that the dialog's modal layer neither blocks pointer events on the\nlist nor treats a click in it as a click outside and closes."
2823
2861
  }
2824
2862
  ],
2825
- "import": "import {\n Button,\n Combobox,\n Field,\n FieldText,\n IconSearch,\n Label,\n Modal,\n Skeleton,\n Tag,\n} from \"@agentero/design-system\";\nimport { Suspense } from \"react\";"
2863
+ "import": "import { Button, Combobox, Field, FieldText, Label, Modal, Skeleton, Tag } from \"@agentero/design-system\";\nimport { Suspense } from \"react\";"
2826
2864
  },
2827
2865
  "components-command": {
2828
2866
  "id": "components-command",
@@ -9552,6 +9590,6 @@
9552
9590
  },
9553
9591
  "meta": {
9554
9592
  "docgen": "react-component-meta",
9555
- "durationMs": 5204
9593
+ "durationMs": 3999
9556
9594
  }
9557
9595
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.32.0",
3
+ "version": "0.32.1",
4
4
  "description": "A React component library built with Tailwind CSS v4 and Radix UI primitives",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -2,8 +2,13 @@ import { ComponentPropsWithRef } from 'react';
2
2
  import { Combobox as ComboboxPrimitive } from '@base-ui/react/combobox';
3
3
  import { InputSize } from '../input';
4
4
  /**
5
- * Style recipe for Combobox. The surface carries its own chrome — border,
6
- * background and shadow — because the list it holds is plain content.
5
+ * Style recipe for Combobox. Slots: `action` and `trigger` (the clear and
6
+ * chevron buttons inside the input's frame, which is `InputGroup`'s),
7
+ * `triggerReplaced` (hides the chevron once the clear button has a value to
8
+ * clear), `content`,
9
+ * `list`, `item`, `indicator` and `message`. The surface carries its
10
+ * own chrome — border, background and shadow — because the list it holds is
11
+ * plain content.
7
12
  *
8
13
  * @summary tailwind-variants recipe backing the Combobox surface and list styles
9
14
  */
@@ -12,11 +17,12 @@ export declare const comboboxRecipe: import('tailwind-variants').TVReturnType<{
12
17
  [key: string]: import('tailwind-merge').ClassNameValue | {
13
18
  content?: import('tailwind-merge').ClassNameValue;
14
19
  list?: import('tailwind-merge').ClassNameValue;
15
- clear?: import('tailwind-merge').ClassNameValue;
16
20
  item?: import('tailwind-merge').ClassNameValue;
17
21
  indicator?: import('tailwind-merge').ClassNameValue;
18
22
  message?: import('tailwind-merge').ClassNameValue;
19
- field?: import('tailwind-merge').ClassNameValue;
23
+ action?: import('tailwind-merge').ClassNameValue;
24
+ trigger?: import('tailwind-merge').ClassNameValue;
25
+ triggerReplaced?: import('tailwind-merge').ClassNameValue;
20
26
  };
21
27
  };
22
28
  } | {
@@ -24,16 +30,18 @@ export declare const comboboxRecipe: import('tailwind-variants').TVReturnType<{
24
30
  [x: string]: import('tailwind-merge').ClassNameValue | {
25
31
  content?: import('tailwind-merge').ClassNameValue;
26
32
  list?: import('tailwind-merge').ClassNameValue;
27
- clear?: import('tailwind-merge').ClassNameValue;
28
33
  item?: import('tailwind-merge').ClassNameValue;
29
34
  indicator?: import('tailwind-merge').ClassNameValue;
30
35
  message?: import('tailwind-merge').ClassNameValue;
31
- field?: import('tailwind-merge').ClassNameValue;
36
+ action?: import('tailwind-merge').ClassNameValue;
37
+ trigger?: import('tailwind-merge').ClassNameValue;
38
+ triggerReplaced?: import('tailwind-merge').ClassNameValue;
32
39
  };
33
40
  };
34
41
  } | {}, {
35
- field: string;
36
- clear: string[];
42
+ action: string[];
43
+ trigger: string;
44
+ triggerReplaced: string;
37
45
  content: string[];
38
46
  list: string[];
39
47
  item: string[];
@@ -44,39 +52,60 @@ export declare const comboboxRecipe: import('tailwind-variants').TVReturnType<{
44
52
  [key: string]: import('tailwind-merge').ClassNameValue | {
45
53
  content?: import('tailwind-merge').ClassNameValue;
46
54
  list?: import('tailwind-merge').ClassNameValue;
47
- clear?: import('tailwind-merge').ClassNameValue;
48
55
  item?: import('tailwind-merge').ClassNameValue;
49
56
  indicator?: import('tailwind-merge').ClassNameValue;
50
57
  message?: import('tailwind-merge').ClassNameValue;
51
- field?: import('tailwind-merge').ClassNameValue;
58
+ action?: import('tailwind-merge').ClassNameValue;
59
+ trigger?: import('tailwind-merge').ClassNameValue;
60
+ triggerReplaced?: import('tailwind-merge').ClassNameValue;
52
61
  };
53
62
  };
54
63
  } | {}, {
55
- field: string;
56
- clear: string[];
64
+ action: string[];
65
+ trigger: string;
66
+ triggerReplaced: string;
57
67
  content: string[];
58
68
  list: string[];
59
69
  item: string[];
60
70
  indicator: string[];
61
71
  message: string[];
62
72
  }, import('tailwind-variants').TVReturnType<unknown, {
63
- field: string;
64
- clear: string[];
73
+ action: string[];
74
+ trigger: string;
75
+ triggerReplaced: string;
65
76
  content: string[];
66
77
  list: string[];
67
78
  item: string[];
68
79
  indicator: string[];
69
80
  message: string[];
70
81
  }, undefined, unknown, unknown, undefined>>;
82
+ export type ComboboxRootProps<Value, Multiple extends boolean | undefined, Item> = ComboboxPrimitive.Root.Props<Value, Multiple, Item> & {
83
+ /**
84
+ * Characters the query needs, spaces aside, before the list opens. Below it
85
+ * focusing, clicking or pressing the chevron leaves the list shut, and
86
+ * deleting back under it closes the list. Defaults to `0`: the list opens on
87
+ * focus.
88
+ */
89
+ minInputLength?: number;
90
+ };
71
91
  /**
72
92
  * Root of a combobox: a text input that filters a list of options and writes the
73
- * chosen one back into itself. With `Input` as the only trigger, the list opens on
74
- * focus or on the first keystroke.
93
+ * chosen one back into itself. The list opens on focus, on the first keystroke
94
+ * or from the chevron of `Input`'s `select` variant.
95
+ *
96
+ * For a search against an API, set `minInputLength` so the list stays shut until
97
+ * the query is worth sending: a field that opens an empty list on focus reads as
98
+ * broken. For a pick from a short fixed list with no typing at all, prefer a
99
+ * select.
75
100
  *
76
101
  * Pass the options as `items`. For a list that comes from the server, set
77
102
  * `filter={null}`, hand the already-filtered rows to `filteredItems`, and request
78
103
  * them from `onInputValueChange`; `Status` then announces the wait.
79
104
  *
105
+ * Every Base UI root prop is accepted and forwarded — `items`, `filter`,
106
+ * `value`, `onValueChange` and so on. `open` stays the consumer's to control;
107
+ * `minInputLength` only refuses to open below the minimum.
108
+ *
80
109
  * With `multiple`, the value is an array and picking a row toggles it. The list
81
110
  * stays open and keeps the typed query between picks, so several rows can be
82
111
  * ticked from one search; closing the list (Escape, a click outside) still resets
@@ -102,20 +131,37 @@ export declare const comboboxRecipe: import('tailwind-variants').TVReturnType<{
102
131
  * ```
103
132
  */
104
133
  export declare const Root: {
105
- <Value, Multiple extends boolean | undefined = false, Item = Value>({ onOpenChange, ...props }: ComboboxPrimitive.Root.Props<Value, Multiple, Item>): import("react/jsx-runtime").JSX.Element;
134
+ <Value, Multiple extends boolean | undefined = false, Item = Value>({ minInputLength, open: openProp, defaultOpen, onOpenChange, onOpenChangeComplete, onInputValueChange, ...props }: ComboboxRootProps<Value, Multiple, Item>): import("react/jsx-runtime").JSX.Element;
106
135
  displayName: string;
107
136
  };
108
137
  type InputProps = Omit<ComponentPropsWithRef<typeof ComboboxPrimitive.Input>, 'size'> & {
109
138
  /** Control height, matching `Input`. Defaults to `'md'`. */
110
139
  size?: InputSize;
140
+ /**
141
+ * What surrounds the input. Defaults to `'search'`.
142
+ * - `search` — magnifier in front, clear button after: a lookup the user types
143
+ * - `plain` — the input alone
144
+ * - `select` — a chevron that toggles the list: a short list to browse
145
+ */
146
+ variant?: 'search' | 'plain' | 'select';
147
+ /**
148
+ * Whether a clear button follows the input once an option is picked.
149
+ * Defaults to `true` for `search` and `false` for `plain` and `select`; pass
150
+ * it to override the variant. On a `select` it replaces the chevron while
151
+ * there is a value. Ignored under a `multiple` root, which never
152
+ * shows one.
153
+ */
154
+ showClear?: boolean;
111
155
  /**
112
156
  * Accessible name for the clear button. Defaults to `'Clear'`; name it after
113
157
  * the field when a page carries several comboboxes.
114
158
  */
115
159
  clearLabel?: string;
160
+ /** Accessible name for the chevron of the `select` variant. Defaults to `'Show options'`. */
161
+ triggerLabel?: string;
116
162
  };
117
163
  /**
118
- * The search field, which doubles as the trigger: focusing or typing opens the
164
+ * The text field, which doubles as the trigger: focusing or typing opens the
119
165
  * list. It wears the same styling as `Input` and takes the same wiring from
120
166
  * `InputContext`, so inside a `FieldText` the label, the hint and the error point
121
167
  * at it with nothing passed by hand. Its own props win over the context.
@@ -127,21 +173,38 @@ type InputProps = Omit<ComponentPropsWithRef<typeof ComboboxPrimitive.Input>, 's
127
173
  * reference keeps the name. Outside a field, give it `aria-label` or an
128
174
  * `aria-labelledby` of your own.
129
175
  *
130
- * It carries a clear button, which Base UI mounts only once there is something
131
- * to clear, so an untouched field shows nothing. Name it with `clearLabel` when
132
- * a page has more than one combobox. Under a `multiple` root there is no clear
133
- * button: Base UI's would drop the whole selection, while a cross inside a search
134
- * field reads as "clear what I typed" — and the selection is rendered outside
135
- * the field, with its own way to remove each entry.
176
+ * It is drawn as an `InputGroup` frame, and the list anchors to that whole
177
+ * frame, so it is as wide as the field and starts at its left edge whatever
178
+ * sits beside the input. `variant` picks what does:
179
+ *
180
+ * - `search` (default): a magnifier in front and a clear button. For any field
181
+ * where the user types to look something up.
182
+ * - `plain`: the input alone, with no icon and no button. When the field needs
183
+ * no affordance at all.
184
+ * - `select`: a chevron that opens and closes the list. For a short known list
185
+ * the user may want to browse before typing.
186
+ *
187
+ * Only `search` carries a clear button by default; `showClear` adds it to the
188
+ * other two or drops it from `search`. On a `select`, the clear button takes the
189
+ * chevron's place once an option is picked; the list still opens from the input. Base UI mounts it only once there is
190
+ * something to clear — a picked option — so an untouched field shows nothing. Name it with
191
+ * `clearLabel`, and the chevron with `triggerLabel`, when a page has more than
192
+ * one combobox.
193
+ *
194
+ * Under a `multiple` root there is neither: the list stays open between picks,
195
+ * so a chevron has nothing to toggle, and Base UI's clear button would drop the
196
+ * whole selection while a cross inside a search field reads as "clear what I
197
+ * typed". The selection is rendered outside the field, with its own way to
198
+ * remove each entry.
136
199
  *
137
- * Pass `render` to swap the element for a richer control — a field with a leading
138
- * search icon, for instance — and the combobox wiring rides along.
200
+ * Every native input attribute is accepted and forwarded to the `<input>` —
201
+ * `placeholder`, `disabled`, `autoComplete` — and so is `className`.
139
202
  *
140
- * @summary Search field that opens the list on focus and filters it as you type
203
+ * @summary Search, plain or select field that opens and filters the list
141
204
  * @dataAttribute {string} data-slot - Always set to "combobox-input"
142
205
  */
143
206
  export declare const Input: {
144
- ({ clearLabel, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
207
+ ({ variant, showClear, clearLabel, triggerLabel, ...props }: InputProps): import("react/jsx-runtime").JSX.Element;
145
208
  displayName: string;
146
209
  };
147
210
  type ContentProps = ComponentPropsWithRef<typeof ComboboxPrimitive.Popup> & Pick<ComponentPropsWithRef<typeof ComboboxPrimitive.Positioner>, 'side' | 'align' | 'sideOffset' | 'alignOffset' | 'anchor' | 'collisionPadding'>;
@@ -4,20 +4,22 @@ import { useMergeProps as t } from "../../lib/merge-props.js";
4
4
  import { useFieldContext as n } from "../field/context.js";
5
5
  import { InputContext as r } from "../input/context.js";
6
6
  import { inputRecipe as i } from "../input/input.js";
7
- import { IconCancel as a, IconCheck as o } from "./icons.js";
8
- import { tv as s } from "tailwind-variants";
9
- import { createContext as c, use as l } from "react";
10
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
11
- import { Combobox as f } from "@base-ui/react/combobox";
7
+ import { InputGroup as a } from "../input-group/index.js";
8
+ import { IconCancel as o, IconCheck as s, IconKeyboardArrowDown as c, IconSearch as l } from "./icons.js";
9
+ import { tv as u } from "tailwind-variants";
10
+ import { createContext as d, use as f, useRef as p, useState as m } from "react";
11
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
12
+ import { Combobox as _ } from "@base-ui/react/combobox";
12
13
  //#region src/combobox/combobox.tsx
13
- var p = s({ slots: {
14
- field: "relative",
15
- clear: [
16
- "absolute top-1/2 right-3 flex size-6 -translate-y-1/2 cursor-pointer items-center",
17
- "justify-center rounded-sm [&>svg>path]:fill-icon-input-default",
18
- "hover:[&>svg>path]:fill-icon-default-base-primary",
14
+ var v = u({ slots: {
15
+ action: [
16
+ "flex size-6 shrink-0 cursor-pointer items-center justify-center self-center rounded-sm",
17
+ "[&>svg>path]:fill-icon-input-default hover:[&>svg>path]:fill-icon-default-base-primary",
18
+ "focus-visible:outline-2 focus-visible:outline-border-input-focus",
19
19
  "disabled:cursor-default disabled:[&>svg>path]:fill-icon-input-disable"
20
20
  ],
21
+ trigger: "transition-[rotate] duration-100 data-popup-open:rotate-180 motion-reduce:transition-none",
22
+ triggerReplaced: "[[data-slot=combobox-field]:not([data-placeholder])_&]:hidden",
21
23
  content: [
22
24
  "w-(--anchor-width) max-h-(--available-height) overflow-hidden rounded-md",
23
25
  "border border-border-default-base-primary bg-bg-default-base-primary shadow-lg",
@@ -27,7 +29,7 @@ var p = s({ slots: {
27
29
  "data-[ending-style]:scale-95 data-[ending-style]:opacity-0",
28
30
  "motion-reduce:transition-none"
29
31
  ],
30
- list: ["max-h-[min(10.25rem,var(--available-height))] overflow-y-auto scroll-py-2 py-2 outline-none", "data-empty:py-0"],
32
+ list: ["max-h-[min(10.25rem,var(--available-height))] overflow-y-auto scroll-py-2 py-2 outline-none", "empty:py-0"],
31
33
  item: [
32
34
  "group/item mx-2 my-px flex cursor-pointer items-center rounded-[calc(var(--radius-md)-2px)]",
33
35
  "px-3 py-2 text-sm text-text-default-base-primary select-none",
@@ -45,49 +47,78 @@ var p = s({ slots: {
45
47
  "group-data-disabled/item:data-selected:bg-bg-checkbox-disabled"
46
48
  ],
47
49
  message: ["mx-2 my-px px-3 py-2 text-sm text-text-default-base-tertiary", "empty:m-0 empty:p-0"]
48
- } }), m = p(), h = c(!1), g = ({ onOpenChange: e, ...t }) => {
49
- let n = !!t.multiple;
50
- return /* @__PURE__ */ u(h, {
51
- value: n,
52
- children: /* @__PURE__ */ u(f.Root, {
53
- onOpenChange: (t, r) => {
54
- e?.(t, r), n && !t && r.reason === "item-press" && r.cancel();
55
- },
56
- ...t
50
+ } }), y = v(), b = d(!1), x = ({ minInputLength: e = 0, open: t, defaultOpen: n = !1, onOpenChange: r, onOpenChangeComplete: i, onInputValueChange: a, ...o }) => {
51
+ let s = !!o.multiple, [c, l] = m(n), u = p(String(o.inputValue ?? o.defaultInputValue ?? ""));
52
+ o.inputValue !== void 0 && (u.current = String(o.inputValue));
53
+ let d = (t) => t.trim().length < e, f = p(!1), g = p(!1), v = (e, t) => {
54
+ if (e && d(u.current)) {
55
+ t.cancel();
56
+ return;
57
+ }
58
+ f.current = !1, r?.(e, t), s && !e && t.reason === "item-press" && t.cancel(), t.isCanceled || l(e);
59
+ }, y = (e) => {
60
+ i?.(e), !e && f.current && (f.current = !1, g.current = !0, queueMicrotask(() => g.current = !1));
61
+ }, x = (e, n) => {
62
+ if (g.current) {
63
+ g.current = !1, n.cancel();
64
+ return;
65
+ }
66
+ u.current = e, a?.(e, n), d(e) && t === void 0 && c && (f.current = !0, l(!1));
67
+ }, S = t ?? (e > 0 ? c : void 0);
68
+ return /* @__PURE__ */ h(b, {
69
+ value: s,
70
+ children: /* @__PURE__ */ h(_.Root, {
71
+ open: S,
72
+ defaultOpen: n,
73
+ onOpenChange: v,
74
+ onOpenChangeComplete: y,
75
+ onInputValueChange: x,
76
+ ...o
57
77
  })
58
78
  });
59
79
  };
60
- g.displayName = "Combobox.Root";
61
- var _ = /* @__PURE__ */ new Set([
80
+ x.displayName = "Combobox.Root";
81
+ var S = /* @__PURE__ */ new Set([
62
82
  "value",
63
83
  "defaultValue",
64
84
  "onChange"
65
- ]), v = (e) => {
66
- let i = l(r), a = n(), o = i ? Object.fromEntries(Object.entries(i).filter(([e]) => !_.has(e))) : null, s = !("aria-label" in e || "aria-labelledby" in e) && a ? { "aria-labelledby": a.labelId } : null;
85
+ ]), C = (e) => {
86
+ let i = f(r), a = n(), o = i ? Object.fromEntries(Object.entries(i).filter(([e]) => !S.has(e))) : null, s = !("aria-label" in e || "aria-labelledby" in e) && a ? { "aria-labelledby": a.labelId } : null;
67
87
  return t({
68
88
  ...o,
69
89
  ...s
70
90
  }, e);
71
- }, y = ({ clearLabel: t = "Clear", ...n }) => {
72
- let { className: r, size: o = "md", ...s } = v(n), c = l(h);
73
- return /* @__PURE__ */ d("div", {
91
+ }, w = ({ variant: t = "search", showClear: n = t === "search", clearLabel: r = "Clear", triggerLabel: s = "Show options", ...u }) => {
92
+ let { className: d, size: p = "md", ...m } = C(u), v = f(b);
93
+ return /* @__PURE__ */ g(_.InputGroup, {
94
+ render: /* @__PURE__ */ h(a.Root, {}),
74
95
  "data-slot": "combobox-field",
75
- className: m.field(),
76
- children: [/* @__PURE__ */ u(f.Input, {
77
- "data-slot": "combobox-input",
78
- "data-size": o,
79
- className: e(i({ size: o }), "pr-11", r),
80
- ...s
81
- }), !c && /* @__PURE__ */ u(f.Clear, {
82
- "data-slot": "combobox-clear",
83
- "aria-label": t,
84
- className: m.clear(),
85
- children: /* @__PURE__ */ u(a, {})
86
- })]
96
+ className: t === "plain" ? "px-4" : void 0,
97
+ children: [
98
+ t === "search" && /* @__PURE__ */ h(a.Addon, { children: /* @__PURE__ */ h(l, {}) }),
99
+ /* @__PURE__ */ h(_.Input, {
100
+ "data-slot": "combobox-input",
101
+ "data-size": p,
102
+ className: e(i({ size: p }), d),
103
+ ...m
104
+ }),
105
+ n && !v && /* @__PURE__ */ h(_.Clear, {
106
+ "data-slot": "combobox-clear",
107
+ "aria-label": r,
108
+ className: y.action(),
109
+ children: /* @__PURE__ */ h(o, {})
110
+ }),
111
+ t === "select" && !v && /* @__PURE__ */ h(_.Trigger, {
112
+ "data-slot": "combobox-trigger",
113
+ "aria-label": s,
114
+ className: e(y.action(), y.trigger(), n && y.triggerReplaced()),
115
+ children: /* @__PURE__ */ h(c, {})
116
+ })
117
+ ]
87
118
  });
88
119
  };
89
- y.displayName = "Combobox.Input";
90
- var b = ({ className: t, side: n = "bottom", align: r = "start", sideOffset: i = 12, alignOffset: a, anchor: o, collisionPadding: s = 8, ...c }) => /* @__PURE__ */ u(f.Portal, { children: /* @__PURE__ */ u(f.Positioner, {
120
+ w.displayName = "Combobox.Input";
121
+ var T = ({ className: t, side: n = "bottom", align: r = "start", sideOffset: i = 12, alignOffset: a, anchor: o, collisionPadding: s = 8, ...c }) => /* @__PURE__ */ h(_.Portal, { children: /* @__PURE__ */ h(_.Positioner, {
91
122
  side: n,
92
123
  align: r,
93
124
  sideOffset: i,
@@ -95,49 +126,49 @@ var b = ({ className: t, side: n = "bottom", align: r = "start", sideOffset: i =
95
126
  anchor: o,
96
127
  collisionPadding: s,
97
128
  className: "isolate z-(--z-index-top-layer) pointer-events-auto",
98
- children: /* @__PURE__ */ u(f.Popup, {
129
+ children: /* @__PURE__ */ h(_.Popup, {
99
130
  "data-slot": "combobox-content",
100
- className: e(m.content(), t),
131
+ className: e(y.content(), t),
101
132
  ...c
102
133
  })
103
134
  }) });
104
- b.displayName = "Combobox.Content";
105
- var x = ({ className: t, ...n }) => {
135
+ T.displayName = "Combobox.Content";
136
+ var E = ({ className: t, ...n }) => {
106
137
  let r = "aria-label" in n || "aria-labelledby" in n;
107
- return /* @__PURE__ */ u(f.List, {
138
+ return /* @__PURE__ */ h(_.List, {
108
139
  "data-slot": "combobox-list",
109
140
  "aria-label": r ? void 0 : "Suggestions",
110
- className: e(m.list(), t),
141
+ className: e(y.list(), t),
111
142
  ...n
112
143
  });
113
144
  };
114
- x.displayName = "Combobox.List";
115
- var S = ({ className: t, children: n, ...r }) => {
116
- let i = l(h);
117
- return /* @__PURE__ */ d(f.Item, {
145
+ E.displayName = "Combobox.List";
146
+ var D = ({ className: t, children: n, ...r }) => {
147
+ let i = f(b);
148
+ return /* @__PURE__ */ g(_.Item, {
118
149
  "data-slot": "combobox-item",
119
- className: e(m.item(), t),
150
+ className: e(y.item(), t),
120
151
  ...r,
121
- children: [i && /* @__PURE__ */ u(f.ItemIndicator, {
152
+ children: [i && /* @__PURE__ */ h(_.ItemIndicator, {
122
153
  keepMounted: !0,
123
154
  "data-slot": "combobox-item-indicator",
124
- className: m.indicator(),
125
- children: /* @__PURE__ */ u(o, { className: "size-3.5" })
155
+ className: y.indicator(),
156
+ children: /* @__PURE__ */ h(s, { className: "size-3.5" })
126
157
  }), n]
127
158
  });
128
159
  };
129
- S.displayName = "Combobox.Item";
130
- var C = ({ className: t, ...n }) => /* @__PURE__ */ u(f.Empty, {
160
+ D.displayName = "Combobox.Item";
161
+ var O = ({ className: t, ...n }) => /* @__PURE__ */ h(_.Empty, {
131
162
  "data-slot": "combobox-empty",
132
- className: e(m.message(), t),
163
+ className: e(y.message(), t),
133
164
  ...n
134
165
  });
135
- C.displayName = "Combobox.Empty";
136
- var w = ({ className: t, ...n }) => /* @__PURE__ */ u(f.Status, {
166
+ O.displayName = "Combobox.Empty";
167
+ var k = ({ className: t, ...n }) => /* @__PURE__ */ h(_.Status, {
137
168
  "data-slot": "combobox-status",
138
- className: e(m.message(), t),
169
+ className: e(y.message(), t),
139
170
  ...n
140
171
  });
141
- w.displayName = "Combobox.Status";
172
+ k.displayName = "Combobox.Status";
142
173
  //#endregion
143
- export { b as Content, C as Empty, y as Input, S as Item, x as List, g as Root, w as Status, p as comboboxRecipe };
174
+ export { T as Content, O as Empty, w as Input, D as Item, E as List, x as Root, k as Status, v as comboboxRecipe };
@@ -16,3 +16,19 @@ export declare const IconCancel: (props: SVGProps<SVGSVGElement>) => import("rea
16
16
  * @summary 24px check used by the Combobox selection indicator
17
17
  */
18
18
  export declare const IconCheck: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
19
+ /**
20
+ * Magnifier leading the `search` variant of `Combobox.Input`, the same glyph
21
+ * `Command` uses. Fill color comes from the input group addon, so it follows
22
+ * the field's disabled state.
23
+ *
24
+ * @summary 24px search icon leading the Combobox search field
25
+ */
26
+ export declare const IconSearch: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
27
+ /**
28
+ * Downward chevron inside the `chevron` variant's trigger, the same glyph
29
+ * `Accordion` uses. Fill color is supplied by the trigger's class so it
30
+ * follows the button's state.
31
+ *
32
+ * @summary 24px chevron-down icon used as the Combobox list trigger
33
+ */
34
+ export declare const IconKeyboardArrowDown: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
@@ -18,6 +18,24 @@ var t = (t) => /* @__PURE__ */ e("svg", {
18
18
  xmlns: "http://www.w3.org/2000/svg",
19
19
  ...t,
20
20
  children: /* @__PURE__ */ e("path", { d: "M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" })
21
+ }), r = (t) => /* @__PURE__ */ e("svg", {
22
+ width: "24",
23
+ height: "24",
24
+ viewBox: "0 0 24 24",
25
+ fill: "none",
26
+ "aria-hidden": "true",
27
+ xmlns: "http://www.w3.org/2000/svg",
28
+ ...t,
29
+ children: /* @__PURE__ */ e("path", { d: "M9.52 15.615q-2.562 0-4.34-1.777Q3.405 12.062 3.405 9.5T5.18 5.162q1.776-1.777 4.338-1.777t4.339 1.777q1.777 1.776 1.777 4.338 0 1.071-.36 2.046a5.7 5.7 0 0 1-.96 1.696l5.754 5.754q.209.209.213.522a.7.7 0 0 1-.213.532.72.72 0 0 1-.527.217.72.72 0 0 1-.527-.217l-5.753-5.754q-.75.62-1.725.97t-2.018.35m0-1.5q1.933 0 3.273-1.341 1.342-1.34 1.342-3.274 0-1.932-1.342-3.274-1.34-1.341-3.274-1.341-1.932 0-3.274 1.34Q4.904 7.569 4.904 9.5q0 1.933 1.341 3.274 1.342 1.341 3.274 1.341" })
30
+ }), i = (t) => /* @__PURE__ */ e("svg", {
31
+ width: "24",
32
+ height: "24",
33
+ viewBox: "0 0 24 24",
34
+ fill: "none",
35
+ "aria-hidden": "true",
36
+ xmlns: "http://www.w3.org/2000/svg",
37
+ ...t,
38
+ children: /* @__PURE__ */ e("path", { d: "M12 14.677a.83.83 0 0 1-.633-.256L6.873 9.927a.73.73 0 0 1-.212-.522.7.7 0 0 1 .212-.532.72.72 0 0 1 .527-.217q.31 0 .527.217L12 12.946l4.073-4.073a.73.73 0 0 1 .522-.212.7.7 0 0 1 .532.212q.217.217.217.527a.72.72 0 0 1-.217.527l-4.494 4.494a.83.83 0 0 1-.633.256" })
21
39
  });
22
40
  //#endregion
23
- export { t as IconCancel, n as IconCheck };
41
+ export { t as IconCancel, n as IconCheck, i as IconKeyboardArrowDown, r as IconSearch };
@@ -1,13 +1,17 @@
1
1
  export { comboboxRecipe } from './combobox';
2
+ export type { ComboboxRootProps } from './combobox';
2
3
  export declare const Combobox: {
3
4
  Root: {
4
- <Value, Multiple extends boolean | undefined = false, Item = Value>({ onOpenChange, ...props }: import("@base-ui/react/combobox").ComboboxRoot.Props<Value, Multiple, Item>): import("react/jsx-runtime").JSX.Element;
5
+ <Value, Multiple extends boolean | undefined = false, Item = Value>({ minInputLength, open: openProp, defaultOpen, onOpenChange, onOpenChangeComplete, onInputValueChange, ...props }: import('./combobox').ComboboxRootProps<Value, Multiple, Item>): import("react/jsx-runtime").JSX.Element;
5
6
  displayName: string;
6
7
  };
7
8
  Input: {
8
- ({ clearLabel, ...props }: Omit<Omit<import('@base-ui/react/combobox').ComboboxInputProps, "ref"> & import('react').RefAttributes<HTMLInputElement>, "size"> & {
9
+ ({ variant, showClear, clearLabel, triggerLabel, ...props }: Omit<Omit<import('@base-ui/react/combobox').ComboboxInputProps, "ref"> & import('react').RefAttributes<HTMLInputElement>, "size"> & {
9
10
  size?: import('../input').InputSize;
11
+ variant?: "search" | "plain" | "select";
12
+ showClear?: boolean;
10
13
  clearLabel?: string;
14
+ triggerLabel?: string;
11
15
  }): import("react/jsx-runtime").JSX.Element;
12
16
  displayName: string;
13
17
  };