@agentero/design-system 0.34.2 → 0.35.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">36 components ok</span><span class="filter-pill ok" aria-disabled="true">294 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">37 components ok</span><span class="filter-pill ok" aria-disabled="true">305 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>3.1s</strong>.
597
+ Using <code>react-component-meta</code>. Generation took <strong>5.0s</strong>.
598
598
  </div>
599
599
  <h2 class="section-title">Components</h2>
600
600
  <div class="grid" role="list">
@@ -7901,6 +7901,270 @@ several search fields does not read &quot;Clear search&quot; twice.</div>
7901
7901
  </div>
7902
7902
 
7903
7903
 
7904
+ </div>
7905
+ </article>
7906
+ <article
7907
+ class="card
7908
+ no-error
7909
+ no-info
7910
+ no-story-error
7911
+ no-doc-error"
7912
+ role="listitem"
7913
+ aria-label="InputTel">
7914
+ <div class="head">
7915
+ <div class="title">
7916
+ <h2><span class="status-dot dot-ok"></span> InputTel</h2>
7917
+ <div class="badges">
7918
+ <label for="c-21-components-inputtel-props" class="badge ok as-toggle">1 prop type</label>
7919
+
7920
+ <label for="c-21-components-inputtel-stories" class="badge ok as-toggle">11 stories</label>
7921
+
7922
+
7923
+ </div>
7924
+ </div>
7925
+ <div class="meta" title="./src/input-tel/input-tel.stories.tsx">components-inputtel · ./src/input-tel/input-tel.stories.tsx</div>
7926
+ <div>US phone input with a +1 prefix that masks as you type</div>
7927
+ <div class="hint">InputTel is a US phone number field that masks as you type: the digits are
7928
+ laid out as `(555) 123-4567`, anything else is dropped and the caret stays
7929
+ next to the digit just edited. It is an `Input` of `type=&quot;tel&quot;` inside an
7930
+ `InputGroup` frame with a `+1` prefix; `onChange` receives the native event
7931
+ with the masked text already in `event.target.value`.</div>
7932
+ <div class="kv"><span class="chip">summary: US phone input with a +1 prefix that masks as you type</span><span class="chip">dataAttribute: {string} data-slot - Always set to &quot;input-tel&quot; on the frame</span><span class="chip">example: &lt;FieldText&gt;
7933
+ &lt;Label&gt;Phone number&lt;/Label&gt;
7934
+ &lt;InputTel name=&quot;phone&quot; autoComplete=&quot;tel-national&quot; placeholder=&quot;(555) 000-0000&quot; /&gt;
7935
+ &lt;/FieldText&gt;</span></div>
7936
+ </div>
7937
+
7938
+ <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7939
+
7940
+
7941
+ <input id="c-21-components-inputtel-stories" class="tg tg-stories" type="checkbox" hidden />
7942
+
7943
+
7944
+ <input id="c-21-components-inputtel-props" class="tg tg-props" type="checkbox" hidden />
7945
+
7946
+ <div class="panels">
7947
+
7948
+
7949
+
7950
+ <div class="panel panel-props">
7951
+ <div class="note ok">
7952
+ <div class="row">
7953
+ <span class="ex-name">Prop types <small>(react-component-meta)</small></span>
7954
+ <span class="badge ok">1 prop type</span>
7955
+ </div>
7956
+ <pre><code>Component: src/input-tel/input-tel.tsx::InputTel</code></pre>
7957
+ <pre><code>Props:</code></pre>
7958
+ <pre><code>/**
7959
+ * Control height. Defaults to `&#39;md&#39;`.
7960
+ * - `sm` (32px) — dense layouts where vertical space is tight.
7961
+ * - `md` (40px) — standard form density.
7962
+ * - `lg` (48px) — low-density forms and larger touch targets. Also raises
7963
+ * the text to `base` and the corner radius to `lg`.
7964
+ */
7965
+ size?: &quot;sm&quot; | &quot;md&quot; | &quot;lg&quot;</code></pre>
7966
+ </div>
7967
+ </div>
7968
+
7969
+ <div class="panel panel-stories">
7970
+
7971
+
7972
+
7973
+ <div class="note ok">
7974
+ <div class="row">
7975
+ <span class="ex-name">Imports</span>
7976
+ </div>
7977
+ <pre><code>import { Button, Field, FieldText, InputContext, InputTel, Label } from "@agentero/design-system";</code></pre>
7978
+ </div>
7979
+
7980
+
7981
+ <div class="note ok">
7982
+ <div class="row">
7983
+ <span class="ex-name">Default</span>
7984
+ <span class="badge ok">story ok</span>
7985
+ </div>
7986
+ <div>Phone field with a +1 prefix masking the digits as they are typed</div>
7987
+ <div class="hint">An uncontrolled phone field with its `+1` prefix. Typing the ten digits lays
7988
+ them out as `(555) 123-4567`, and every `onChange` call carries the masked
7989
+ text in `event.target.value`; the prefix never enters the value.</div>
7990
+ <pre><code>const Default = () =&gt; &lt;InputTel
7991
+ aria-label=&quot;Phone number&quot;
7992
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)} /&gt;;</code></pre>
7993
+ </div>
7994
+ <div class="note ok">
7995
+ <div class="row">
7996
+ <span class="ex-name">Controlled</span>
7997
+ <span class="badge ok">story ok</span>
7998
+ </div>
7999
+ <div>Controlled phone field storing the masked value</div>
8000
+ <div class="hint">A controlled phone field. The state that owns the value stores what
8001
+ `event.target.value` reports, which is already masked, and the input shows
8002
+ it back unchanged. A partial number carries the separator that comes before
8003
+ the next digit, so six digits read `(555) 123-`.</div>
8004
+ <pre><code>const Controlled = () =&gt; {
8005
+ const [phone, setPhone] = useState(&#39;&#39;);
8006
+
8007
+ return (
8008
+ &lt;InputTel
8009
+ aria-label=&quot;Phone number&quot;
8010
+ value={phone}
8011
+ onChange={event =&gt; {
8012
+ setPhone(event.target.value);
8013
+ args.onChange?.(event);
8014
+ }} /&gt;
8015
+ );
8016
+ };</code></pre>
8017
+ </div>
8018
+ <div class="note ok">
8019
+ <div class="row">
8020
+ <span class="ex-name">Caret In The Middle</span>
8021
+ <span class="badge ok">story ok</span>
8022
+ </div>
8023
+ <div>Caret staying put when a digit is inserted mid-number</div>
8024
+ <div class="hint">Editing in the middle of a full number: a digit typed after `(5` shifts the
8025
+ rest right, the eleventh digit falls off the end and the caret stays after
8026
+ the digit just typed instead of jumping to the end.</div>
8027
+ <pre><code>const CaretInTheMiddle = () =&gt; &lt;InputTel
8028
+ aria-label=&quot;Phone number&quot;
8029
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)} /&gt;;</code></pre>
8030
+ </div>
8031
+ <div class="note ok">
8032
+ <div class="row">
8033
+ <span class="ex-name">Backspace Over Mask Characters</span>
8034
+ <span class="badge ok">story ok</span>
8035
+ </div>
8036
+ <div>Backspace trimming the separators the mask added</div>
8037
+ <div class="hint">Backspace over the mask&#39;s own characters: after `555` the field shows
8038
+ `(555) `, and deleting backwards trims the separators along with the digit
8039
+ under the caret instead of leaving a dangling `) `.</div>
8040
+ <pre><code>const BackspaceOverMaskCharacters = () =&gt; &lt;InputTel
8041
+ aria-label=&quot;Phone number&quot;
8042
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)} /&gt;;</code></pre>
8043
+ </div>
8044
+ <div class="note ok">
8045
+ <div class="row">
8046
+ <span class="ex-name">Paste</span>
8047
+ <span class="badge ok">story ok</span>
8048
+ </div>
8049
+ <div>Pasted number reduced to its digits and masked</div>
8050
+ <div class="hint">A pasted number in any format is reduced to its digits and masked, so
8051
+ `555-123-4567` from a clipboard ends up as `(555) 123-4567`.</div>
8052
+ <pre><code>const Paste = () =&gt; &lt;InputTel
8053
+ aria-label=&quot;Phone number&quot;
8054
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)} /&gt;;</code></pre>
8055
+ </div>
8056
+ <div class="note ok">
8057
+ <div class="row">
8058
+ <span class="ex-name">Paste With Country Code</span>
8059
+ <span class="badge ok">story ok</span>
8060
+ </div>
8061
+ <div>Pasted number with a +1 country code</div>
8062
+ <div class="hint">A number that carries the `+1` country code, as copied from a signature or
8063
+ filled in by the browser&#39;s autofill, loses the code and keeps its ten
8064
+ national digits: `+1 (555) 123-4567` ends up as `(555) 123-4567`.</div>
8065
+ <pre><code>const PasteWithCountryCode = () =&gt; &lt;InputTel
8066
+ aria-label=&quot;Phone number&quot;
8067
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)} /&gt;;</code></pre>
8068
+ </div>
8069
+ <div class="note ok">
8070
+ <div class="row">
8071
+ <span class="ex-name">Inside Field Text</span>
8072
+ <span class="badge ok">story ok</span>
8073
+ </div>
8074
+ <div>Label and description reaching the input through the field</div>
8075
+ <div class="hint">Inside a `FieldText` the input takes its label, description and states from
8076
+ the field, and the masked value still reaches a handler the field provides.</div>
8077
+ <pre><code>const InsideFieldText = () =&gt; (
8078
+ &lt;FieldText&gt;
8079
+ &lt;Label&gt;Phone number&lt;/Label&gt;
8080
+ &lt;InputTel /&gt;
8081
+ &lt;Field.Description&gt;We only call about your account.&lt;/Field.Description&gt;
8082
+ &lt;/FieldText&gt;
8083
+ );</code></pre>
8084
+ </div>
8085
+ <div class="note ok">
8086
+ <div class="row">
8087
+ <span class="ex-name">With React Hook Form</span>
8088
+ <span class="badge ok">story ok</span>
8089
+ </div>
8090
+ <div>Masked value reaching react-hook-form through register</div>
8091
+ <div class="hint">Registered with react-hook-form, the form receives the masked value: the
8092
+ `register` handler reads `event.target.value` like any other change.</div>
8093
+ <pre><code>const WithReactHookForm = () =&gt; {
8094
+ const { register, handleSubmit } = useForm&lt;ContactValues&gt;({
8095
+ defaultValues: { phone: &#39;&#39; }
8096
+ });
8097
+
8098
+ return (
8099
+ &lt;form
8100
+ aria-label=&quot;Contact&quot;
8101
+ noValidate
8102
+ onSubmit={handleSubmit(values =&gt; onSubmit(values))}
8103
+ style={{ display: &#39;grid&#39;, gap: &#39;1rem&#39; }}&gt;
8104
+ &lt;InputTel aria-label=&quot;Phone number&quot; {...register(&#39;phone&#39;)} /&gt;
8105
+ &lt;Button type=&quot;submit&quot;&gt;Save&lt;/Button&gt;
8106
+ &lt;/form&gt;
8107
+ );
8108
+ };</code></pre>
8109
+ </div>
8110
+ <div class="note ok">
8111
+ <div class="row">
8112
+ <span class="ex-name">Disabled</span>
8113
+ <span class="badge ok">story ok</span>
8114
+ </div>
8115
+ <div>Disabled phone field</div>
8116
+ <div class="hint">A disabled phone field keeps its masked value and takes no input.</div>
8117
+ <pre><code>const Disabled = () =&gt; &lt;InputTel
8118
+ aria-label=&quot;Phone number&quot;
8119
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8120
+ disabled
8121
+ defaultValue=&quot;(555) 123-4567&quot; /&gt;;</code></pre>
8122
+ </div>
8123
+ <div class="note ok">
8124
+ <div class="row">
8125
+ <span class="ex-name">Invalid</span>
8126
+ <span class="badge ok">story ok</span>
8127
+ </div>
8128
+ <div>Invalid field driving the frame&#39;s destructive border</div>
8129
+ <div class="hint">`invalid` on the surrounding `FieldText` sets `aria-invalid` on the input,
8130
+ which paints the frame&#39;s destructive border, and `Field.Error` is announced
8131
+ with the input.</div>
8132
+ <pre><code>const Invalid = () =&gt; (
8133
+ &lt;FieldText invalid&gt;
8134
+ &lt;Label&gt;Phone number&lt;/Label&gt;
8135
+ &lt;InputTel defaultValue=&quot;(555) 12&quot; /&gt;
8136
+ &lt;Field.Error errors={[{ message: &#39;Enter a 10-digit phone number.&#39; }]} /&gt;
8137
+ &lt;/FieldText&gt;
8138
+ );</code></pre>
8139
+ </div>
8140
+ <div class="note ok">
8141
+ <div class="row">
8142
+ <span class="ex-name">All Sizes</span>
8143
+ <span class="badge ok">story ok</span>
8144
+ </div>
8145
+ <div>The three sizes side by side</div>
8146
+ <div class="hint">The three sizes match Input&#39;s: the frame is 32, 40 or 48px tall.</div>
8147
+ <pre><code>const AllSizes = () =&gt; &lt;div style={{ display: &#39;grid&#39;, gap: &#39;1rem&#39; }}&gt;
8148
+ &lt;InputTel
8149
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8150
+ size=&quot;sm&quot;
8151
+ aria-label=&quot;Small&quot;
8152
+ defaultValue=&quot;(555) 123-4567&quot; /&gt;
8153
+ &lt;InputTel
8154
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8155
+ size=&quot;md&quot;
8156
+ aria-label=&quot;Medium&quot;
8157
+ defaultValue=&quot;(555) 123-4567&quot; /&gt;
8158
+ &lt;InputTel
8159
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8160
+ size=&quot;lg&quot;
8161
+ aria-label=&quot;Large&quot;
8162
+ defaultValue=&quot;(555) 123-4567&quot; /&gt;
8163
+ &lt;/div&gt;;</code></pre>
8164
+ </div>
8165
+ </div>
8166
+
8167
+
7904
8168
  </div>
7905
8169
  </article>
7906
8170
  <article
@@ -7915,9 +8179,9 @@ several search fields does not read &quot;Clear search&quot; twice.</div>
7915
8179
  <div class="title">
7916
8180
  <h2><span class="status-dot dot-ok"></span> Label</h2>
7917
8181
  <div class="badges">
7918
- <label for="c-21-components-label-props" class="badge ok as-toggle">3 prop types</label>
8182
+ <label for="c-22-components-label-props" class="badge ok as-toggle">3 prop types</label>
7919
8183
 
7920
- <label for="c-21-components-label-stories" class="badge ok as-toggle">5 stories</label>
8184
+ <label for="c-22-components-label-stories" class="badge ok as-toggle">5 stories</label>
7921
8185
 
7922
8186
 
7923
8187
  </div>
@@ -7937,10 +8201,10 @@ still has to carry `required` / `aria-required`.</div>
7937
8201
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7938
8202
 
7939
8203
 
7940
- <input id="c-21-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
8204
+ <input id="c-22-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
7941
8205
 
7942
8206
 
7943
- <input id="c-21-components-label-props" class="tg tg-props" type="checkbox" hidden />
8207
+ <input id="c-22-components-label-props" class="tg tg-props" type="checkbox" hidden />
7944
8208
 
7945
8209
  <div class="panels">
7946
8210
 
@@ -8064,9 +8328,9 @@ field&#39;s control with no `htmlFor`. An explicit `htmlFor` still wins.</div>
8064
8328
  <div class="title">
8065
8329
  <h2><span class="status-dot dot-ok"></span> Loading</h2>
8066
8330
  <div class="badges">
8067
- <label for="c-22-components-loading-props" class="badge ok as-toggle">1 prop type</label>
8331
+ <label for="c-23-components-loading-props" class="badge ok as-toggle">1 prop type</label>
8068
8332
 
8069
- <label for="c-22-components-loading-stories" class="badge ok as-toggle">7 stories</label>
8333
+ <label for="c-23-components-loading-stories" class="badge ok as-toggle">7 stories</label>
8070
8334
 
8071
8335
 
8072
8336
  </div>
@@ -8083,10 +8347,10 @@ parent font size via `em`-based size variants (`sm`, `md`, `lg`).</div>
8083
8347
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8084
8348
 
8085
8349
 
8086
- <input id="c-22-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
8350
+ <input id="c-23-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
8087
8351
 
8088
8352
 
8089
- <input id="c-22-components-loading-props" class="tg tg-props" type="checkbox" hidden />
8353
+ <input id="c-23-components-loading-props" class="tg tg-props" type="checkbox" hidden />
8090
8354
 
8091
8355
  <div class="panels">
8092
8356
 
@@ -8237,9 +8501,9 @@ against the dark background.</div>
8237
8501
  <div class="title">
8238
8502
  <h2><span class="status-dot dot-ok"></span> Modal.Root</h2>
8239
8503
  <div class="badges">
8240
- <label for="c-23-components-modal-props" class="badge ok as-toggle">5 prop types</label>
8504
+ <label for="c-24-components-modal-props" class="badge ok as-toggle">5 prop types</label>
8241
8505
 
8242
- <label for="c-23-components-modal-stories" class="badge ok as-toggle">6 stories</label>
8506
+ <label for="c-24-components-modal-stories" class="badge ok as-toggle">6 stories</label>
8243
8507
 
8244
8508
 
8245
8509
  </div>
@@ -8285,10 +8549,10 @@ import { Modal } from &#39;@agentero/design-system/modal&#39;;
8285
8549
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8286
8550
 
8287
8551
 
8288
- <input id="c-23-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
8552
+ <input id="c-24-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
8289
8553
 
8290
8554
 
8291
- <input id="c-23-components-modal-props" class="tg tg-props" type="checkbox" hidden />
8555
+ <input id="c-24-components-modal-props" class="tg tg-props" type="checkbox" hidden />
8292
8556
 
8293
8557
  <div class="panels">
8294
8558
 
@@ -8508,9 +8772,9 @@ announced as an `alertdialog`. Only the footer actions close it.</div>
8508
8772
  <div class="title">
8509
8773
  <h2><span class="status-dot dot-ok"></span> Pagination</h2>
8510
8774
  <div class="badges">
8511
- <label for="c-24-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
8775
+ <label for="c-25-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
8512
8776
 
8513
- <label for="c-24-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
8777
+ <label for="c-25-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
8514
8778
 
8515
8779
 
8516
8780
  </div>
@@ -8540,10 +8804,10 @@ const [page, setPage] = useState(1);
8540
8804
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8541
8805
 
8542
8806
 
8543
- <input id="c-24-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
8807
+ <input id="c-25-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
8544
8808
 
8545
8809
 
8546
- <input id="c-24-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
8810
+ <input id="c-25-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
8547
8811
 
8548
8812
  <div class="panels">
8549
8813
 
@@ -8698,9 +8962,9 @@ entries summary follow along.</div>
8698
8962
  <div class="title">
8699
8963
  <h2><span class="status-dot dot-ok"></span> Popover.Root</h2>
8700
8964
  <div class="badges">
8701
- <label for="c-25-components-popover-props" class="badge ok as-toggle">5 prop types</label>
8965
+ <label for="c-26-components-popover-props" class="badge ok as-toggle">5 prop types</label>
8702
8966
 
8703
- <label for="c-25-components-popover-stories" class="badge ok as-toggle">5 stories</label>
8967
+ <label for="c-26-components-popover-stories" class="badge ok as-toggle">5 stories</label>
8704
8968
 
8705
8969
 
8706
8970
  </div>
@@ -8729,10 +8993,10 @@ import { Popover } from &#39;@agentero/design-system/popover&#39;;
8729
8993
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8730
8994
 
8731
8995
 
8732
- <input id="c-25-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
8996
+ <input id="c-26-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
8733
8997
 
8734
8998
 
8735
- <input id="c-25-components-popover-props" class="tg tg-props" type="checkbox" hidden />
8999
+ <input id="c-26-components-popover-props" class="tg tg-props" type="checkbox" hidden />
8736
9000
 
8737
9001
  <div class="panels">
8738
9002
 
@@ -8931,9 +9195,9 @@ focus returning to its trigger once the modal closes.</div>
8931
9195
  <div class="title">
8932
9196
  <h2><span class="status-dot dot-ok"></span> Progress</h2>
8933
9197
  <div class="badges">
8934
- <label for="c-26-components-progress-props" class="badge ok as-toggle">4 prop types</label>
9198
+ <label for="c-27-components-progress-props" class="badge ok as-toggle">4 prop types</label>
8935
9199
 
8936
- <label for="c-26-components-progress-stories" class="badge ok as-toggle">4 stories</label>
9200
+ <label for="c-27-components-progress-stories" class="badge ok as-toggle">4 stories</label>
8937
9201
 
8938
9202
 
8939
9203
  </div>
@@ -8952,10 +9216,10 @@ with `currentColor`, so recolor it with a plain text class — e.g.
8952
9216
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8953
9217
 
8954
9218
 
8955
- <input id="c-26-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
9219
+ <input id="c-27-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
8956
9220
 
8957
9221
 
8958
- <input id="c-26-components-progress-props" class="tg tg-props" type="checkbox" hidden />
9222
+ <input id="c-27-components-progress-props" class="tg tg-props" type="checkbox" hidden />
8959
9223
 
8960
9224
  <div class="panels">
8961
9225
 
@@ -9074,7 +9338,7 @@ percentage: number</code></pre>
9074
9338
  <div class="badges">
9075
9339
 
9076
9340
 
9077
- <label for="c-27-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
9341
+ <label for="c-28-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
9078
9342
 
9079
9343
 
9080
9344
  </div>
@@ -9091,7 +9355,7 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
9091
9355
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9092
9356
 
9093
9357
 
9094
- <input id="c-27-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
9358
+ <input id="c-28-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
9095
9359
 
9096
9360
 
9097
9361
 
@@ -9187,9 +9451,9 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
9187
9451
  <div class="title">
9188
9452
  <h2><span class="status-dot dot-ok"></span> Switch</h2>
9189
9453
  <div class="badges">
9190
- <label for="c-28-components-switch-props" class="badge ok as-toggle">8 prop types</label>
9454
+ <label for="c-29-components-switch-props" class="badge ok as-toggle">8 prop types</label>
9191
9455
 
9192
- <label for="c-28-components-switch-stories" class="badge ok as-toggle">7 stories</label>
9456
+ <label for="c-29-components-switch-stories" class="badge ok as-toggle">7 stories</label>
9193
9457
 
9194
9458
 
9195
9459
  </div>
@@ -9205,10 +9469,10 @@ a Checkbox for a single boolean that applies instantly, and pair it with a
9205
9469
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9206
9470
 
9207
9471
 
9208
- <input id="c-28-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
9472
+ <input id="c-29-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
9209
9473
 
9210
9474
 
9211
- <input id="c-28-components-switch-props" class="tg tg-props" type="checkbox" hidden />
9475
+ <input id="c-29-components-switch-props" class="tg tg-props" type="checkbox" hidden />
9212
9476
 
9213
9477
  <div class="panels">
9214
9478
 
@@ -9389,9 +9653,9 @@ size?: &quot;sm&quot; | &quot;md&quot;</code></pre>
9389
9653
  <div class="title">
9390
9654
  <h2><span class="status-dot dot-ok"></span> Table.Root</h2>
9391
9655
  <div class="badges">
9392
- <label for="c-29-components-table-props" class="badge ok as-toggle">6 prop types</label>
9656
+ <label for="c-30-components-table-props" class="badge ok as-toggle">6 prop types</label>
9393
9657
 
9394
- <label for="c-29-components-table-stories" class="badge ok as-toggle">10 stories</label>
9658
+ <label for="c-30-components-table-stories" class="badge ok as-toggle">10 stories</label>
9395
9659
 
9396
9660
 
9397
9661
  </div>
@@ -9422,10 +9686,10 @@ import { Table } from &#39;@agentero/design-system/table&#39;;
9422
9686
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9423
9687
 
9424
9688
 
9425
- <input id="c-29-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
9689
+ <input id="c-30-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
9426
9690
 
9427
9691
 
9428
- <input id="c-29-components-table-props" class="tg tg-props" type="checkbox" hidden />
9692
+ <input id="c-30-components-table-props" class="tg tg-props" type="checkbox" hidden />
9429
9693
 
9430
9694
  <div class="panels">
9431
9695
 
@@ -9643,9 +9907,9 @@ keeps its padding.</div>
9643
9907
  <div class="title">
9644
9908
  <h2><span class="status-dot dot-ok"></span> Tabs.Root</h2>
9645
9909
  <div class="badges">
9646
- <label for="c-30-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
9910
+ <label for="c-31-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
9647
9911
 
9648
- <label for="c-30-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
9912
+ <label for="c-31-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
9649
9913
 
9650
9914
 
9651
9915
  </div>
@@ -9676,10 +9940,10 @@ import { Tabs } from &#39;@agentero/design-system/tabs&#39;;
9676
9940
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9677
9941
 
9678
9942
 
9679
- <input id="c-30-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
9943
+ <input id="c-31-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
9680
9944
 
9681
9945
 
9682
- <input id="c-30-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
9946
+ <input id="c-31-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
9683
9947
 
9684
9948
  <div class="panels">
9685
9949
 
@@ -9900,9 +10164,9 @@ tablist role is announced.</div>
9900
10164
  <div class="title">
9901
10165
  <h2><span class="status-dot dot-ok"></span> Tag</h2>
9902
10166
  <div class="badges">
9903
- <label for="c-31-components-tag-props" class="badge ok as-toggle">8 prop types</label>
10167
+ <label for="c-32-components-tag-props" class="badge ok as-toggle">8 prop types</label>
9904
10168
 
9905
- <label for="c-31-components-tag-stories" class="badge ok as-toggle">12 stories</label>
10169
+ <label for="c-32-components-tag-stories" class="badge ok as-toggle">12 stories</label>
9906
10170
 
9907
10171
 
9908
10172
  </div>
@@ -9921,10 +10185,10 @@ it an interactive link or button, which adds the pointer cursor and hover fill.<
9921
10185
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9922
10186
 
9923
10187
 
9924
- <input id="c-31-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
10188
+ <input id="c-32-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
9925
10189
 
9926
10190
 
9927
- <input id="c-31-components-tag-props" class="tg tg-props" type="checkbox" hidden />
10191
+ <input id="c-32-components-tag-props" class="tg tg-props" type="checkbox" hidden />
9928
10192
 
9929
10193
  <div class="panels">
9930
10194
 
@@ -10349,9 +10613,9 @@ leading + trailing icons, and icon-only.</div>
10349
10613
  <div class="title">
10350
10614
  <h2><span class="status-dot dot-ok"></span> TextArea</h2>
10351
10615
  <div class="badges">
10352
- <label for="c-32-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
10616
+ <label for="c-33-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
10353
10617
 
10354
- <label for="c-32-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
10618
+ <label for="c-33-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
10355
10619
 
10356
10620
 
10357
10621
  </div>
@@ -10368,10 +10632,10 @@ states. It wires nothing on its own: `id`, `aria-invalid` and
10368
10632
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10369
10633
 
10370
10634
 
10371
- <input id="c-32-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
10635
+ <input id="c-33-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
10372
10636
 
10373
10637
 
10374
- <input id="c-32-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
10638
+ <input id="c-33-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
10375
10639
 
10376
10640
  <div class="panels">
10377
10641
 
@@ -10503,9 +10767,9 @@ size and the minimum height shows three rows.</div>
10503
10767
  <div class="title">
10504
10768
  <h2><span class="status-dot dot-ok"></span> Toast</h2>
10505
10769
  <div class="badges">
10506
- <label for="c-33-components-toast-props" class="badge ok as-toggle">11 prop types</label>
10770
+ <label for="c-34-components-toast-props" class="badge ok as-toggle">11 prop types</label>
10507
10771
 
10508
- <label for="c-33-components-toast-stories" class="badge ok as-toggle">11 stories</label>
10772
+ <label for="c-34-components-toast-stories" class="badge ok as-toggle">11 stories</label>
10509
10773
 
10510
10774
 
10511
10775
  </div>
@@ -10524,10 +10788,10 @@ common feedback patterns.</div>
10524
10788
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10525
10789
 
10526
10790
 
10527
- <input id="c-33-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
10791
+ <input id="c-34-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
10528
10792
 
10529
10793
 
10530
- <input id="c-33-components-toast-props" class="tg tg-props" type="checkbox" hidden />
10794
+ <input id="c-34-components-toast-props" class="tg tg-props" type="checkbox" hidden />
10531
10795
 
10532
10796
  <div class="panels">
10533
10797
 
@@ -10956,9 +11220,9 @@ action (e.g., a sync icon for a sync-in-progress toast).</div>
10956
11220
  <div class="title">
10957
11221
  <h2><span class="status-dot dot-ok"></span> Tooltip</h2>
10958
11222
  <div class="badges">
10959
- <label for="c-34-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
11223
+ <label for="c-35-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
10960
11224
 
10961
- <label for="c-34-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
11225
+ <label for="c-35-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
10962
11226
 
10963
11227
 
10964
11228
  </div>
@@ -10977,10 +11241,10 @@ on hover or focus.</div>
10977
11241
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10978
11242
 
10979
11243
 
10980
- <input id="c-34-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
11244
+ <input id="c-35-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
10981
11245
 
10982
11246
 
10983
- <input id="c-34-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
11247
+ <input id="c-35-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
10984
11248
 
10985
11249
  <div class="panels">
10986
11250
 
@@ -11152,9 +11416,9 @@ sideOffset?: number = 4</code></pre>
11152
11416
  <div class="title">
11153
11417
  <h2><span class="status-dot dot-ok"></span> ValidationList</h2>
11154
11418
  <div class="badges">
11155
- <label for="c-35-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
11419
+ <label for="c-36-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
11156
11420
 
11157
- <label for="c-35-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
11421
+ <label for="c-36-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
11158
11422
 
11159
11423
 
11160
11424
  </div>
@@ -11175,10 +11439,10 @@ your form validation; it does not own the input or block submission.</div>
11175
11439
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
11176
11440
 
11177
11441
 
11178
- <input id="c-35-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
11442
+ <input id="c-36-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
11179
11443
 
11180
11444
 
11181
- <input id="c-35-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
11445
+ <input id="c-36-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
11182
11446
 
11183
11447
  <div class="panels">
11184
11448
 
@@ -6941,6 +6941,154 @@
6941
6941
  ],
6942
6942
  "import": "import { Field, FieldText, InputSearch, Label } from \"@agentero/design-system\";"
6943
6943
  },
6944
+ "components-inputtel": {
6945
+ "id": "components-inputtel",
6946
+ "name": "InputTel",
6947
+ "path": "./src/input-tel/input-tel.stories.tsx",
6948
+ "jsDocTags": {
6949
+ "summary": [
6950
+ "US phone input with a +1 prefix that masks as you type"
6951
+ ],
6952
+ "dataAttribute": [
6953
+ "{string} data-slot - Always set to \"input-tel\" on the frame"
6954
+ ],
6955
+ "example": [
6956
+ "<FieldText>\n <Label>Phone number</Label>\n <InputTel name=\"phone\" autoComplete=\"tel-national\" placeholder=\"(555) 000-0000\" />\n</FieldText>"
6957
+ ]
6958
+ },
6959
+ "description": "InputTel is a US phone number field that masks as you type: the digits are\nlaid out as `(555) 123-4567`, anything else is dropped and the caret stays\nnext to the digit just edited. It is an `Input` of `type=\"tel\"` inside an\n`InputGroup` frame with a `+1` prefix; `onChange` receives the native event\nwith the masked text already in `event.target.value`.",
6960
+ "summary": "US phone input with a +1 prefix that masks as you type",
6961
+ "reactComponentMeta": {
6962
+ "displayName": "InputTel",
6963
+ "exportName": "InputTel",
6964
+ "filePath": "/home/runner/work/design-system/design-system/src/input-tel/input-tel.tsx",
6965
+ "description": "InputTel is a US phone number field that masks as you type: the digits the\nuser enters are laid out as `(555) 123-4567` while typing, pasting or\ndeleting, anything that is not a digit is dropped, and the caret stays next\nto the digit just edited instead of jumping to the end. A leading `+1`\ncountry code, as pasted or filled in by the browser's autofill, is dropped\ntoo; past that, the eleventh digit and beyond are ignored. `onChange`\nreceives the native change event with the masked text already in\n`event.target.value`, so a controlled field, an uncontrolled one and one\nregistered with react-hook-form all store `(555) 123-4567`, never the bare\ndigits.\n\nIt is an [Input](?path=/docs/components-input--docs) of `type=\"tel\"` inside\nan [InputGroup](?path=/docs/components-inputgroup--docs) frame that always\nshows a `+1` prefix in front of the number. The prefix is part of the frame,\nnot of the value. `className` styles the frame. Every other prop goes to the\n`<input>`: the `ref`, `id`, `name`, `value`, `onChange`, `aria-*` and every\nother standard attribute, such as `placeholder`, `autoComplete` or\n`required`. Only `type` is not accepted. Inside a `FieldText` the input\ntakes its label, messages and states from the field as a plain `Input` does.\nThe invalid state comes from `aria-invalid`, and the frame paints the\ndestructive border.\n\nDo not use it for international numbers or extensions; the mask only knows\nthe ten-digit US format. For free-form text that happens to hold a number,\nuse `Input`.",
6966
+ "jsDocTags": {
6967
+ "summary": [
6968
+ "US phone input with a +1 prefix that masks as you type"
6969
+ ],
6970
+ "dataAttribute": [
6971
+ "{string} data-slot - Always set to \"input-tel\" on the frame"
6972
+ ],
6973
+ "example": [
6974
+ "<FieldText>\n <Label>Phone number</Label>\n <InputTel name=\"phone\" autoComplete=\"tel-national\" placeholder=\"(555) 000-0000\" />\n</FieldText>"
6975
+ ]
6976
+ },
6977
+ "props": {
6978
+ "size": {
6979
+ "name": "size",
6980
+ "required": false,
6981
+ "type": {
6982
+ "name": "enum",
6983
+ "raw": "\"sm\" | \"md\" | \"lg\"",
6984
+ "value": [
6985
+ {
6986
+ "value": "\"sm\""
6987
+ },
6988
+ {
6989
+ "value": "\"md\""
6990
+ },
6991
+ {
6992
+ "value": "\"lg\""
6993
+ }
6994
+ ]
6995
+ },
6996
+ "description": "Control height. Defaults to `'md'`.\n- `sm` (32px) — dense layouts where vertical space is tight.\n- `md` (40px) — standard form density.\n- `lg` (48px) — low-density forms and larger touch targets. Also raises\n the text to `base` and the corner radius to `lg`.",
6997
+ "defaultValue": null,
6998
+ "parent": {
6999
+ "name": "InputProps",
7000
+ "fileName": "src/input/input.tsx"
7001
+ },
7002
+ "declarations": [
7003
+ {
7004
+ "name": "TypeLiteral",
7005
+ "fileName": "src/input/input.tsx"
7006
+ }
7007
+ ]
7008
+ }
7009
+ }
7010
+ },
7011
+ "stories": [
7012
+ {
7013
+ "id": "components-inputtel--default",
7014
+ "name": "Default",
7015
+ "snippet": "const Default = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)} />;",
7016
+ "description": "An uncontrolled phone field with its `+1` prefix. Typing the ten digits lays\nthem out as `(555) 123-4567`, and every `onChange` call carries the masked\ntext in `event.target.value`; the prefix never enters the value.",
7017
+ "summary": "Phone field with a +1 prefix masking the digits as they are typed"
7018
+ },
7019
+ {
7020
+ "id": "components-inputtel--controlled",
7021
+ "name": "Controlled",
7022
+ "snippet": "const Controlled = () => {\n const [phone, setPhone] = useState('');\n\n return (\n <InputTel\n aria-label=\"Phone number\"\n value={phone}\n onChange={event => {\n setPhone(event.target.value);\n args.onChange?.(event);\n }} />\n );\n};",
7023
+ "description": "A controlled phone field. The state that owns the value stores what\n`event.target.value` reports, which is already masked, and the input shows\nit back unchanged. A partial number carries the separator that comes before\nthe next digit, so six digits read `(555) 123-`.",
7024
+ "summary": "Controlled phone field storing the masked value"
7025
+ },
7026
+ {
7027
+ "id": "components-inputtel--caret-in-the-middle",
7028
+ "name": "Caret In The Middle",
7029
+ "snippet": "const CaretInTheMiddle = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)} />;",
7030
+ "description": "Editing in the middle of a full number: a digit typed after `(5` shifts the\nrest right, the eleventh digit falls off the end and the caret stays after\nthe digit just typed instead of jumping to the end.",
7031
+ "summary": "Caret staying put when a digit is inserted mid-number"
7032
+ },
7033
+ {
7034
+ "id": "components-inputtel--backspace-over-mask-characters",
7035
+ "name": "Backspace Over Mask Characters",
7036
+ "snippet": "const BackspaceOverMaskCharacters = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)} />;",
7037
+ "description": "Backspace over the mask's own characters: after `555` the field shows\n`(555) `, and deleting backwards trims the separators along with the digit\nunder the caret instead of leaving a dangling `) `.",
7038
+ "summary": "Backspace trimming the separators the mask added"
7039
+ },
7040
+ {
7041
+ "id": "components-inputtel--paste",
7042
+ "name": "Paste",
7043
+ "snippet": "const Paste = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)} />;",
7044
+ "description": "A pasted number in any format is reduced to its digits and masked, so\n`555-123-4567` from a clipboard ends up as `(555) 123-4567`.",
7045
+ "summary": "Pasted number reduced to its digits and masked"
7046
+ },
7047
+ {
7048
+ "id": "components-inputtel--paste-with-country-code",
7049
+ "name": "Paste With Country Code",
7050
+ "snippet": "const PasteWithCountryCode = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)} />;",
7051
+ "description": "A number that carries the `+1` country code, as copied from a signature or\nfilled in by the browser's autofill, loses the code and keeps its ten\nnational digits: `+1 (555) 123-4567` ends up as `(555) 123-4567`.",
7052
+ "summary": "Pasted number with a +1 country code"
7053
+ },
7054
+ {
7055
+ "id": "components-inputtel--inside-field-text",
7056
+ "name": "Inside Field Text",
7057
+ "snippet": "const InsideFieldText = () => (\n <FieldText>\n <Label>Phone number</Label>\n <InputTel />\n <Field.Description>We only call about your account.</Field.Description>\n </FieldText>\n);",
7058
+ "description": "Inside a `FieldText` the input takes its label, description and states from\nthe field, and the masked value still reaches a handler the field provides.",
7059
+ "summary": "Label and description reaching the input through the field"
7060
+ },
7061
+ {
7062
+ "id": "components-inputtel--with-react-hook-form",
7063
+ "name": "With React Hook Form",
7064
+ "snippet": "const WithReactHookForm = () => {\n const { register, handleSubmit } = useForm<ContactValues>({\n defaultValues: { phone: '' }\n });\n\n return (\n <form\n aria-label=\"Contact\"\n noValidate\n onSubmit={handleSubmit(values => onSubmit(values))}\n style={{ display: 'grid', gap: '1rem' }}>\n <InputTel aria-label=\"Phone number\" {...register('phone')} />\n <Button type=\"submit\">Save</Button>\n </form>\n );\n};",
7065
+ "description": "Registered with react-hook-form, the form receives the masked value: the\n`register` handler reads `event.target.value` like any other change.",
7066
+ "summary": "Masked value reaching react-hook-form through register"
7067
+ },
7068
+ {
7069
+ "id": "components-inputtel--disabled",
7070
+ "name": "Disabled",
7071
+ "snippet": "const Disabled = () => <InputTel\n aria-label=\"Phone number\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n disabled\n defaultValue=\"(555) 123-4567\" />;",
7072
+ "description": "A disabled phone field keeps its masked value and takes no input.",
7073
+ "summary": "Disabled phone field"
7074
+ },
7075
+ {
7076
+ "id": "components-inputtel--invalid",
7077
+ "name": "Invalid",
7078
+ "snippet": "const Invalid = () => (\n <FieldText invalid>\n <Label>Phone number</Label>\n <InputTel defaultValue=\"(555) 12\" />\n <Field.Error errors={[{ message: 'Enter a 10-digit phone number.' }]} />\n </FieldText>\n);",
7079
+ "description": "`invalid` on the surrounding `FieldText` sets `aria-invalid` on the input,\nwhich paints the frame's destructive border, and `Field.Error` is announced\nwith the input.",
7080
+ "summary": "Invalid field driving the frame's destructive border"
7081
+ },
7082
+ {
7083
+ "id": "components-inputtel--all-sizes",
7084
+ "name": "All Sizes",
7085
+ "snippet": "const AllSizes = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputTel\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"sm\"\n aria-label=\"Small\"\n defaultValue=\"(555) 123-4567\" />\n <InputTel\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"md\"\n aria-label=\"Medium\"\n defaultValue=\"(555) 123-4567\" />\n <InputTel\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n size=\"lg\"\n aria-label=\"Large\"\n defaultValue=\"(555) 123-4567\" />\n</div>;",
7086
+ "description": "The three sizes match Input's: the frame is 32, 40 or 48px tall.",
7087
+ "summary": "The three sizes side by side"
7088
+ }
7089
+ ],
7090
+ "import": "import { Button, Field, FieldText, InputContext, InputTel, Label } from \"@agentero/design-system\";"
7091
+ },
6944
7092
  "components-input": {
6945
7093
  "id": "components-input",
6946
7094
  "name": "Input",
@@ -9907,6 +10055,6 @@
9907
10055
  },
9908
10056
  "meta": {
9909
10057
  "docgen": "react-component-meta",
9910
- "durationMs": 3150
10058
+ "durationMs": 4990
9911
10059
  }
9912
10060
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.34.2",
3
+ "version": "0.35.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",
@@ -105,6 +105,10 @@
105
105
  "types": "./src/input-search/index.d.ts",
106
106
  "import": "./src/input-search/index.js"
107
107
  },
108
+ "./input-tel": {
109
+ "types": "./src/input-tel/index.d.ts",
110
+ "import": "./src/input-tel/index.js"
111
+ },
108
112
  "./label": {
109
113
  "types": "./src/label/index.d.ts",
110
114
  "import": "./src/label/index.js"
@@ -0,0 +1,2 @@
1
+ export { InputTel } from './input-tel';
2
+ export type { InputTelProps } from './input-tel';
@@ -0,0 +1,2 @@
1
+ import { InputTel as e } from "./input-tel.js";
2
+ export { e as InputTel };
@@ -0,0 +1,42 @@
1
+ import { InputProps } from '../input';
2
+ export type InputTelProps = Omit<InputProps, 'type'>;
3
+ /**
4
+ * InputTel is a US phone number field that masks as you type: the digits the
5
+ * user enters are laid out as `(555) 123-4567` while typing, pasting or
6
+ * deleting, anything that is not a digit is dropped, and the caret stays next
7
+ * to the digit just edited instead of jumping to the end. A leading `+1`
8
+ * country code, as pasted or filled in by the browser's autofill, is dropped
9
+ * too; past that, the eleventh digit and beyond are ignored. `onChange`
10
+ * receives the native change event with the masked text already in
11
+ * `event.target.value`, so a controlled field, an uncontrolled one and one
12
+ * registered with react-hook-form all store `(555) 123-4567`, never the bare
13
+ * digits.
14
+ *
15
+ * It is an [Input](?path=/docs/components-input--docs) of `type="tel"` inside
16
+ * an [InputGroup](?path=/docs/components-inputgroup--docs) frame that always
17
+ * shows a `+1` prefix in front of the number. The prefix is part of the frame,
18
+ * not of the value. `className` styles the frame. Every other prop goes to the
19
+ * `<input>`: the `ref`, `id`, `name`, `value`, `onChange`, `aria-*` and every
20
+ * other standard attribute, such as `placeholder`, `autoComplete` or
21
+ * `required`. Only `type` is not accepted. Inside a `FieldText` the input
22
+ * takes its label, messages and states from the field as a plain `Input` does.
23
+ * The invalid state comes from `aria-invalid`, and the frame paints the
24
+ * destructive border.
25
+ *
26
+ * Do not use it for international numbers or extensions; the mask only knows
27
+ * the ten-digit US format. For free-form text that happens to hold a number,
28
+ * use `Input`.
29
+ *
30
+ * @summary US phone input with a +1 prefix that masks as you type
31
+ * @dataAttribute {string} data-slot - Always set to "input-tel" on the frame
32
+ *
33
+ * @example
34
+ * <FieldText>
35
+ * <Label>Phone number</Label>
36
+ * <InputTel name="phone" autoComplete="tel-national" placeholder="(555) 000-0000" />
37
+ * </FieldText>
38
+ */
39
+ export declare const InputTel: {
40
+ (props: InputTelProps): import("react/jsx-runtime").JSX.Element;
41
+ displayName: string;
42
+ };
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ import { mergeRefs as e } from "../../lib/merge-props.js";
3
+ import { InputContext as t, useInputContext as n } from "../input/context.js";
4
+ import { Input as r } from "../input/input.js";
5
+ import { InputGroup as i } from "../input-group/index.js";
6
+ import { US_PHONE_MASK as a, applyMask as o, getCursorPosition as s, getDeletionType as c, toNationalDigits as l } from "./mask.js";
7
+ import { useMemo as u, useRef as d } from "react";
8
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
9
+ //#region src/input-tel/input-tel.tsx
10
+ var m = (m) => {
11
+ let { className: h, onChange: g, ref: _, ...v } = n(m), y = d(null), b = u(() => e(y, _), [_]), x = (e) => {
12
+ let t = c(e.nativeEvent.inputType), { value: n, selectionStart: r } = e.target, i = o({
13
+ value: l(n),
14
+ mask: a,
15
+ maskSymbol: ".",
16
+ trimNonMaskCharsLeftover: t === "backward"
17
+ }), u = s({
18
+ cursorPositionAfterInput: r ?? 0,
19
+ phoneAfterInput: n,
20
+ phoneAfterFormatted: i,
21
+ deletion: t
22
+ });
23
+ e.target.value = i;
24
+ let d = () => {
25
+ let e = y.current;
26
+ e && e === document.activeElement && e.setSelectionRange(u, u);
27
+ };
28
+ g ? (g(e), Promise.resolve().then(d)) : d();
29
+ };
30
+ return /* @__PURE__ */ p(i.Root, {
31
+ "data-slot": "input-tel",
32
+ className: h,
33
+ children: [/* @__PURE__ */ f(i.Text, { children: "+1" }), /* @__PURE__ */ f(t, {
34
+ value: null,
35
+ children: /* @__PURE__ */ f(r, {
36
+ ...v,
37
+ type: "tel",
38
+ ref: b,
39
+ onChange: x
40
+ })
41
+ })]
42
+ });
43
+ };
44
+ m.displayName = "InputTel";
45
+ //#endregion
46
+ export { m as InputTel };
@@ -0,0 +1,51 @@
1
+ /** The US phone mask InputTel applies: three groups of 3, 3 and 4 digits. */
2
+ export declare const US_PHONE_MASK = "(...) ...-....";
3
+ /** The character that marks a digit slot in a mask. */
4
+ export declare const MASK_SYMBOL = ".";
5
+ /**
6
+ * Reduces a phone number in any format to its national digits: drops
7
+ * everything that is not a digit and, past ten digits, the leading `1` of the
8
+ * `+1` country code. No US area code starts with `1`, so a national number is
9
+ * never cut.
10
+ */
11
+ export declare const toNationalDigits: (value: string) => string;
12
+ type ApplyMaskArgs = {
13
+ value: string;
14
+ mask: string;
15
+ maskSymbol: string;
16
+ /**
17
+ * @description Removes all non-maskSymbol chars from the result's ending if the value is shorter than the mask
18
+ * @example
19
+ * value: "1234"
20
+ * mask: "(....) ...."
21
+ * if true -> "(1234"
22
+ * if false -> "(1234) "
23
+ */
24
+ trimNonMaskCharsLeftover?: boolean;
25
+ };
26
+ /**
27
+ * Places each character of `value` in the next `maskSymbol` slot of `mask`,
28
+ * keeping the mask's own characters in between. A value longer than the mask
29
+ * is cut; a shorter one stops at its last character, followed by the
30
+ * separators up to the next slot unless `trimNonMaskCharsLeftover` is set.
31
+ */
32
+ export declare const applyMask: ({ value, mask, maskSymbol, trimNonMaskCharsLeftover }: ApplyMaskArgs) => string;
33
+ export declare const isNumeric: (str?: string) => boolean;
34
+ type GetCursorPositionProps = {
35
+ phoneAfterInput: string;
36
+ phoneAfterFormatted: string;
37
+ cursorPositionAfterInput: number;
38
+ deletion?: 'forward' | 'backward' | undefined;
39
+ };
40
+ /**
41
+ * Finds where the caret should sit in the formatted phone after an edit, so
42
+ * that it stays after the digit the user just typed or removed instead of
43
+ * jumping to the end when the mask characters move around it.
44
+ */
45
+ export declare const getCursorPosition: ({ phoneAfterInput, phoneAfterFormatted, cursorPositionAfterInput, deletion }: GetCursorPositionProps) => number;
46
+ /**
47
+ * Reads the `inputType` of a native `input` event and tells whether the edit
48
+ * removed characters, and in which direction. `undefined` for an insertion.
49
+ */
50
+ export declare const getDeletionType: (inputType?: string) => "forward" | "backward" | undefined;
51
+ export {};
@@ -0,0 +1,39 @@
1
+ //#region src/input-tel/mask.ts
2
+ var e = "(...) ...-....", t = (e) => {
3
+ let t = e.replace(/\D/g, "");
4
+ return t.length > 10 && t.startsWith("1") ? t.slice(1) : t;
5
+ }, n = ({ value: e, mask: t, maskSymbol: n, trimNonMaskCharsLeftover: r = !1 }) => {
6
+ if (!e) return "";
7
+ let i = "", a = 0;
8
+ for (let o of t.split("")) {
9
+ if (a >= e.length) {
10
+ if (!r && o !== n) {
11
+ i += o;
12
+ continue;
13
+ }
14
+ break;
15
+ }
16
+ o === n ? (i += e[a], a += 1) : i += o;
17
+ }
18
+ return i;
19
+ }, r = (e) => e ? /^\d+$/.test(e) : !1, i = ({ phoneAfterInput: e, phoneAfterFormatted: t, cursorPositionAfterInput: n, deletion: i }) => {
20
+ let a = null;
21
+ for (let t = n - 1; t >= 0; --t) if (r(e[t])) {
22
+ a = t;
23
+ break;
24
+ }
25
+ if (a === null) {
26
+ for (let n = 0; n < e.length; n += 1) if (r(t[n])) return n;
27
+ return e.length;
28
+ }
29
+ let o = 0;
30
+ for (let t = 0; t < a; t += 1) r(e[t]) && (o += 1);
31
+ let s = 0, c = 0;
32
+ for (let e = 0; e < t.length && (s += 1, r(t[e]) && (c += 1), !(c >= o + 1)); e += 1);
33
+ if (i !== "backward") for (; !r(t[s]) && s < t.length;) s += 1;
34
+ return s;
35
+ }, a = (e) => {
36
+ if (e?.toLocaleLowerCase().includes("delete") ?? !1) return e?.toLocaleLowerCase().includes("forward") ? "forward" : "backward";
37
+ };
38
+ //#endregion
39
+ export { e as US_PHONE_MASK, n as applyMask, i as getCursorPosition, a as getDeletionType, r as isNumeric, t as toNationalDigits };