@agentero/design-system 0.31.8 → 0.32.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.
@@ -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">33 components ok</span><span class="filter-pill ok" aria-disabled="true">266 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">279 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>4.6s</strong>.
597
+ Using <code>react-component-meta</code>. Generation took <strong>5.2s</strong>.
598
598
  </div>
599
599
  <h2 class="section-title">Components</h2>
600
600
  <div class="grid" role="list">
@@ -7045,6 +7045,333 @@ they cannot change.</div>
7045
7045
  </div>
7046
7046
 
7047
7047
 
7048
+ </div>
7049
+ </article>
7050
+ <article
7051
+ class="card
7052
+ no-error
7053
+ no-info
7054
+ no-story-error
7055
+ no-doc-error"
7056
+ role="listitem"
7057
+ aria-label="InputGroup.Root">
7058
+ <div class="head">
7059
+ <div class="title">
7060
+ <h2><span class="status-dot dot-ok"></span> InputGroup.Root</h2>
7061
+ <div class="badges">
7062
+
7063
+
7064
+ <label for="c-18-components-inputgroup-stories" class="badge ok as-toggle">13 stories</label>
7065
+
7066
+
7067
+ </div>
7068
+ </div>
7069
+ <div class="meta" title="./src/input-group/input-group.stories.tsx">components-inputgroup · ./src/input-group/input-group.stories.tsx</div>
7070
+ <div>Bordered frame drawing an Input and its addons as one control</div>
7071
+ <div class="hint">InputGroup draws an `Input` and what sits beside it — an icon, a currency or
7072
+ unit affix, a trailing action — as one bordered control. The frame owns the
7073
+ border, the shadow and the focus ring; the `Input` inside renders bare and
7074
+ hands its state to the frame through `:has()`, so focus, `disabled`,
7075
+ `aria-invalid` and `size` are never wired by hand. Inside a `FieldText` the
7076
+ `Input` keeps taking its label and messages from the field.</div>
7077
+ <div class="kv"><span class="chip">summary: Bordered frame drawing an Input and its addons as one control</span><span class="chip">dataAttribute: {string} data-slot - Always set to &quot;input-group&quot;</span><span class="chip">example: &lt;InputGroup.Root&gt;
7078
+ &lt;InputGroup.Text&gt;$&lt;/InputGroup.Text&gt;
7079
+ &lt;Input inputMode=&quot;decimal&quot; placeholder=&quot;0.00&quot; /&gt;
7080
+ &lt;InputGroup.Text&gt;USD&lt;/InputGroup.Text&gt;
7081
+ &lt;/InputGroup.Root&gt;</span></div>
7082
+ </div>
7083
+
7084
+ <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7085
+
7086
+
7087
+ <input id="c-18-components-inputgroup-stories" class="tg tg-stories" type="checkbox" hidden />
7088
+
7089
+
7090
+
7091
+
7092
+ <div class="panels">
7093
+
7094
+
7095
+
7096
+
7097
+ <div class="panel panel-stories">
7098
+
7099
+
7100
+
7101
+ <div class="note ok">
7102
+ <div class="row">
7103
+ <span class="ex-name">Imports</span>
7104
+ </div>
7105
+ <pre><code>import {
7106
+ Button,
7107
+ Divider,
7108
+ DropdownMenu,
7109
+ Field,
7110
+ FieldText,
7111
+ IconCancel,
7112
+ IconKeyboardArrowDown,
7113
+ IconSearch,
7114
+ Input,
7115
+ InputGroup,
7116
+ Label,
7117
+ Tag,
7118
+ } from "@agentero/design-system";</code></pre>
7119
+ </div>
7120
+
7121
+
7122
+ <div class="note ok">
7123
+ <div class="row">
7124
+ <span class="ex-name">Default</span>
7125
+ <span class="badge ok">story ok</span>
7126
+ </div>
7127
+ <div>Leading icon addon with the frame drawing the focus ring</div>
7128
+ <div class="hint">A search field: the icon leads and the input takes the rest of the frame.
7129
+ Pressing the icon focuses the input, and the frame — not the input — draws the
7130
+ focus ring, so the two never show a double border.</div>
7131
+ <pre><code>const Default = () =&gt; (
7132
+ &lt;InputGroup.Root&gt;
7133
+ &lt;InputGroup.Addon&gt;
7134
+ &lt;IconSearch /&gt;
7135
+ &lt;/InputGroup.Addon&gt;
7136
+ &lt;Input type=&quot;search&quot; placeholder=&quot;Search agencies&quot; aria-label=&quot;Search agencies&quot; /&gt;
7137
+ &lt;/InputGroup.Root&gt;
7138
+ );</code></pre>
7139
+ </div>
7140
+ <div class="note ok">
7141
+ <div class="row">
7142
+ <span class="ex-name">Text Affixes</span>
7143
+ <span class="badge ok">story ok</span>
7144
+ </div>
7145
+ <div>Currency prefix and unit suffix around an amount</div>
7146
+ <div class="hint">Text affixes on both sides. They are decoration only — a screen reader does
7147
+ not read them as part of the value — so the unit is also in the label.</div>
7148
+ <pre><code>const TextAffixes = () =&gt; (
7149
+ &lt;InputGroup.Root&gt;
7150
+ &lt;InputGroup.Text&gt;$&lt;/InputGroup.Text&gt;
7151
+ &lt;Input inputMode=&quot;decimal&quot; placeholder=&quot;0.00&quot; aria-label=&quot;Premium in US dollars&quot; /&gt;
7152
+ &lt;InputGroup.Text&gt;USD&lt;/InputGroup.Text&gt;
7153
+ &lt;/InputGroup.Root&gt;
7154
+ );</code></pre>
7155
+ </div>
7156
+ <div class="note ok">
7157
+ <div class="row">
7158
+ <span class="ex-name">With Divider</span>
7159
+ <span class="badge ok">story ok</span>
7160
+ </div>
7161
+ <div>Protocol prefix and domain suffix split off by vertical dividers</div>
7162
+ <div class="hint">A `Divider` separates an affix from the value when it reads as a distinct
7163
+ segment, like a protocol before a domain. It spans the full height of the
7164
+ frame, meeting its border at both ends.</div>
7165
+ <pre><code>const WithDivider = () =&gt; (
7166
+ &lt;InputGroup.Root&gt;
7167
+ &lt;InputGroup.Text&gt;https://&lt;/InputGroup.Text&gt;
7168
+ &lt;Divider orientation=&quot;vertical&quot; /&gt;
7169
+ &lt;Input
7170
+ type=&quot;text&quot;
7171
+ inputMode=&quot;url&quot;
7172
+ autoCapitalize=&quot;none&quot;
7173
+ placeholder=&quot;www.agentero&quot;
7174
+ aria-label=&quot;Agency website&quot;
7175
+ /&gt;
7176
+ &lt;Divider orientation=&quot;vertical&quot; /&gt;
7177
+ &lt;InputGroup.Text&gt;.com&lt;/InputGroup.Text&gt;
7178
+ &lt;/InputGroup.Root&gt;
7179
+ );</code></pre>
7180
+ </div>
7181
+ <div class="note ok">
7182
+ <div class="row">
7183
+ <span class="ex-name">With Tags</span>
7184
+ <span class="badge ok">story ok</span>
7185
+ </div>
7186
+ <div>Leading and trailing tags tucked into the frame&#39;s padding</div>
7187
+ <div class="hint">A `Tag` in an addon labels what the value is. It tucks into the frame&#39;s
7188
+ padding on the side it touches and takes a tighter radius, so it sits inside
7189
+ the corner instead of competing with it.</div>
7190
+ <pre><code>const WithTags = () =&gt; (
7191
+ &lt;div className=&quot;flex flex-col gap-4&quot;&gt;
7192
+ &lt;InputGroup.Root&gt;
7193
+ &lt;InputGroup.Addon&gt;
7194
+ &lt;Tag color=&quot;informative&quot;&gt;FEIN&lt;/Tag&gt;
7195
+ &lt;/InputGroup.Addon&gt;
7196
+ &lt;Input placeholder=&quot;XX-XXXXXXX&quot; aria-label=&quot;FEIN&quot; /&gt;
7197
+ &lt;/InputGroup.Root&gt;
7198
+ &lt;InputGroup.Root&gt;
7199
+ &lt;InputGroup.Addon&gt;
7200
+ &lt;IconSearch /&gt;
7201
+ &lt;/InputGroup.Addon&gt;
7202
+ &lt;Input placeholder=&quot;Describe your filters&quot; aria-label=&quot;Describe your filters&quot; /&gt;
7203
+ &lt;InputGroup.Addon&gt;
7204
+ &lt;Tag color=&quot;informative&quot;&gt;Beta&lt;/Tag&gt;
7205
+ &lt;/InputGroup.Addon&gt;
7206
+ &lt;/InputGroup.Root&gt;
7207
+ &lt;/div&gt;
7208
+ );</code></pre>
7209
+ </div>
7210
+ <div class="note ok">
7211
+ <div class="row">
7212
+ <span class="ex-name">With Button</span>
7213
+ <span class="badge ok">story ok</span>
7214
+ </div>
7215
+
7216
+
7217
+ <pre><code>const WithButton = () =&gt; (
7218
+ &lt;InputGroup.Root&gt;
7219
+ &lt;Input value=&quot;https://agentero.com/r/abc123&quot; readOnly aria-label=&quot;Referral link&quot; /&gt;
7220
+ &lt;Divider orientation=&quot;vertical&quot; /&gt;
7221
+ &lt;InputGroup.Addon variant=&quot;action&quot;&gt;
7222
+ &lt;Button type=&quot;button&quot; variant=&quot;ghost&quot; size=&quot;md&quot; onClick={handleCopy}&gt;
7223
+ Copy link
7224
+ &lt;/Button&gt;
7225
+ &lt;/InputGroup.Addon&gt;
7226
+ &lt;/InputGroup.Root&gt;
7227
+ );</code></pre>
7228
+ </div>
7229
+ <div class="note ok">
7230
+ <div class="row">
7231
+ <span class="ex-name">Disabled</span>
7232
+ <span class="badge ok">story ok</span>
7233
+ </div>
7234
+ <div>Disabled input greying out the frame and its addons</div>
7235
+ <div class="hint">`disabled` on the input greys out the whole frame — background, border, icon
7236
+ and affix — and the frame stops offering the text cursor.</div>
7237
+ <pre><code>const Disabled = () =&gt; (
7238
+ &lt;InputGroup.Root&gt;
7239
+ &lt;InputGroup.Addon&gt;
7240
+ &lt;IconSearch /&gt;
7241
+ &lt;/InputGroup.Addon&gt;
7242
+ &lt;Input placeholder=&quot;Search agencies&quot; aria-label=&quot;Search agencies&quot; disabled /&gt;
7243
+ &lt;InputGroup.Text&gt;USD&lt;/InputGroup.Text&gt;
7244
+ &lt;/InputGroup.Root&gt;
7245
+ );</code></pre>
7246
+ </div>
7247
+ <div class="note ok">
7248
+ <div class="row">
7249
+ <span class="ex-name">Invalid</span>
7250
+ <span class="badge ok">story ok</span>
7251
+ </div>
7252
+ <div>Invalid input driving the frame&#39;s destructive border</div>
7253
+ <div class="hint">`aria-invalid` on the input moves the destructive border to the frame, and
7254
+ the ring follows it on focus.</div>
7255
+ <pre><code>const Invalid = () =&gt; (
7256
+ &lt;InputGroup.Root&gt;
7257
+ &lt;InputGroup.Text&gt;$&lt;/InputGroup.Text&gt;
7258
+ &lt;Input defaultValue=&quot;-40&quot; aria-invalid aria-label=&quot;Premium in US dollars&quot; /&gt;
7259
+ &lt;/InputGroup.Root&gt;
7260
+ );</code></pre>
7261
+ </div>
7262
+ <div class="note ok">
7263
+ <div class="row">
7264
+ <span class="ex-name">All Sizes</span>
7265
+ <span class="badge ok">story ok</span>
7266
+ </div>
7267
+ <div>The three input sizes inside the frame, heights matching Input</div>
7268
+ <div class="hint">The frame follows the input&#39;s `size`: the same height as a standalone
7269
+ `Input` of that size, and the larger corner radius with `lg`.</div>
7270
+ <pre><code>const AllSizes = () =&gt; (
7271
+ &lt;div className=&quot;flex flex-col gap-4&quot;&gt;
7272
+ {SIZES.map(size =&gt; (
7273
+ &lt;InputGroup.Root key={size}&gt;
7274
+ &lt;InputGroup.Addon&gt;
7275
+ &lt;IconSearch /&gt;
7276
+ &lt;/InputGroup.Addon&gt;
7277
+ &lt;Input size={size} placeholder={`Size ${size}`} aria-label={`Size ${size}`} /&gt;
7278
+ &lt;/InputGroup.Root&gt;
7279
+ ))}
7280
+ &lt;/div&gt;
7281
+ );</code></pre>
7282
+ </div>
7283
+ <div class="note ok">
7284
+ <div class="row">
7285
+ <span class="ex-name">Inside Field Text</span>
7286
+ <span class="badge ok">story ok</span>
7287
+ </div>
7288
+ <div>Label and description reaching the input through the frame</div>
7289
+ <div class="hint">Inside a `FieldText` nothing changes for the group: the `Input` reads the
7290
+ field&#39;s context at any depth, so the label names it and the description
7291
+ describes it with no ids written by hand. The frame is not in the way.</div>
7292
+ <pre><code>const InsideFieldText = () =&gt; (
7293
+ &lt;FieldText required&gt;
7294
+ &lt;Label&gt;Agency website&lt;/Label&gt;
7295
+ &lt;InputGroup.Root&gt;
7296
+ &lt;InputGroup.Text&gt;https://&lt;/InputGroup.Text&gt;
7297
+ &lt;Input type=&quot;text&quot; inputMode=&quot;url&quot; autoCapitalize=&quot;none&quot; placeholder=&quot;www.agentero.com&quot; /&gt;
7298
+ &lt;/InputGroup.Root&gt;
7299
+ &lt;Field.Description&gt;Shown on your public profile.&lt;/Field.Description&gt;
7300
+ &lt;/FieldText&gt;
7301
+ );</code></pre>
7302
+ </div>
7303
+ <div class="note ok">
7304
+ <div class="row">
7305
+ <span class="ex-name">Search Field</span>
7306
+ <span class="badge ok">story ok</span>
7307
+ </div>
7308
+
7309
+
7310
+ <pre><code>const SearchField = () =&gt; (
7311
+ &lt;form onSubmit={handleSearchSubmit}&gt;
7312
+ &lt;FieldStates
7313
+ label=&quot;Agency&quot;
7314
+ tooltip=&quot;Search by agency name or NPN.&quot;
7315
+ required
7316
+ error=&quot;Pick an agency from the list.&quot;&gt;
7317
+ &lt;SearchInput /&gt;
7318
+ &lt;/FieldStates&gt;
7319
+ &lt;/form&gt;
7320
+ );</code></pre>
7321
+ </div>
7322
+ <div class="note ok">
7323
+ <div class="row">
7324
+ <span class="ex-name">Email Field</span>
7325
+ <span class="badge ok">story ok</span>
7326
+ </div>
7327
+ <div>Email field with a leading icon, valid and invalid</div>
7328
+ <div class="hint">The email field from the Portal UI library: a leading icon is all it adds
7329
+ to the input.</div>
7330
+ <pre><code>const EmailField = () =&gt; (
7331
+ &lt;FieldStates label=&quot;Email&quot; required error=&quot;Enter a valid email address.&quot;&gt;
7332
+ &lt;InputGroup.Root&gt;
7333
+ &lt;InputGroup.Addon&gt;
7334
+ &lt;IconMail /&gt;
7335
+ &lt;/InputGroup.Addon&gt;
7336
+ &lt;Input type=&quot;email&quot; placeholder=&quot;me@email.com&quot; /&gt;
7337
+ &lt;/InputGroup.Root&gt;
7338
+ &lt;/FieldStates&gt;
7339
+ );</code></pre>
7340
+ </div>
7341
+ <div class="note ok">
7342
+ <div class="row">
7343
+ <span class="ex-name">Price Field</span>
7344
+ <span class="badge ok">story ok</span>
7345
+ </div>
7346
+ <div>Price field with a symbol and a currency picker, valid and invalid</div>
7347
+ <div class="hint">The price field from the Portal UI library: a currency symbol before the
7348
+ value and a currency picker after it. The picker is a `DropdownMenu` whose
7349
+ trigger fills an `action` addon, so it keeps the frame&#39;s height and an inset
7350
+ focus ring.</div>
7351
+ <pre><code>const PriceField = () =&gt; (
7352
+ &lt;FieldStates label=&quot;Price&quot; required error=&quot;Enter a price above zero.&quot;&gt;
7353
+ &lt;PriceInput /&gt;
7354
+ &lt;/FieldStates&gt;
7355
+ );</code></pre>
7356
+ </div>
7357
+ <div class="note ok">
7358
+ <div class="row">
7359
+ <span class="ex-name">Phone Field</span>
7360
+ <span class="badge ok">story ok</span>
7361
+ </div>
7362
+ <div>Phone field with a leading country code picker, valid and invalid</div>
7363
+ <div class="hint">The optional phone field from the Portal UI library: a country code picker
7364
+ before the number. The picker leads, so the `action` addon rounds its
7365
+ button&#39;s start corners with the frame.</div>
7366
+ <pre><code>const PhoneField = () =&gt; (
7367
+ &lt;FieldStates label=&quot;Phone number&quot; error=&quot;Enter a 10-digit phone number.&quot;&gt;
7368
+ &lt;PhoneInput /&gt;
7369
+ &lt;/FieldStates&gt;
7370
+ );</code></pre>
7371
+ </div>
7372
+ </div>
7373
+
7374
+
7048
7375
  </div>
7049
7376
  </article>
7050
7377
  <article
@@ -7059,9 +7386,9 @@ they cannot change.</div>
7059
7386
  <div class="title">
7060
7387
  <h2><span class="status-dot dot-ok"></span> Label</h2>
7061
7388
  <div class="badges">
7062
- <label for="c-18-components-label-props" class="badge ok as-toggle">3 prop types</label>
7389
+ <label for="c-19-components-label-props" class="badge ok as-toggle">3 prop types</label>
7063
7390
 
7064
- <label for="c-18-components-label-stories" class="badge ok as-toggle">5 stories</label>
7391
+ <label for="c-19-components-label-stories" class="badge ok as-toggle">5 stories</label>
7065
7392
 
7066
7393
 
7067
7394
  </div>
@@ -7081,10 +7408,10 @@ still has to carry `required` / `aria-required`.</div>
7081
7408
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7082
7409
 
7083
7410
 
7084
- <input id="c-18-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
7411
+ <input id="c-19-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
7085
7412
 
7086
7413
 
7087
- <input id="c-18-components-label-props" class="tg tg-props" type="checkbox" hidden />
7414
+ <input id="c-19-components-label-props" class="tg tg-props" type="checkbox" hidden />
7088
7415
 
7089
7416
  <div class="panels">
7090
7417
 
@@ -7204,9 +7531,9 @@ field&#39;s control with no `htmlFor`. An explicit `htmlFor` still wins.</div>
7204
7531
  <div class="title">
7205
7532
  <h2><span class="status-dot dot-ok"></span> Loading</h2>
7206
7533
  <div class="badges">
7207
- <label for="c-19-components-loading-props" class="badge ok as-toggle">1 prop type</label>
7534
+ <label for="c-20-components-loading-props" class="badge ok as-toggle">1 prop type</label>
7208
7535
 
7209
- <label for="c-19-components-loading-stories" class="badge ok as-toggle">7 stories</label>
7536
+ <label for="c-20-components-loading-stories" class="badge ok as-toggle">7 stories</label>
7210
7537
 
7211
7538
 
7212
7539
  </div>
@@ -7223,10 +7550,10 @@ parent font size via `em`-based size variants (`sm`, `md`, `lg`).</div>
7223
7550
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7224
7551
 
7225
7552
 
7226
- <input id="c-19-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
7553
+ <input id="c-20-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
7227
7554
 
7228
7555
 
7229
- <input id="c-19-components-loading-props" class="tg tg-props" type="checkbox" hidden />
7556
+ <input id="c-20-components-loading-props" class="tg tg-props" type="checkbox" hidden />
7230
7557
 
7231
7558
  <div class="panels">
7232
7559
 
@@ -7377,9 +7704,9 @@ against the dark background.</div>
7377
7704
  <div class="title">
7378
7705
  <h2><span class="status-dot dot-ok"></span> Modal.Root</h2>
7379
7706
  <div class="badges">
7380
- <label for="c-20-components-modal-props" class="badge ok as-toggle">5 prop types</label>
7707
+ <label for="c-21-components-modal-props" class="badge ok as-toggle">5 prop types</label>
7381
7708
 
7382
- <label for="c-20-components-modal-stories" class="badge ok as-toggle">6 stories</label>
7709
+ <label for="c-21-components-modal-stories" class="badge ok as-toggle">6 stories</label>
7383
7710
 
7384
7711
 
7385
7712
  </div>
@@ -7425,10 +7752,10 @@ import { Modal } from &#39;@agentero/design-system/modal&#39;;
7425
7752
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7426
7753
 
7427
7754
 
7428
- <input id="c-20-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
7755
+ <input id="c-21-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
7429
7756
 
7430
7757
 
7431
- <input id="c-20-components-modal-props" class="tg tg-props" type="checkbox" hidden />
7758
+ <input id="c-21-components-modal-props" class="tg tg-props" type="checkbox" hidden />
7432
7759
 
7433
7760
  <div class="panels">
7434
7761
 
@@ -7648,9 +7975,9 @@ announced as an `alertdialog`. Only the footer actions close it.</div>
7648
7975
  <div class="title">
7649
7976
  <h2><span class="status-dot dot-ok"></span> Pagination</h2>
7650
7977
  <div class="badges">
7651
- <label for="c-21-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
7978
+ <label for="c-22-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
7652
7979
 
7653
- <label for="c-21-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
7980
+ <label for="c-22-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
7654
7981
 
7655
7982
 
7656
7983
  </div>
@@ -7680,10 +8007,10 @@ const [page, setPage] = useState(1);
7680
8007
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7681
8008
 
7682
8009
 
7683
- <input id="c-21-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
8010
+ <input id="c-22-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
7684
8011
 
7685
8012
 
7686
- <input id="c-21-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
8013
+ <input id="c-22-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
7687
8014
 
7688
8015
  <div class="panels">
7689
8016
 
@@ -7838,9 +8165,9 @@ entries summary follow along.</div>
7838
8165
  <div class="title">
7839
8166
  <h2><span class="status-dot dot-ok"></span> Popover.Root</h2>
7840
8167
  <div class="badges">
7841
- <label for="c-22-components-popover-props" class="badge ok as-toggle">5 prop types</label>
8168
+ <label for="c-23-components-popover-props" class="badge ok as-toggle">5 prop types</label>
7842
8169
 
7843
- <label for="c-22-components-popover-stories" class="badge ok as-toggle">5 stories</label>
8170
+ <label for="c-23-components-popover-stories" class="badge ok as-toggle">5 stories</label>
7844
8171
 
7845
8172
 
7846
8173
  </div>
@@ -7869,10 +8196,10 @@ import { Popover } from &#39;@agentero/design-system/popover&#39;;
7869
8196
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7870
8197
 
7871
8198
 
7872
- <input id="c-22-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
8199
+ <input id="c-23-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
7873
8200
 
7874
8201
 
7875
- <input id="c-22-components-popover-props" class="tg tg-props" type="checkbox" hidden />
8202
+ <input id="c-23-components-popover-props" class="tg tg-props" type="checkbox" hidden />
7876
8203
 
7877
8204
  <div class="panels">
7878
8205
 
@@ -8071,9 +8398,9 @@ focus returning to its trigger once the modal closes.</div>
8071
8398
  <div class="title">
8072
8399
  <h2><span class="status-dot dot-ok"></span> Progress</h2>
8073
8400
  <div class="badges">
8074
- <label for="c-23-components-progress-props" class="badge ok as-toggle">4 prop types</label>
8401
+ <label for="c-24-components-progress-props" class="badge ok as-toggle">4 prop types</label>
8075
8402
 
8076
- <label for="c-23-components-progress-stories" class="badge ok as-toggle">4 stories</label>
8403
+ <label for="c-24-components-progress-stories" class="badge ok as-toggle">4 stories</label>
8077
8404
 
8078
8405
 
8079
8406
  </div>
@@ -8092,10 +8419,10 @@ with `currentColor`, so recolor it with a plain text class — e.g.
8092
8419
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8093
8420
 
8094
8421
 
8095
- <input id="c-23-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
8422
+ <input id="c-24-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
8096
8423
 
8097
8424
 
8098
- <input id="c-23-components-progress-props" class="tg tg-props" type="checkbox" hidden />
8425
+ <input id="c-24-components-progress-props" class="tg tg-props" type="checkbox" hidden />
8099
8426
 
8100
8427
  <div class="panels">
8101
8428
 
@@ -8214,7 +8541,7 @@ percentage: number</code></pre>
8214
8541
  <div class="badges">
8215
8542
 
8216
8543
 
8217
- <label for="c-24-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
8544
+ <label for="c-25-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
8218
8545
 
8219
8546
 
8220
8547
  </div>
@@ -8231,7 +8558,7 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
8231
8558
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8232
8559
 
8233
8560
 
8234
- <input id="c-24-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
8561
+ <input id="c-25-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
8235
8562
 
8236
8563
 
8237
8564
 
@@ -8327,9 +8654,9 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
8327
8654
  <div class="title">
8328
8655
  <h2><span class="status-dot dot-ok"></span> Switch</h2>
8329
8656
  <div class="badges">
8330
- <label for="c-25-components-switch-props" class="badge ok as-toggle">8 prop types</label>
8657
+ <label for="c-26-components-switch-props" class="badge ok as-toggle">8 prop types</label>
8331
8658
 
8332
- <label for="c-25-components-switch-stories" class="badge ok as-toggle">7 stories</label>
8659
+ <label for="c-26-components-switch-stories" class="badge ok as-toggle">7 stories</label>
8333
8660
 
8334
8661
 
8335
8662
  </div>
@@ -8345,10 +8672,10 @@ a Checkbox for a single boolean that applies instantly, and pair it with a
8345
8672
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8346
8673
 
8347
8674
 
8348
- <input id="c-25-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
8675
+ <input id="c-26-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
8349
8676
 
8350
8677
 
8351
- <input id="c-25-components-switch-props" class="tg tg-props" type="checkbox" hidden />
8678
+ <input id="c-26-components-switch-props" class="tg tg-props" type="checkbox" hidden />
8352
8679
 
8353
8680
  <div class="panels">
8354
8681
 
@@ -8529,9 +8856,9 @@ size?: &quot;sm&quot; | &quot;md&quot;</code></pre>
8529
8856
  <div class="title">
8530
8857
  <h2><span class="status-dot dot-ok"></span> Table.Root</h2>
8531
8858
  <div class="badges">
8532
- <label for="c-26-components-table-props" class="badge ok as-toggle">6 prop types</label>
8859
+ <label for="c-27-components-table-props" class="badge ok as-toggle">6 prop types</label>
8533
8860
 
8534
- <label for="c-26-components-table-stories" class="badge ok as-toggle">10 stories</label>
8861
+ <label for="c-27-components-table-stories" class="badge ok as-toggle">10 stories</label>
8535
8862
 
8536
8863
 
8537
8864
  </div>
@@ -8562,10 +8889,10 @@ import { Table } from &#39;@agentero/design-system/table&#39;;
8562
8889
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8563
8890
 
8564
8891
 
8565
- <input id="c-26-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
8892
+ <input id="c-27-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
8566
8893
 
8567
8894
 
8568
- <input id="c-26-components-table-props" class="tg tg-props" type="checkbox" hidden />
8895
+ <input id="c-27-components-table-props" class="tg tg-props" type="checkbox" hidden />
8569
8896
 
8570
8897
  <div class="panels">
8571
8898
 
@@ -8783,9 +9110,9 @@ keeps its padding.</div>
8783
9110
  <div class="title">
8784
9111
  <h2><span class="status-dot dot-ok"></span> Tabs.Root</h2>
8785
9112
  <div class="badges">
8786
- <label for="c-27-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
9113
+ <label for="c-28-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
8787
9114
 
8788
- <label for="c-27-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
9115
+ <label for="c-28-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
8789
9116
 
8790
9117
 
8791
9118
  </div>
@@ -8816,10 +9143,10 @@ import { Tabs } from &#39;@agentero/design-system/tabs&#39;;
8816
9143
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8817
9144
 
8818
9145
 
8819
- <input id="c-27-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
9146
+ <input id="c-28-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
8820
9147
 
8821
9148
 
8822
- <input id="c-27-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
9149
+ <input id="c-28-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
8823
9150
 
8824
9151
  <div class="panels">
8825
9152
 
@@ -9040,9 +9367,9 @@ tablist role is announced.</div>
9040
9367
  <div class="title">
9041
9368
  <h2><span class="status-dot dot-ok"></span> Tag</h2>
9042
9369
  <div class="badges">
9043
- <label for="c-28-components-tag-props" class="badge ok as-toggle">8 prop types</label>
9370
+ <label for="c-29-components-tag-props" class="badge ok as-toggle">8 prop types</label>
9044
9371
 
9045
- <label for="c-28-components-tag-stories" class="badge ok as-toggle">12 stories</label>
9372
+ <label for="c-29-components-tag-stories" class="badge ok as-toggle">12 stories</label>
9046
9373
 
9047
9374
 
9048
9375
  </div>
@@ -9061,10 +9388,10 @@ it an interactive link or button, which adds the pointer cursor and hover fill.<
9061
9388
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9062
9389
 
9063
9390
 
9064
- <input id="c-28-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
9391
+ <input id="c-29-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
9065
9392
 
9066
9393
 
9067
- <input id="c-28-components-tag-props" class="tg tg-props" type="checkbox" hidden />
9394
+ <input id="c-29-components-tag-props" class="tg tg-props" type="checkbox" hidden />
9068
9395
 
9069
9396
  <div class="panels">
9070
9397
 
@@ -9489,9 +9816,9 @@ leading + trailing icons, and icon-only.</div>
9489
9816
  <div class="title">
9490
9817
  <h2><span class="status-dot dot-ok"></span> TextArea</h2>
9491
9818
  <div class="badges">
9492
- <label for="c-29-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
9819
+ <label for="c-30-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
9493
9820
 
9494
- <label for="c-29-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
9821
+ <label for="c-30-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
9495
9822
 
9496
9823
 
9497
9824
  </div>
@@ -9508,10 +9835,10 @@ states. It wires nothing on its own: `id`, `aria-invalid` and
9508
9835
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9509
9836
 
9510
9837
 
9511
- <input id="c-29-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
9838
+ <input id="c-30-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
9512
9839
 
9513
9840
 
9514
- <input id="c-29-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
9841
+ <input id="c-30-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
9515
9842
 
9516
9843
  <div class="panels">
9517
9844
 
@@ -9643,9 +9970,9 @@ size and the minimum height shows three rows.</div>
9643
9970
  <div class="title">
9644
9971
  <h2><span class="status-dot dot-ok"></span> Toast</h2>
9645
9972
  <div class="badges">
9646
- <label for="c-30-components-toast-props" class="badge ok as-toggle">11 prop types</label>
9973
+ <label for="c-31-components-toast-props" class="badge ok as-toggle">11 prop types</label>
9647
9974
 
9648
- <label for="c-30-components-toast-stories" class="badge ok as-toggle">11 stories</label>
9975
+ <label for="c-31-components-toast-stories" class="badge ok as-toggle">11 stories</label>
9649
9976
 
9650
9977
 
9651
9978
  </div>
@@ -9664,10 +9991,10 @@ common feedback patterns.</div>
9664
9991
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9665
9992
 
9666
9993
 
9667
- <input id="c-30-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
9994
+ <input id="c-31-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
9668
9995
 
9669
9996
 
9670
- <input id="c-30-components-toast-props" class="tg tg-props" type="checkbox" hidden />
9997
+ <input id="c-31-components-toast-props" class="tg tg-props" type="checkbox" hidden />
9671
9998
 
9672
9999
  <div class="panels">
9673
10000
 
@@ -10096,9 +10423,9 @@ action (e.g., a sync icon for a sync-in-progress toast).</div>
10096
10423
  <div class="title">
10097
10424
  <h2><span class="status-dot dot-ok"></span> Tooltip</h2>
10098
10425
  <div class="badges">
10099
- <label for="c-31-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
10426
+ <label for="c-32-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
10100
10427
 
10101
- <label for="c-31-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
10428
+ <label for="c-32-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
10102
10429
 
10103
10430
 
10104
10431
  </div>
@@ -10117,10 +10444,10 @@ on hover or focus.</div>
10117
10444
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10118
10445
 
10119
10446
 
10120
- <input id="c-31-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
10447
+ <input id="c-32-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
10121
10448
 
10122
10449
 
10123
- <input id="c-31-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
10450
+ <input id="c-32-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
10124
10451
 
10125
10452
  <div class="panels">
10126
10453
 
@@ -10292,9 +10619,9 @@ sideOffset?: number = 4</code></pre>
10292
10619
  <div class="title">
10293
10620
  <h2><span class="status-dot dot-ok"></span> ValidationList</h2>
10294
10621
  <div class="badges">
10295
- <label for="c-32-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
10622
+ <label for="c-33-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
10296
10623
 
10297
- <label for="c-32-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
10624
+ <label for="c-33-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
10298
10625
 
10299
10626
 
10300
10627
  </div>
@@ -10315,10 +10642,10 @@ your form validation; it does not own the input or block submission.</div>
10315
10642
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10316
10643
 
10317
10644
 
10318
- <input id="c-32-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
10645
+ <input id="c-33-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
10319
10646
 
10320
10647
 
10321
- <input id="c-32-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
10648
+ <input id="c-33-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
10322
10649
 
10323
10650
  <div class="panels">
10324
10651
 
@@ -6460,6 +6460,132 @@
6460
6460
  ],
6461
6461
  "import": "import { Avatar, Button, HoverCard } from \"@agentero/design-system\";"
6462
6462
  },
6463
+ "components-inputgroup": {
6464
+ "id": "components-inputgroup",
6465
+ "name": "InputGroup.Root",
6466
+ "path": "./src/input-group/input-group.stories.tsx",
6467
+ "jsDocTags": {
6468
+ "summary": [
6469
+ "Bordered frame drawing an Input and its addons as one control"
6470
+ ],
6471
+ "dataAttribute": [
6472
+ "{string} data-slot - Always set to \"input-group\""
6473
+ ],
6474
+ "example": [
6475
+ "<InputGroup.Root>\n <InputGroup.Text>$</InputGroup.Text>\n <Input inputMode=\"decimal\" placeholder=\"0.00\" />\n <InputGroup.Text>USD</InputGroup.Text>\n</InputGroup.Root>"
6476
+ ]
6477
+ },
6478
+ "description": "InputGroup draws an `Input` and what sits beside it — an icon, a currency or\nunit affix, a trailing action — as one bordered control. The frame owns the\nborder, the shadow and the focus ring; the `Input` inside renders bare and\nhands its state to the frame through `:has()`, so focus, `disabled`,\n`aria-invalid` and `size` are never wired by hand. Inside a `FieldText` the\n`Input` keeps taking its label and messages from the field.",
6479
+ "summary": "Bordered frame drawing an Input and its addons as one control",
6480
+ "reactComponentMeta": {
6481
+ "displayName": "InputGroup.Root",
6482
+ "exportName": "InputGroup",
6483
+ "filePath": "/home/runner/work/design-system/design-system/src/input-group/index.ts",
6484
+ "description": "The bordered frame of an input group: one box around an `Input` and its\naddons, drawn as a single control. It carries the border, the shadow, the\nfocus ring and the horizontal padding; the `Input` inside renders bare and\ngives the frame its state — focus, `disabled`, `aria-invalid` and `size`\nall propagate through `:has()`, so nothing is passed to the frame by hand.\n\nPut the `Input` and any `InputGroup.Addon` or `InputGroup.Text` inside, in\nreading order. Inside a `FieldText` the `Input` still takes its label and\nmessages from the field, at any depth; the frame needs nothing.\n\nUse it for an input with a leading icon, a currency or unit affix, or a\ntrailing action. A plain input with nothing around it is just `Input`, and a\nbutton beside an input that submits on its own is a form layout, not a group.\nNot a `role=\"group\"`, like `Field`.",
6485
+ "jsDocTags": {
6486
+ "summary": [
6487
+ "Bordered frame drawing an Input and its addons as one control"
6488
+ ],
6489
+ "dataAttribute": [
6490
+ "{string} data-slot - Always set to \"input-group\""
6491
+ ],
6492
+ "example": [
6493
+ "<InputGroup.Root>\n <InputGroup.Text>$</InputGroup.Text>\n <Input inputMode=\"decimal\" placeholder=\"0.00\" />\n <InputGroup.Text>USD</InputGroup.Text>\n</InputGroup.Root>"
6494
+ ]
6495
+ },
6496
+ "props": {}
6497
+ },
6498
+ "stories": [
6499
+ {
6500
+ "id": "components-inputgroup--default",
6501
+ "name": "Default",
6502
+ "snippet": "const Default = () => (\n <InputGroup.Root>\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <Input type=\"search\" placeholder=\"Search agencies\" aria-label=\"Search agencies\" />\n </InputGroup.Root>\n);",
6503
+ "description": "A search field: the icon leads and the input takes the rest of the frame.\nPressing the icon focuses the input, and the frame — not the input — draws the\nfocus ring, so the two never show a double border.",
6504
+ "summary": "Leading icon addon with the frame drawing the focus ring"
6505
+ },
6506
+ {
6507
+ "id": "components-inputgroup--text-affixes",
6508
+ "name": "Text Affixes",
6509
+ "snippet": "const TextAffixes = () => (\n <InputGroup.Root>\n <InputGroup.Text>$</InputGroup.Text>\n <Input inputMode=\"decimal\" placeholder=\"0.00\" aria-label=\"Premium in US dollars\" />\n <InputGroup.Text>USD</InputGroup.Text>\n </InputGroup.Root>\n);",
6510
+ "description": "Text affixes on both sides. They are decoration only — a screen reader does\nnot read them as part of the value — so the unit is also in the label.",
6511
+ "summary": "Currency prefix and unit suffix around an amount"
6512
+ },
6513
+ {
6514
+ "id": "components-inputgroup--with-divider",
6515
+ "name": "With Divider",
6516
+ "snippet": "const WithDivider = () => (\n <InputGroup.Root>\n <InputGroup.Text>https://</InputGroup.Text>\n <Divider orientation=\"vertical\" />\n <Input\n type=\"text\"\n inputMode=\"url\"\n autoCapitalize=\"none\"\n placeholder=\"www.agentero\"\n aria-label=\"Agency website\"\n />\n <Divider orientation=\"vertical\" />\n <InputGroup.Text>.com</InputGroup.Text>\n </InputGroup.Root>\n);",
6517
+ "description": "A `Divider` separates an affix from the value when it reads as a distinct\nsegment, like a protocol before a domain. It spans the full height of the\nframe, meeting its border at both ends.",
6518
+ "summary": "Protocol prefix and domain suffix split off by vertical dividers"
6519
+ },
6520
+ {
6521
+ "id": "components-inputgroup--with-tags",
6522
+ "name": "With Tags",
6523
+ "snippet": "const WithTags = () => (\n <div className=\"flex flex-col gap-4\">\n <InputGroup.Root>\n <InputGroup.Addon>\n <Tag color=\"informative\">FEIN</Tag>\n </InputGroup.Addon>\n <Input placeholder=\"XX-XXXXXXX\" aria-label=\"FEIN\" />\n </InputGroup.Root>\n <InputGroup.Root>\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <Input placeholder=\"Describe your filters\" aria-label=\"Describe your filters\" />\n <InputGroup.Addon>\n <Tag color=\"informative\">Beta</Tag>\n </InputGroup.Addon>\n </InputGroup.Root>\n </div>\n);",
6524
+ "description": "A `Tag` in an addon labels what the value is. It tucks into the frame's\npadding on the side it touches and takes a tighter radius, so it sits inside\nthe corner instead of competing with it.",
6525
+ "summary": "Leading and trailing tags tucked into the frame's padding"
6526
+ },
6527
+ {
6528
+ "id": "components-inputgroup--with-button",
6529
+ "name": "With Button",
6530
+ "snippet": "const WithButton = () => (\n <InputGroup.Root>\n <Input value=\"https://agentero.com/r/abc123\" readOnly aria-label=\"Referral link\" />\n <Divider orientation=\"vertical\" />\n <InputGroup.Addon variant=\"action\">\n <Button type=\"button\" variant=\"ghost\" size=\"md\" onClick={handleCopy}>\n Copy link\n </Button>\n </InputGroup.Addon>\n </InputGroup.Root>\n);"
6531
+ },
6532
+ {
6533
+ "id": "components-inputgroup--disabled",
6534
+ "name": "Disabled",
6535
+ "snippet": "const Disabled = () => (\n <InputGroup.Root>\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <Input placeholder=\"Search agencies\" aria-label=\"Search agencies\" disabled />\n <InputGroup.Text>USD</InputGroup.Text>\n </InputGroup.Root>\n);",
6536
+ "description": "`disabled` on the input greys out the whole frame — background, border, icon\nand affix — and the frame stops offering the text cursor.",
6537
+ "summary": "Disabled input greying out the frame and its addons"
6538
+ },
6539
+ {
6540
+ "id": "components-inputgroup--invalid",
6541
+ "name": "Invalid",
6542
+ "snippet": "const Invalid = () => (\n <InputGroup.Root>\n <InputGroup.Text>$</InputGroup.Text>\n <Input defaultValue=\"-40\" aria-invalid aria-label=\"Premium in US dollars\" />\n </InputGroup.Root>\n);",
6543
+ "description": "`aria-invalid` on the input moves the destructive border to the frame, and\nthe ring follows it on focus.",
6544
+ "summary": "Invalid input driving the frame's destructive border"
6545
+ },
6546
+ {
6547
+ "id": "components-inputgroup--all-sizes",
6548
+ "name": "All Sizes",
6549
+ "snippet": "const AllSizes = () => (\n <div className=\"flex flex-col gap-4\">\n {SIZES.map(size => (\n <InputGroup.Root key={size}>\n <InputGroup.Addon>\n <IconSearch />\n </InputGroup.Addon>\n <Input size={size} placeholder={`Size ${size}`} aria-label={`Size ${size}`} />\n </InputGroup.Root>\n ))}\n </div>\n);",
6550
+ "description": "The frame follows the input's `size`: the same height as a standalone\n`Input` of that size, and the larger corner radius with `lg`.",
6551
+ "summary": "The three input sizes inside the frame, heights matching Input"
6552
+ },
6553
+ {
6554
+ "id": "components-inputgroup--inside-field-text",
6555
+ "name": "Inside Field Text",
6556
+ "snippet": "const InsideFieldText = () => (\n <FieldText required>\n <Label>Agency website</Label>\n <InputGroup.Root>\n <InputGroup.Text>https://</InputGroup.Text>\n <Input type=\"text\" inputMode=\"url\" autoCapitalize=\"none\" placeholder=\"www.agentero.com\" />\n </InputGroup.Root>\n <Field.Description>Shown on your public profile.</Field.Description>\n </FieldText>\n);",
6557
+ "description": "Inside a `FieldText` nothing changes for the group: the `Input` reads the\nfield's context at any depth, so the label names it and the description\ndescribes it with no ids written by hand. The frame is not in the way.",
6558
+ "summary": "Label and description reaching the input through the frame"
6559
+ },
6560
+ {
6561
+ "id": "components-inputgroup--search-field",
6562
+ "name": "Search Field",
6563
+ "snippet": "const SearchField = () => (\n <form onSubmit={handleSearchSubmit}>\n <FieldStates\n label=\"Agency\"\n tooltip=\"Search by agency name or NPN.\"\n required\n error=\"Pick an agency from the list.\">\n <SearchInput />\n </FieldStates>\n </form>\n);"
6564
+ },
6565
+ {
6566
+ "id": "components-inputgroup--email-field",
6567
+ "name": "Email Field",
6568
+ "snippet": "const EmailField = () => (\n <FieldStates label=\"Email\" required error=\"Enter a valid email address.\">\n <InputGroup.Root>\n <InputGroup.Addon>\n <IconMail />\n </InputGroup.Addon>\n <Input type=\"email\" placeholder=\"me@email.com\" />\n </InputGroup.Root>\n </FieldStates>\n);",
6569
+ "description": "The email field from the Portal UI library: a leading icon is all it adds\nto the input.",
6570
+ "summary": "Email field with a leading icon, valid and invalid"
6571
+ },
6572
+ {
6573
+ "id": "components-inputgroup--price-field",
6574
+ "name": "Price Field",
6575
+ "snippet": "const PriceField = () => (\n <FieldStates label=\"Price\" required error=\"Enter a price above zero.\">\n <PriceInput />\n </FieldStates>\n);",
6576
+ "description": "The price field from the Portal UI library: a currency symbol before the\nvalue and a currency picker after it. The picker is a `DropdownMenu` whose\ntrigger fills an `action` addon, so it keeps the frame's height and an inset\nfocus ring.",
6577
+ "summary": "Price field with a symbol and a currency picker, valid and invalid"
6578
+ },
6579
+ {
6580
+ "id": "components-inputgroup--phone-field",
6581
+ "name": "Phone Field",
6582
+ "snippet": "const PhoneField = () => (\n <FieldStates label=\"Phone number\" error=\"Enter a 10-digit phone number.\">\n <PhoneInput />\n </FieldStates>\n);",
6583
+ "description": "The optional phone field from the Portal UI library: a country code picker\nbefore the number. The picker leads, so the `action` addon rounds its\nbutton's start corners with the frame.",
6584
+ "summary": "Phone field with a leading country code picker, valid and invalid"
6585
+ }
6586
+ ],
6587
+ "import": "import {\n Button,\n Divider,\n DropdownMenu,\n Field,\n FieldText,\n IconCancel,\n IconKeyboardArrowDown,\n IconSearch,\n Input,\n InputGroup,\n Label,\n Tag,\n} from \"@agentero/design-system\";"
6588
+ },
6463
6589
  "components-input": {
6464
6590
  "id": "components-input",
6465
6591
  "name": "Input",
@@ -9426,6 +9552,6 @@
9426
9552
  },
9427
9553
  "meta": {
9428
9554
  "docgen": "react-component-meta",
9429
- "durationMs": 4635
9555
+ "durationMs": 5204
9430
9556
  }
9431
9557
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.31.8",
3
+ "version": "0.32.0",
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",
@@ -93,6 +93,10 @@
93
93
  "types": "./src/input/index.d.ts",
94
94
  "import": "./src/input/index.js"
95
95
  },
96
+ "./input-group": {
97
+ "types": "./src/input-group/index.d.ts",
98
+ "import": "./src/input-group/index.js"
99
+ },
96
100
  "./label": {
97
101
  "types": "./src/label/index.d.ts",
98
102
  "import": "./src/label/index.js"
@@ -0,0 +1,16 @@
1
+ export { inputGroupRecipe } from './input-group';
2
+ export type { InputGroupAddonProps, InputGroupRootProps, InputGroupTextProps } from './input-group';
3
+ export declare const InputGroup: {
4
+ Root: {
5
+ ({ className, ...props }: import('./input-group').InputGroupRootProps): import("react/jsx-runtime").JSX.Element;
6
+ displayName: string;
7
+ };
8
+ Addon: {
9
+ ({ className, variant, onMouseDown, ...props }: import('./input-group').InputGroupAddonProps): import("react/jsx-runtime").JSX.Element;
10
+ displayName: string;
11
+ };
12
+ Text: {
13
+ ({ className, ...props }: import('./input-group').InputGroupTextProps): import("react/jsx-runtime").JSX.Element;
14
+ displayName: string;
15
+ };
16
+ };
@@ -0,0 +1,9 @@
1
+ import { Addon as e, Root as t, Text as n, inputGroupRecipe as r } from "./input-group.js";
2
+ //#region src/input-group/index.ts
3
+ var i = {
4
+ Root: t,
5
+ Addon: e,
6
+ Text: n
7
+ };
8
+ //#endregion
9
+ export { i as InputGroup, r as inputGroupRecipe };
@@ -0,0 +1,130 @@
1
+ import { ComponentPropsWithRef } from 'react';
2
+ import { VariantProps } from 'tailwind-variants';
3
+ /**
4
+ * Style recipe for InputGroup. Slots: `root` (the bordered frame), `addon` and
5
+ * `text`. Focus, disabled, invalid and size all come from the `<input>` or
6
+ * `<textarea>` inside through `:has()`, so the frame can never disagree with
7
+ * its control.
8
+ *
9
+ * @summary tailwind-variants recipe backing the InputGroup frame and addons
10
+ */
11
+ export declare const inputGroupRecipe: import('tailwind-variants').TVReturnType<{
12
+ variant: {
13
+ default: {};
14
+ action: {
15
+ addon: string[];
16
+ };
17
+ };
18
+ }, {
19
+ root: string[];
20
+ addon: string[];
21
+ text: string[];
22
+ }, undefined, {
23
+ variant: {
24
+ default: {};
25
+ action: {
26
+ addon: string[];
27
+ };
28
+ };
29
+ }, {
30
+ root: string[];
31
+ addon: string[];
32
+ text: string[];
33
+ }, import('tailwind-variants').TVReturnType<{
34
+ variant: {
35
+ default: {};
36
+ action: {
37
+ addon: string[];
38
+ };
39
+ };
40
+ }, {
41
+ root: string[];
42
+ addon: string[];
43
+ text: string[];
44
+ }, undefined, unknown, unknown, undefined>>;
45
+ type InputGroupVariants = VariantProps<typeof inputGroupRecipe>;
46
+ export type InputGroupRootProps = ComponentPropsWithRef<'div'>;
47
+ /**
48
+ * The bordered frame of an input group: one box around an `Input` and its
49
+ * addons, drawn as a single control. It carries the border, the shadow, the
50
+ * focus ring and the horizontal padding; the `Input` inside renders bare and
51
+ * gives the frame its state — focus, `disabled`, `aria-invalid` and `size`
52
+ * all propagate through `:has()`, so nothing is passed to the frame by hand.
53
+ *
54
+ * Put the `Input` and any `InputGroup.Addon` or `InputGroup.Text` inside, in
55
+ * reading order. Inside a `FieldText` the `Input` still takes its label and
56
+ * messages from the field, at any depth; the frame needs nothing.
57
+ *
58
+ * Use it for an input with a leading icon, a currency or unit affix, or a
59
+ * trailing action. A plain input with nothing around it is just `Input`, and a
60
+ * button beside an input that submits on its own is a form layout, not a group.
61
+ * Not a `role="group"`, like `Field`.
62
+ *
63
+ * @summary Bordered frame drawing an Input and its addons as one control
64
+ * @dataAttribute {string} data-slot - Always set to "input-group"
65
+ *
66
+ * @example
67
+ * <InputGroup.Root>
68
+ * <InputGroup.Text>$</InputGroup.Text>
69
+ * <Input inputMode="decimal" placeholder="0.00" />
70
+ * <InputGroup.Text>USD</InputGroup.Text>
71
+ * </InputGroup.Root>
72
+ */
73
+ export declare const Root: {
74
+ ({ className, ...props }: InputGroupRootProps): import("react/jsx-runtime").JSX.Element;
75
+ displayName: string;
76
+ };
77
+ export type InputGroupAddonProps = ComponentPropsWithRef<'div'> & {
78
+ /**
79
+ * `action` runs a button edge to edge at the frame's height.
80
+ *
81
+ * @default 'default'
82
+ */
83
+ variant?: InputGroupVariants['variant'];
84
+ };
85
+ /**
86
+ * A slot for an icon, a tag or a button inside the frame. Icons take the input
87
+ * colour and size (24px) and follow the disabled state. A press on the addon
88
+ * itself focuses the input, so the whole frame reads as the control; a press on
89
+ * a button or link inside it is left alone.
90
+ *
91
+ * For a text affix use `InputGroup.Text`, which sets the type and colour a
92
+ * prefix needs.
93
+ *
94
+ * @summary Icon, tag or button slot inside the input group frame
95
+ * @dataAttribute {string} data-slot - Always set to "input-group-addon"
96
+ *
97
+ * @example
98
+ * <InputGroup.Root>
99
+ * <InputGroup.Addon><IconSearch /></InputGroup.Addon>
100
+ * <Input type="search" placeholder="Search agencies" />
101
+ * </InputGroup.Root>
102
+ */
103
+ export declare const Addon: {
104
+ ({ className, variant, onMouseDown, ...props }: InputGroupAddonProps): import("react/jsx-runtime").JSX.Element;
105
+ displayName: string;
106
+ };
107
+ export type InputGroupTextProps = ComponentPropsWithRef<'span'>;
108
+ /**
109
+ * A text affix inside the frame: a currency symbol, a protocol, a unit. Set in
110
+ * the placeholder colour so the typed value stands out, at the input's text
111
+ * size (`base` when the input is `lg`), and greyed out with a disabled input.
112
+ * It is decoration only: a screen reader does not read it as part of the value,
113
+ * so say the unit in the label or the description when it matters — and the
114
+ * value excludes it too, so a `https://` prefix goes with a `type="text"`
115
+ * input, not `type="url"`, whose native validation would reject the bare host.
116
+ *
117
+ * @summary Currency, protocol or unit affix set beside the input
118
+ * @dataAttribute {string} data-slot - Always set to "input-group-text"
119
+ *
120
+ * @example
121
+ * <InputGroup.Root>
122
+ * <InputGroup.Text>https://</InputGroup.Text>
123
+ * <Input type="text" inputMode="url" autoCapitalize="none" placeholder="www.agentero.com" />
124
+ * </InputGroup.Root>
125
+ */
126
+ export declare const Text: {
127
+ ({ className, ...props }: InputGroupTextProps): import("react/jsx-runtime").JSX.Element;
128
+ displayName: string;
129
+ };
130
+ export {};
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { cn as e } from "../../lib/utils.js";
3
+ import { tv as t } from "tailwind-variants";
4
+ import { jsx as n } from "react/jsx-runtime";
5
+ //#region src/input-group/input-group.tsx
6
+ var r = t({
7
+ slots: {
8
+ root: [
9
+ "group/input-group [--input-group-gap:0.75rem]",
10
+ "flex gap-(--input-group-gap) rounded-md border border-solid border-border-input-default",
11
+ "bg-bg-input-normal px-3 text-text-input-normal shadow-sm",
12
+ "cursor-text overflow-hidden outline-0 -outline-offset-1 outline-[transparent]",
13
+ "transition-[color,box-shadow,outline-color,outline-offset,outline-width] duration-75",
14
+ "has-[input:focus-within,textarea:focus-within]:border-border-input-focus",
15
+ "has-[input:focus-within,textarea:focus-within]:outline-border-input-focus",
16
+ "has-[input:focus-within,textarea:focus-within]:outline-[0.125rem]",
17
+ "has-[input:focus-within,textarea:focus-within]:outline-offset-[-0.0625rem]",
18
+ "has-[input:disabled,textarea:disabled]:cursor-default",
19
+ "has-[input:disabled,textarea:disabled]:border-border-input-disable",
20
+ "has-[input:disabled,textarea:disabled]:bg-bg-input-disable",
21
+ "has-[input:disabled,textarea:disabled]:text-text-input-disable",
22
+ "has-[input:disabled,textarea:disabled]:shadow-none",
23
+ "has-[input[data-size=\"lg\"],textarea[data-size=\"lg\"]]:rounded-lg",
24
+ "has-[input[aria-invalid=\"true\"],textarea[aria-invalid=\"true\"]]:border-border-input-destructive",
25
+ "has-[input[aria-invalid=\"true\"]:focus-within,textarea[aria-invalid=\"true\"]:focus-within]:outline-outline-input-destructive",
26
+ "**:[input,textarea]:-my-0.25 **:[input,textarea]:border-none **:[input,textarea]:bg-transparent",
27
+ "**:[input,textarea]:px-0 **:[input,textarea]:pb-0.125 **:[input,textarea]:shadow-none",
28
+ "**:[input,textarea]:outline-none **:[input,textarea]:focus-visible:outline-none",
29
+ "**:[[data-slot=separator]]:bg-border-input-default",
30
+ "**:[[data-slot=tag]]:-ms-1 **:[[data-slot=tag]]:rounded-sm",
31
+ "**:[input+[data-slot=input-group-addon]>[data-slot=tag]]:ms-0",
32
+ "**:[input+[data-slot=input-group-addon]>[data-slot=tag]]:-me-1"
33
+ ],
34
+ addon: [
35
+ "flex h-auto cursor-text items-center justify-center",
36
+ "*:[svg]:-mx-1 *:[svg]:size-6 [&_path]:fill-icon-input-default",
37
+ "group-has-[input:disabled,textarea:disabled]/input-group:[&_path]:fill-icon-input-disable"
38
+ ],
39
+ text: [
40
+ "flex items-center text-sm text-text-input-placeholder",
41
+ "group-has-[input[data-size=\"lg\"]]/input-group:text-base",
42
+ "group-has-[input:disabled,textarea:disabled]/input-group:text-text-input-disable"
43
+ ]
44
+ },
45
+ variants: { variant: {
46
+ default: {},
47
+ action: { addon: [
48
+ "-mx-(--input-group-gap) shrink-0 items-stretch rounded-[inherit]",
49
+ "*:[button]:h-auto *:[button]:rounded-none *:[button]:focus-visible:-outline-offset-2",
50
+ "first:*:[button]:rounded-s-[inherit] last:*:[button]:rounded-e-[inherit]"
51
+ ] }
52
+ } },
53
+ defaultVariants: { variant: "default" }
54
+ }), i = r(), a = ({ className: t, ...r }) => /* @__PURE__ */ n("div", {
55
+ "data-slot": "input-group",
56
+ className: e(i.root(), t),
57
+ ...r
58
+ });
59
+ a.displayName = "InputGroup.Root";
60
+ var o = ({ className: t, variant: i, onMouseDown: a, ...o }) => /* @__PURE__ */ n("div", {
61
+ "data-slot": "input-group-addon",
62
+ className: e(r({ variant: i }).addon(), t),
63
+ onMouseDown: (e) => {
64
+ if (a?.(e), e.defaultPrevented || e.target.closest("button, a")) return;
65
+ e.preventDefault();
66
+ let t = e.currentTarget.parentElement, n = t?.querySelector("input, textarea");
67
+ n && !t?.querySelector("input:focus, textarea:focus") && n.focus();
68
+ },
69
+ ...o
70
+ });
71
+ o.displayName = "InputGroup.Addon";
72
+ var s = ({ className: t, ...r }) => /* @__PURE__ */ n("span", {
73
+ "data-slot": "input-group-text",
74
+ className: e(i.text(), t),
75
+ ...r
76
+ });
77
+ s.displayName = "InputGroup.Text";
78
+ //#endregion
79
+ export { o as Addon, a as Root, s as Text, r as inputGroupRecipe };