@agentero/design-system 0.35.1 → 0.36.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">38 components ok</span><span class="filter-pill ok" aria-disabled="true">308 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">39 components ok</span><span class="filter-pill ok" aria-disabled="true">319 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.9s</strong>.
597
+ Using <code>react-component-meta</code>. Generation took <strong>5.1s</strong>.
598
598
  </div>
599
599
  <h2 class="section-title">Components</h2>
600
600
  <div class="grid" role="list">
@@ -7697,6 +7697,327 @@ button&#39;s start corners with the frame.</div>
7697
7697
  </div>
7698
7698
 
7699
7699
 
7700
+ </div>
7701
+ </article>
7702
+ <article
7703
+ class="card
7704
+ no-error
7705
+ no-info
7706
+ no-story-error
7707
+ no-doc-error"
7708
+ role="listitem"
7709
+ aria-label="InputNumber">
7710
+ <div class="head">
7711
+ <div class="title">
7712
+ <h2><span class="status-dot dot-ok"></span> InputNumber</h2>
7713
+ <div class="badges">
7714
+ <label for="c-20-components-inputnumber-props" class="badge ok as-toggle">7 prop types</label>
7715
+
7716
+ <label for="c-20-components-inputnumber-stories" class="badge ok as-toggle">11 stories</label>
7717
+
7718
+
7719
+ </div>
7720
+ </div>
7721
+ <div class="meta" title="./src/input-number/input-number.stories.tsx">components-inputnumber · ./src/input-number/input-number.stories.tsx</div>
7722
+ <div>Numeric input formatting as you type, built on react-number-format</div>
7723
+ <div class="hint">InputNumber is a text field that only takes digits and a decimal point and
7724
+ formats them as the user types: thousands separators on request, leading
7725
+ zeros kept unless turned off, no sign. `onChange` fires with the formatted text in
7726
+ `event.target.value`. It is an `Input` driven by `react-number-format`, so
7727
+ it takes its label, messages and states from a surrounding `FieldText`.</div>
7728
+ <div class="kv"><span class="chip">summary: Numeric input formatting as you type, built on react-number-format</span><span class="chip">dataAttribute: {string} data-slot - Always set to &quot;input-number&quot;</span><span class="chip">example: &lt;FieldText&gt;
7729
+ &lt;Label&gt;Annual premium&lt;/Label&gt;
7730
+ &lt;InputNumber name=&quot;premium&quot; thousandSeparator allowLeadingZeros={false} /&gt;
7731
+ &lt;/FieldText&gt;</span></div>
7732
+ </div>
7733
+
7734
+ <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7735
+
7736
+
7737
+ <input id="c-20-components-inputnumber-stories" class="tg tg-stories" type="checkbox" hidden />
7738
+
7739
+
7740
+ <input id="c-20-components-inputnumber-props" class="tg tg-props" type="checkbox" hidden />
7741
+
7742
+ <div class="panels">
7743
+
7744
+
7745
+
7746
+ <div class="panel panel-props">
7747
+ <div class="note ok">
7748
+ <div class="row">
7749
+ <span class="ex-name">Prop types <small>(react-component-meta)</small></span>
7750
+ <span class="badge ok">7 prop types</span>
7751
+ </div>
7752
+ <pre><code>Component: src/input-number/input-number.tsx::InputNumber</code></pre>
7753
+ <pre><code>Props:</code></pre>
7754
+ <pre><code>/**
7755
+ * Keeps zeros typed in front of the number (`007`), which an identifier
7756
+ * such as an NPN or a ZIP code needs. Set it to `false` for an amount, so
7757
+ * `007` becomes `7` once the field loses focus, and `onChange` fires with
7758
+ * the trimmed value. Defaults to `true`.
7759
+ */
7760
+ allowLeadingZeros?: boolean = true
7761
+
7762
+ /**
7763
+ * Initial value of an uncontrolled field, in the same forms as `value`.
7764
+ */
7765
+ defaultValue?: string | number | null | undefined
7766
+
7767
+ /**
7768
+ * Highest value the field accepts. A keystroke or paste that would take
7769
+ * the number above it is dropped before it reaches the field, so `onChange`
7770
+ * is not called for it. No limit by default.
7771
+ */
7772
+ maxNumber?: number
7773
+
7774
+ /**
7775
+ * Control height. Defaults to `&#39;md&#39;`.
7776
+ * - `sm` (32px) — dense layouts where vertical space is tight.
7777
+ * - `md` (40px) — standard form density.
7778
+ * - `lg` (48px) — low-density forms and larger touch targets. Also raises
7779
+ * the text to `base` and the corner radius to `lg`.
7780
+ */
7781
+ size?: &quot;sm&quot; | &quot;md&quot; | &quot;lg&quot;
7782
+
7783
+ /**
7784
+ * Groups the integer part in thousands with a comma as the user types
7785
+ * (`1,234,567`). The separators are part of `event.target.value`. Defaults
7786
+ * to `false`.
7787
+ */
7788
+ thousandSeparator?: boolean = false
7789
+
7790
+ /**
7791
+ * The underlying `&lt;input&gt;` type. Defaults to `&#39;text&#39;`.
7792
+ * - `text` — the general case.
7793
+ * - `tel` — brings up the numeric keypad on phones, for codes and
7794
+ * identifiers that are digits only.
7795
+ * - `password` — masks the digits, for a PIN.
7796
+ */
7797
+ type?: &quot;text&quot; | &quot;tel&quot; | &quot;password&quot;
7798
+
7799
+ /**
7800
+ * The number to show, as a number or as a string of digits. It is displayed
7801
+ * formatted according to `thousandSeparator`. `null` and `&#39;&#39;` both clear the
7802
+ * field.
7803
+ */
7804
+ value?: string | number | null | undefined</code></pre>
7805
+ </div>
7806
+ </div>
7807
+
7808
+ <div class="panel panel-stories">
7809
+
7810
+
7811
+
7812
+ <div class="note ok">
7813
+ <div class="row">
7814
+ <span class="ex-name">Imports</span>
7815
+ </div>
7816
+ <pre><code>import { Controller, Field, FieldText, InputContext, InputGroup, InputNumber, Label } from "@agentero/design-system";</code></pre>
7817
+ </div>
7818
+
7819
+
7820
+ <div class="note ok">
7821
+ <div class="row">
7822
+ <span class="ex-name">Default</span>
7823
+ <span class="badge ok">story ok</span>
7824
+ </div>
7825
+ <div>Numeric field that ignores letters and signs</div>
7826
+ <div class="hint">Only digits and a decimal point make it into the field: letters and a minus
7827
+ sign are ignored as they are typed. `onChange` reports the text as it is shown, and the `ref`
7828
+ points at the `&lt;input&gt;` element.</div>
7829
+ <pre><code>const Default = () =&gt; &lt;InputNumber
7830
+ aria-label=&quot;Premium&quot;
7831
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7832
+ ref={inputRef} /&gt;;</code></pre>
7833
+ </div>
7834
+ <div class="note ok">
7835
+ <div class="row">
7836
+ <span class="ex-name">Type Tel</span>
7837
+ <span class="badge ok">story ok</span>
7838
+ </div>
7839
+ <div>Numeric keypad on phones through the tel input type</div>
7840
+ <div class="hint">`type=&quot;tel&quot;` brings up the numeric keypad on phones. The field still
7841
+ formats and filters the same way; only the underlying input type changes.</div>
7842
+ <pre><code>const TypeTel = () =&gt; &lt;InputNumber
7843
+ aria-label=&quot;NPN&quot;
7844
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7845
+ type=&quot;tel&quot; /&gt;;</code></pre>
7846
+ </div>
7847
+ <div class="note ok">
7848
+ <div class="row">
7849
+ <span class="ex-name">Thousand Separator</span>
7850
+ <span class="badge ok">story ok</span>
7851
+ </div>
7852
+ <div>Thousands grouped with commas as the user types</div>
7853
+ <div class="hint">With `thousandSeparator` the integer part is grouped with commas as the
7854
+ user types, and the commas are part of what `onChange` reports.</div>
7855
+ <pre><code>const ThousandSeparator = () =&gt; &lt;InputNumber
7856
+ aria-label=&quot;Premium&quot;
7857
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7858
+ thousandSeparator /&gt;;</code></pre>
7859
+ </div>
7860
+ <div class="note ok">
7861
+ <div class="row">
7862
+ <span class="ex-name">Max Number</span>
7863
+ <span class="badge ok">story ok</span>
7864
+ </div>
7865
+ <div>Keystrokes that would exceed a maximum are dropped</div>
7866
+ <div class="hint">`maxNumber` caps the value: a keystroke that would push the number above
7867
+ it is dropped, so the field keeps its previous value and `onChange` is not
7868
+ called for it.</div>
7869
+ <pre><code>const MaxNumber = () =&gt; &lt;InputNumber
7870
+ aria-label=&quot;Commission rate&quot;
7871
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7872
+ maxNumber={100} /&gt;;</code></pre>
7873
+ </div>
7874
+ <div class="note ok">
7875
+ <div class="row">
7876
+ <span class="ex-name">Leading Zeros</span>
7877
+ <span class="badge ok">story ok</span>
7878
+ </div>
7879
+ <div>Leading zeros kept by default, dropped on blur when turned off</div>
7880
+ <div class="hint">Leading zeros are kept by default, as an NPN or a ZIP code needs. With
7881
+ `allowLeadingZeros` off they are dropped once the field loses focus, which
7882
+ is what an amount wants, and `onChange` reports the trimmed value.</div>
7883
+ <pre><code>const LeadingZeros = () =&gt; &lt;div style={{ display: &#39;grid&#39;, gap: &#39;1rem&#39; }}&gt;
7884
+ &lt;InputNumber
7885
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7886
+ aria-label=&quot;ZIP code&quot; /&gt;
7887
+ &lt;InputNumber
7888
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7889
+ aria-label=&quot;Amount&quot;
7890
+ allowLeadingZeros={false} /&gt;
7891
+ &lt;/div&gt;;</code></pre>
7892
+ </div>
7893
+ <div class="note ok">
7894
+ <div class="row">
7895
+ <span class="ex-name">Controlled</span>
7896
+ <span class="badge ok">story ok</span>
7897
+ </div>
7898
+ <div>Controlled field showing a numeric value formatted</div>
7899
+ <div class="hint">A controlled field takes `value` as a number and shows it formatted. The
7900
+ state keeps what `onChange` reports, so it holds the formatted text.</div>
7901
+ <pre><code>const Controlled = () =&gt; {
7902
+ const [premium, setPremium] = useState&lt;number | string&gt;(1234);
7903
+
7904
+ return (
7905
+ &lt;InputNumber
7906
+ aria-label=&quot;Premium&quot;
7907
+ thousandSeparator
7908
+ value={premium}
7909
+ onChange={event =&gt; {
7910
+ setPremium(event.target.value);
7911
+ args.onChange?.(event);
7912
+ }} /&gt;
7913
+ );
7914
+ };</code></pre>
7915
+ </div>
7916
+ <div class="note ok">
7917
+ <div class="row">
7918
+ <span class="ex-name">Inside Input Group</span>
7919
+ <span class="badge ok">story ok</span>
7920
+ </div>
7921
+ <div>Amount with a currency prefix inside an input group</div>
7922
+ <div class="hint">Inside an `InputGroup` the field sits behind a currency affix and the
7923
+ frame takes its focus, disabled and invalid states from the input.</div>
7924
+ <pre><code>const InsideInputGroup = () =&gt; &lt;InputGroup.Root&gt;
7925
+ &lt;InputGroup.Text&gt;$&lt;/InputGroup.Text&gt;
7926
+ &lt;InputNumber
7927
+ aria-label=&quot;Premium&quot;
7928
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7929
+ thousandSeparator
7930
+ allowLeadingZeros={false}
7931
+ placeholder=&quot;0&quot; /&gt;
7932
+ &lt;/InputGroup.Root&gt;;</code></pre>
7933
+ </div>
7934
+ <div class="note ok">
7935
+ <div class="row">
7936
+ <span class="ex-name">Inside Field Text</span>
7937
+ <span class="badge ok">story ok</span>
7938
+ </div>
7939
+ <div>Label, messages and states reaching the input from a FieldText</div>
7940
+ <div class="hint">Inside a `FieldText` the input takes its label, description and states from
7941
+ the field. `invalid` on the field sets `aria-invalid`, which paints the
7942
+ destructive border, and `disabled` reaches the input the same way.</div>
7943
+ <pre><code>const InsideFieldText = () =&gt; (
7944
+ &lt;div style={{ display: &#39;grid&#39;, gap: &#39;1.5rem&#39; }}&gt;
7945
+ &lt;FieldText required&gt;
7946
+ &lt;Label&gt;NPN&lt;/Label&gt;
7947
+ &lt;InputNumber type=&quot;tel&quot; /&gt;
7948
+ &lt;Field.Description&gt;The 7 to 10 digit National Producer Number.&lt;/Field.Description&gt;
7949
+ &lt;/FieldText&gt;
7950
+ &lt;FieldText invalid&gt;
7951
+ &lt;Label&gt;Years in business&lt;/Label&gt;
7952
+ &lt;InputNumber defaultValue={150} /&gt;
7953
+ &lt;Field.Error errors={[{ message: &#39;Enter at most 100 years.&#39; }]} /&gt;
7954
+ &lt;/FieldText&gt;
7955
+ &lt;FieldText disabled&gt;
7956
+ &lt;Label&gt;Agency code&lt;/Label&gt;
7957
+ &lt;InputNumber defaultValue=&quot;00412&quot; /&gt;
7958
+ &lt;/FieldText&gt;
7959
+ &lt;/div&gt;
7960
+ );</code></pre>
7961
+ </div>
7962
+ <div class="note ok">
7963
+ <div class="row">
7964
+ <span class="ex-name">Disabled</span>
7965
+ <span class="badge ok">story ok</span>
7966
+ </div>
7967
+ <div>Disabled field with a formatted value</div>
7968
+ <div class="hint">A disabled field keeps its formatted value and takes no input.</div>
7969
+ <pre><code>const Disabled = () =&gt; &lt;InputNumber
7970
+ aria-label=&quot;Premium&quot;
7971
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7972
+ disabled
7973
+ thousandSeparator
7974
+ defaultValue={12500} /&gt;;</code></pre>
7975
+ </div>
7976
+ <div class="note ok">
7977
+ <div class="row">
7978
+ <span class="ex-name">Invalid</span>
7979
+ <span class="badge ok">story ok</span>
7980
+ </div>
7981
+ <div>Invalid state driven by aria-invalid</div>
7982
+ <div class="hint">`aria-invalid` paints the destructive border, the same way it does on a
7983
+ plain `Input`.</div>
7984
+ <pre><code>const Invalid = () =&gt; &lt;InputNumber
7985
+ aria-label=&quot;Premium&quot;
7986
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
7987
+ aria-invalid
7988
+ defaultValue={0} /&gt;;</code></pre>
7989
+ </div>
7990
+ <div class="note ok">
7991
+ <div class="row">
7992
+ <span class="ex-name">All Sizes</span>
7993
+ <span class="badge ok">story ok</span>
7994
+ </div>
7995
+ <div>The three sizes side by side</div>
7996
+ <div class="hint">The three sizes match Input&#39;s: 32, 40 or 48px tall.</div>
7997
+ <pre><code>const AllSizes = () =&gt; &lt;div style={{ display: &#39;grid&#39;, gap: &#39;1rem&#39; }}&gt;
7998
+ &lt;InputNumber
7999
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8000
+ thousandSeparator
8001
+ defaultValue={1234}
8002
+ size=&quot;sm&quot;
8003
+ aria-label=&quot;Small&quot; /&gt;
8004
+ &lt;InputNumber
8005
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8006
+ thousandSeparator
8007
+ defaultValue={1234}
8008
+ size=&quot;md&quot;
8009
+ aria-label=&quot;Medium&quot; /&gt;
8010
+ &lt;InputNumber
8011
+ onChange={fn((event: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; event.target.value)}
8012
+ thousandSeparator
8013
+ defaultValue={1234}
8014
+ size=&quot;lg&quot;
8015
+ aria-label=&quot;Large&quot; /&gt;
8016
+ &lt;/div&gt;;</code></pre>
8017
+ </div>
8018
+ </div>
8019
+
8020
+
7700
8021
  </div>
7701
8022
  </article>
7702
8023
  <article
@@ -7711,9 +8032,9 @@ button&#39;s start corners with the frame.</div>
7711
8032
  <div class="title">
7712
8033
  <h2><span class="status-dot dot-ok"></span> InputPassword</h2>
7713
8034
  <div class="badges">
7714
- <label for="c-20-components-inputpassword-props" class="badge ok as-toggle">1 prop type</label>
8035
+ <label for="c-21-components-inputpassword-props" class="badge ok as-toggle">1 prop type</label>
7715
8036
 
7716
- <label for="c-20-components-inputpassword-stories" class="badge ok as-toggle">5 stories</label>
8037
+ <label for="c-21-components-inputpassword-stories" class="badge ok as-toggle">5 stories</label>
7717
8038
 
7718
8039
 
7719
8040
  </div>
@@ -7734,10 +8055,10 @@ rendered while the input is disabled.</div>
7734
8055
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7735
8056
 
7736
8057
 
7737
- <input id="c-20-components-inputpassword-stories" class="tg tg-stories" type="checkbox" hidden />
8058
+ <input id="c-21-components-inputpassword-stories" class="tg tg-stories" type="checkbox" hidden />
7738
8059
 
7739
8060
 
7740
- <input id="c-20-components-inputpassword-props" class="tg tg-props" type="checkbox" hidden />
8061
+ <input id="c-21-components-inputpassword-props" class="tg tg-props" type="checkbox" hidden />
7741
8062
 
7742
8063
  <div class="panels">
7743
8064
 
@@ -7879,9 +8200,9 @@ the field through the frame. A disabled field removes the toggle too.</div>
7879
8200
  <div class="title">
7880
8201
  <h2><span class="status-dot dot-ok"></span> InputSearch</h2>
7881
8202
  <div class="badges">
7882
- <label for="c-21-components-inputsearch-props" class="badge ok as-toggle">3 prop types</label>
8203
+ <label for="c-22-components-inputsearch-props" class="badge ok as-toggle">3 prop types</label>
7883
8204
 
7884
- <label for="c-21-components-inputsearch-stories" class="badge ok as-toggle">7 stories</label>
8205
+ <label for="c-22-components-inputsearch-stories" class="badge ok as-toggle">7 stories</label>
7885
8206
 
7886
8207
 
7887
8208
  </div>
@@ -7904,10 +8225,10 @@ once there is text. It is an `Input` of `type=&quot;search&quot;` inside an
7904
8225
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
7905
8226
 
7906
8227
 
7907
- <input id="c-21-components-inputsearch-stories" class="tg tg-stories" type="checkbox" hidden />
8228
+ <input id="c-22-components-inputsearch-stories" class="tg tg-stories" type="checkbox" hidden />
7908
8229
 
7909
8230
 
7910
- <input id="c-21-components-inputsearch-props" class="tg tg-props" type="checkbox" hidden />
8231
+ <input id="c-22-components-inputsearch-props" class="tg tg-props" type="checkbox" hidden />
7911
8232
 
7912
8233
  <div class="panels">
7913
8234
 
@@ -8109,9 +8430,9 @@ several search fields does not read &quot;Clear search&quot; twice.</div>
8109
8430
  <div class="title">
8110
8431
  <h2><span class="status-dot dot-ok"></span> InputTel</h2>
8111
8432
  <div class="badges">
8112
- <label for="c-22-components-inputtel-props" class="badge ok as-toggle">1 prop type</label>
8433
+ <label for="c-23-components-inputtel-props" class="badge ok as-toggle">1 prop type</label>
8113
8434
 
8114
- <label for="c-22-components-inputtel-stories" class="badge ok as-toggle">11 stories</label>
8435
+ <label for="c-23-components-inputtel-stories" class="badge ok as-toggle">11 stories</label>
8115
8436
 
8116
8437
 
8117
8438
  </div>
@@ -8132,10 +8453,10 @@ with the masked text already in `event.target.value`.</div>
8132
8453
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8133
8454
 
8134
8455
 
8135
- <input id="c-22-components-inputtel-stories" class="tg tg-stories" type="checkbox" hidden />
8456
+ <input id="c-23-components-inputtel-stories" class="tg tg-stories" type="checkbox" hidden />
8136
8457
 
8137
8458
 
8138
- <input id="c-22-components-inputtel-props" class="tg tg-props" type="checkbox" hidden />
8459
+ <input id="c-23-components-inputtel-props" class="tg tg-props" type="checkbox" hidden />
8139
8460
 
8140
8461
  <div class="panels">
8141
8462
 
@@ -8373,9 +8694,9 @@ with the input.</div>
8373
8694
  <div class="title">
8374
8695
  <h2><span class="status-dot dot-ok"></span> Label</h2>
8375
8696
  <div class="badges">
8376
- <label for="c-23-components-label-props" class="badge ok as-toggle">3 prop types</label>
8697
+ <label for="c-24-components-label-props" class="badge ok as-toggle">3 prop types</label>
8377
8698
 
8378
- <label for="c-23-components-label-stories" class="badge ok as-toggle">5 stories</label>
8699
+ <label for="c-24-components-label-stories" class="badge ok as-toggle">5 stories</label>
8379
8700
 
8380
8701
 
8381
8702
  </div>
@@ -8395,10 +8716,10 @@ still has to carry `required` / `aria-required`.</div>
8395
8716
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8396
8717
 
8397
8718
 
8398
- <input id="c-23-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
8719
+ <input id="c-24-components-label-stories" class="tg tg-stories" type="checkbox" hidden />
8399
8720
 
8400
8721
 
8401
- <input id="c-23-components-label-props" class="tg tg-props" type="checkbox" hidden />
8722
+ <input id="c-24-components-label-props" class="tg tg-props" type="checkbox" hidden />
8402
8723
 
8403
8724
  <div class="panels">
8404
8725
 
@@ -8522,9 +8843,9 @@ field&#39;s control with no `htmlFor`. An explicit `htmlFor` still wins.</div>
8522
8843
  <div class="title">
8523
8844
  <h2><span class="status-dot dot-ok"></span> Loading</h2>
8524
8845
  <div class="badges">
8525
- <label for="c-24-components-loading-props" class="badge ok as-toggle">1 prop type</label>
8846
+ <label for="c-25-components-loading-props" class="badge ok as-toggle">1 prop type</label>
8526
8847
 
8527
- <label for="c-24-components-loading-stories" class="badge ok as-toggle">7 stories</label>
8848
+ <label for="c-25-components-loading-stories" class="badge ok as-toggle">7 stories</label>
8528
8849
 
8529
8850
 
8530
8851
  </div>
@@ -8541,10 +8862,10 @@ parent font size via `em`-based size variants (`sm`, `md`, `lg`).</div>
8541
8862
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8542
8863
 
8543
8864
 
8544
- <input id="c-24-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
8865
+ <input id="c-25-components-loading-stories" class="tg tg-stories" type="checkbox" hidden />
8545
8866
 
8546
8867
 
8547
- <input id="c-24-components-loading-props" class="tg tg-props" type="checkbox" hidden />
8868
+ <input id="c-25-components-loading-props" class="tg tg-props" type="checkbox" hidden />
8548
8869
 
8549
8870
  <div class="panels">
8550
8871
 
@@ -8695,9 +9016,9 @@ against the dark background.</div>
8695
9016
  <div class="title">
8696
9017
  <h2><span class="status-dot dot-ok"></span> Modal.Root</h2>
8697
9018
  <div class="badges">
8698
- <label for="c-25-components-modal-props" class="badge ok as-toggle">5 prop types</label>
9019
+ <label for="c-26-components-modal-props" class="badge ok as-toggle">5 prop types</label>
8699
9020
 
8700
- <label for="c-25-components-modal-stories" class="badge ok as-toggle">6 stories</label>
9021
+ <label for="c-26-components-modal-stories" class="badge ok as-toggle">6 stories</label>
8701
9022
 
8702
9023
 
8703
9024
  </div>
@@ -8743,10 +9064,10 @@ import { Modal } from &#39;@agentero/design-system/modal&#39;;
8743
9064
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8744
9065
 
8745
9066
 
8746
- <input id="c-25-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
9067
+ <input id="c-26-components-modal-stories" class="tg tg-stories" type="checkbox" hidden />
8747
9068
 
8748
9069
 
8749
- <input id="c-25-components-modal-props" class="tg tg-props" type="checkbox" hidden />
9070
+ <input id="c-26-components-modal-props" class="tg tg-props" type="checkbox" hidden />
8750
9071
 
8751
9072
  <div class="panels">
8752
9073
 
@@ -8966,9 +9287,9 @@ announced as an `alertdialog`. Only the footer actions close it.</div>
8966
9287
  <div class="title">
8967
9288
  <h2><span class="status-dot dot-ok"></span> Pagination</h2>
8968
9289
  <div class="badges">
8969
- <label for="c-26-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
9290
+ <label for="c-27-components-pagination-props" class="badge ok as-toggle">5 prop types</label>
8970
9291
 
8971
- <label for="c-26-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
9292
+ <label for="c-27-components-pagination-stories" class="badge ok as-toggle">7 stories</label>
8972
9293
 
8973
9294
 
8974
9295
  </div>
@@ -8998,10 +9319,10 @@ const [page, setPage] = useState(1);
8998
9319
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
8999
9320
 
9000
9321
 
9001
- <input id="c-26-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
9322
+ <input id="c-27-components-pagination-stories" class="tg tg-stories" type="checkbox" hidden />
9002
9323
 
9003
9324
 
9004
- <input id="c-26-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
9325
+ <input id="c-27-components-pagination-props" class="tg tg-props" type="checkbox" hidden />
9005
9326
 
9006
9327
  <div class="panels">
9007
9328
 
@@ -9156,9 +9477,9 @@ entries summary follow along.</div>
9156
9477
  <div class="title">
9157
9478
  <h2><span class="status-dot dot-ok"></span> Popover.Root</h2>
9158
9479
  <div class="badges">
9159
- <label for="c-27-components-popover-props" class="badge ok as-toggle">5 prop types</label>
9480
+ <label for="c-28-components-popover-props" class="badge ok as-toggle">5 prop types</label>
9160
9481
 
9161
- <label for="c-27-components-popover-stories" class="badge ok as-toggle">5 stories</label>
9482
+ <label for="c-28-components-popover-stories" class="badge ok as-toggle">5 stories</label>
9162
9483
 
9163
9484
 
9164
9485
  </div>
@@ -9187,10 +9508,10 @@ import { Popover } from &#39;@agentero/design-system/popover&#39;;
9187
9508
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9188
9509
 
9189
9510
 
9190
- <input id="c-27-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
9511
+ <input id="c-28-components-popover-stories" class="tg tg-stories" type="checkbox" hidden />
9191
9512
 
9192
9513
 
9193
- <input id="c-27-components-popover-props" class="tg tg-props" type="checkbox" hidden />
9514
+ <input id="c-28-components-popover-props" class="tg tg-props" type="checkbox" hidden />
9194
9515
 
9195
9516
  <div class="panels">
9196
9517
 
@@ -9389,9 +9710,9 @@ focus returning to its trigger once the modal closes.</div>
9389
9710
  <div class="title">
9390
9711
  <h2><span class="status-dot dot-ok"></span> Progress</h2>
9391
9712
  <div class="badges">
9392
- <label for="c-28-components-progress-props" class="badge ok as-toggle">4 prop types</label>
9713
+ <label for="c-29-components-progress-props" class="badge ok as-toggle">4 prop types</label>
9393
9714
 
9394
- <label for="c-28-components-progress-stories" class="badge ok as-toggle">4 stories</label>
9715
+ <label for="c-29-components-progress-stories" class="badge ok as-toggle">4 stories</label>
9395
9716
 
9396
9717
 
9397
9718
  </div>
@@ -9410,10 +9731,10 @@ with `currentColor`, so recolor it with a plain text class — e.g.
9410
9731
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9411
9732
 
9412
9733
 
9413
- <input id="c-28-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
9734
+ <input id="c-29-components-progress-stories" class="tg tg-stories" type="checkbox" hidden />
9414
9735
 
9415
9736
 
9416
- <input id="c-28-components-progress-props" class="tg tg-props" type="checkbox" hidden />
9737
+ <input id="c-29-components-progress-props" class="tg tg-props" type="checkbox" hidden />
9417
9738
 
9418
9739
  <div class="panels">
9419
9740
 
@@ -9532,7 +9853,7 @@ percentage: number</code></pre>
9532
9853
  <div class="badges">
9533
9854
 
9534
9855
 
9535
- <label for="c-29-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
9856
+ <label for="c-30-components-skeleton-stories" class="badge ok as-toggle">4 stories</label>
9536
9857
 
9537
9858
 
9538
9859
  </div>
@@ -9549,7 +9870,7 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
9549
9870
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9550
9871
 
9551
9872
 
9552
- <input id="c-29-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
9873
+ <input id="c-30-components-skeleton-stories" class="tg tg-stories" type="checkbox" hidden />
9553
9874
 
9554
9875
 
9555
9876
 
@@ -9645,9 +9966,9 @@ shape it with Tailwind classes to mirror the content it stands in for.</div>
9645
9966
  <div class="title">
9646
9967
  <h2><span class="status-dot dot-ok"></span> Switch</h2>
9647
9968
  <div class="badges">
9648
- <label for="c-30-components-switch-props" class="badge ok as-toggle">8 prop types</label>
9969
+ <label for="c-31-components-switch-props" class="badge ok as-toggle">8 prop types</label>
9649
9970
 
9650
- <label for="c-30-components-switch-stories" class="badge ok as-toggle">7 stories</label>
9971
+ <label for="c-31-components-switch-stories" class="badge ok as-toggle">7 stories</label>
9651
9972
 
9652
9973
 
9653
9974
  </div>
@@ -9663,10 +9984,10 @@ a Checkbox for a single boolean that applies instantly, and pair it with a
9663
9984
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9664
9985
 
9665
9986
 
9666
- <input id="c-30-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
9987
+ <input id="c-31-components-switch-stories" class="tg tg-stories" type="checkbox" hidden />
9667
9988
 
9668
9989
 
9669
- <input id="c-30-components-switch-props" class="tg tg-props" type="checkbox" hidden />
9990
+ <input id="c-31-components-switch-props" class="tg tg-props" type="checkbox" hidden />
9670
9991
 
9671
9992
  <div class="panels">
9672
9993
 
@@ -9847,9 +10168,9 @@ size?: &quot;sm&quot; | &quot;md&quot;</code></pre>
9847
10168
  <div class="title">
9848
10169
  <h2><span class="status-dot dot-ok"></span> Table.Root</h2>
9849
10170
  <div class="badges">
9850
- <label for="c-31-components-table-props" class="badge ok as-toggle">6 prop types</label>
10171
+ <label for="c-32-components-table-props" class="badge ok as-toggle">6 prop types</label>
9851
10172
 
9852
- <label for="c-31-components-table-stories" class="badge ok as-toggle">10 stories</label>
10173
+ <label for="c-32-components-table-stories" class="badge ok as-toggle">10 stories</label>
9853
10174
 
9854
10175
 
9855
10176
  </div>
@@ -9880,10 +10201,10 @@ import { Table } from &#39;@agentero/design-system/table&#39;;
9880
10201
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
9881
10202
 
9882
10203
 
9883
- <input id="c-31-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
10204
+ <input id="c-32-components-table-stories" class="tg tg-stories" type="checkbox" hidden />
9884
10205
 
9885
10206
 
9886
- <input id="c-31-components-table-props" class="tg tg-props" type="checkbox" hidden />
10207
+ <input id="c-32-components-table-props" class="tg tg-props" type="checkbox" hidden />
9887
10208
 
9888
10209
  <div class="panels">
9889
10210
 
@@ -10101,9 +10422,9 @@ keeps its padding.</div>
10101
10422
  <div class="title">
10102
10423
  <h2><span class="status-dot dot-ok"></span> Tabs.Root</h2>
10103
10424
  <div class="badges">
10104
- <label for="c-32-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
10425
+ <label for="c-33-components-tabs-props" class="badge ok as-toggle">9 prop types</label>
10105
10426
 
10106
- <label for="c-32-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
10427
+ <label for="c-33-components-tabs-stories" class="badge ok as-toggle">8 stories</label>
10107
10428
 
10108
10429
 
10109
10430
  </div>
@@ -10134,10 +10455,10 @@ import { Tabs } from &#39;@agentero/design-system/tabs&#39;;
10134
10455
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10135
10456
 
10136
10457
 
10137
- <input id="c-32-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
10458
+ <input id="c-33-components-tabs-stories" class="tg tg-stories" type="checkbox" hidden />
10138
10459
 
10139
10460
 
10140
- <input id="c-32-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
10461
+ <input id="c-33-components-tabs-props" class="tg tg-props" type="checkbox" hidden />
10141
10462
 
10142
10463
  <div class="panels">
10143
10464
 
@@ -10358,9 +10679,9 @@ tablist role is announced.</div>
10358
10679
  <div class="title">
10359
10680
  <h2><span class="status-dot dot-ok"></span> Tag</h2>
10360
10681
  <div class="badges">
10361
- <label for="c-33-components-tag-props" class="badge ok as-toggle">8 prop types</label>
10682
+ <label for="c-34-components-tag-props" class="badge ok as-toggle">8 prop types</label>
10362
10683
 
10363
- <label for="c-33-components-tag-stories" class="badge ok as-toggle">12 stories</label>
10684
+ <label for="c-34-components-tag-stories" class="badge ok as-toggle">12 stories</label>
10364
10685
 
10365
10686
 
10366
10687
  </div>
@@ -10379,10 +10700,10 @@ it an interactive link or button, which adds the pointer cursor and hover fill.<
10379
10700
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10380
10701
 
10381
10702
 
10382
- <input id="c-33-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
10703
+ <input id="c-34-components-tag-stories" class="tg tg-stories" type="checkbox" hidden />
10383
10704
 
10384
10705
 
10385
- <input id="c-33-components-tag-props" class="tg tg-props" type="checkbox" hidden />
10706
+ <input id="c-34-components-tag-props" class="tg tg-props" type="checkbox" hidden />
10386
10707
 
10387
10708
  <div class="panels">
10388
10709
 
@@ -10807,9 +11128,9 @@ leading + trailing icons, and icon-only.</div>
10807
11128
  <div class="title">
10808
11129
  <h2><span class="status-dot dot-ok"></span> TextArea</h2>
10809
11130
  <div class="badges">
10810
- <label for="c-34-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
11131
+ <label for="c-35-components-textarea-props" class="badge ok as-toggle">1 prop type</label>
10811
11132
 
10812
- <label for="c-34-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
11133
+ <label for="c-35-components-textarea-stories" class="badge ok as-toggle">6 stories</label>
10813
11134
 
10814
11135
 
10815
11136
  </div>
@@ -10826,10 +11147,10 @@ states. It wires nothing on its own: `id`, `aria-invalid` and
10826
11147
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10827
11148
 
10828
11149
 
10829
- <input id="c-34-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
11150
+ <input id="c-35-components-textarea-stories" class="tg tg-stories" type="checkbox" hidden />
10830
11151
 
10831
11152
 
10832
- <input id="c-34-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
11153
+ <input id="c-35-components-textarea-props" class="tg tg-props" type="checkbox" hidden />
10833
11154
 
10834
11155
  <div class="panels">
10835
11156
 
@@ -10961,9 +11282,9 @@ size and the minimum height shows three rows.</div>
10961
11282
  <div class="title">
10962
11283
  <h2><span class="status-dot dot-ok"></span> Toast</h2>
10963
11284
  <div class="badges">
10964
- <label for="c-35-components-toast-props" class="badge ok as-toggle">11 prop types</label>
11285
+ <label for="c-36-components-toast-props" class="badge ok as-toggle">11 prop types</label>
10965
11286
 
10966
- <label for="c-35-components-toast-stories" class="badge ok as-toggle">11 stories</label>
11287
+ <label for="c-36-components-toast-stories" class="badge ok as-toggle">11 stories</label>
10967
11288
 
10968
11289
 
10969
11290
  </div>
@@ -10982,10 +11303,10 @@ common feedback patterns.</div>
10982
11303
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
10983
11304
 
10984
11305
 
10985
- <input id="c-35-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
11306
+ <input id="c-36-components-toast-stories" class="tg tg-stories" type="checkbox" hidden />
10986
11307
 
10987
11308
 
10988
- <input id="c-35-components-toast-props" class="tg tg-props" type="checkbox" hidden />
11309
+ <input id="c-36-components-toast-props" class="tg tg-props" type="checkbox" hidden />
10989
11310
 
10990
11311
  <div class="panels">
10991
11312
 
@@ -11414,9 +11735,9 @@ action (e.g., a sync icon for a sync-in-progress toast).</div>
11414
11735
  <div class="title">
11415
11736
  <h2><span class="status-dot dot-ok"></span> Tooltip</h2>
11416
11737
  <div class="badges">
11417
- <label for="c-36-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
11738
+ <label for="c-37-components-tooltip-props" class="badge ok as-toggle">12 prop types</label>
11418
11739
 
11419
- <label for="c-36-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
11740
+ <label for="c-37-components-tooltip-stories" class="badge ok as-toggle">5 stories</label>
11420
11741
 
11421
11742
 
11422
11743
  </div>
@@ -11435,10 +11756,10 @@ on hover or focus.</div>
11435
11756
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
11436
11757
 
11437
11758
 
11438
- <input id="c-36-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
11759
+ <input id="c-37-components-tooltip-stories" class="tg tg-stories" type="checkbox" hidden />
11439
11760
 
11440
11761
 
11441
- <input id="c-36-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
11762
+ <input id="c-37-components-tooltip-props" class="tg tg-props" type="checkbox" hidden />
11442
11763
 
11443
11764
  <div class="panels">
11444
11765
 
@@ -11610,9 +11931,9 @@ sideOffset?: number = 4</code></pre>
11610
11931
  <div class="title">
11611
11932
  <h2><span class="status-dot dot-ok"></span> ValidationList</h2>
11612
11933
  <div class="badges">
11613
- <label for="c-37-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
11934
+ <label for="c-38-components-validationlist-props" class="badge ok as-toggle">2 prop types</label>
11614
11935
 
11615
- <label for="c-37-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
11936
+ <label for="c-38-components-validationlist-stories" class="badge ok as-toggle">4 stories</label>
11616
11937
 
11617
11938
 
11618
11939
  </div>
@@ -11633,10 +11954,10 @@ your form validation; it does not own the input or block submission.</div>
11633
11954
  <!-- ⬇️ Hidden toggles must be siblings BEFORE .panels -->
11634
11955
 
11635
11956
 
11636
- <input id="c-37-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
11957
+ <input id="c-38-components-validationlist-stories" class="tg tg-stories" type="checkbox" hidden />
11637
11958
 
11638
11959
 
11639
- <input id="c-37-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
11960
+ <input id="c-38-components-validationlist-props" class="tg tg-props" type="checkbox" hidden />
11640
11961
 
11641
11962
  <div class="panels">
11642
11963
 
@@ -6673,6 +6673,284 @@
6673
6673
  ],
6674
6674
  "import": "import {\n Button,\n Divider,\n DropdownMenu,\n Field,\n FieldText,\n IconKeyboardArrowDown,\n IconSearch,\n Input,\n InputGroup,\n Label,\n Tag,\n} from \"@agentero/design-system\";"
6675
6675
  },
6676
+ "components-inputnumber": {
6677
+ "id": "components-inputnumber",
6678
+ "name": "InputNumber",
6679
+ "path": "./src/input-number/input-number.stories.tsx",
6680
+ "jsDocTags": {
6681
+ "summary": [
6682
+ "Numeric input formatting as you type, built on react-number-format"
6683
+ ],
6684
+ "dataAttribute": [
6685
+ "{string} data-slot - Always set to \"input-number\""
6686
+ ],
6687
+ "example": [
6688
+ "<FieldText>\n <Label>Annual premium</Label>\n <InputNumber name=\"premium\" thousandSeparator allowLeadingZeros={false} />\n</FieldText>"
6689
+ ]
6690
+ },
6691
+ "description": "InputNumber is a text field that only takes digits and a decimal point and\nformats them as the user types: thousands separators on request, leading\nzeros kept unless turned off, no sign. `onChange` fires with the formatted text in\n`event.target.value`. It is an `Input` driven by `react-number-format`, so\nit takes its label, messages and states from a surrounding `FieldText`.",
6692
+ "summary": "Numeric input formatting as you type, built on react-number-format",
6693
+ "reactComponentMeta": {
6694
+ "displayName": "InputNumber",
6695
+ "exportName": "InputNumber",
6696
+ "filePath": "/home/runner/work/design-system/design-system/src/input-number/input-number.tsx",
6697
+ "description": "InputNumber is a text field that only takes digits and a decimal point and\nformats them as the user types: a thousands separator when\n`thousandSeparator` is set, leading zeros kept unless `allowLeadingZeros` is\noff, no sign, no decimal rounding.\nAnything that is not a digit or the decimal point is ignored, and\n`maxNumber` drops any edit that would exceed it. `onChange` fires with the\nformatted text in `event.target.value` (`\"1,234\"`, not `1234`), which is\nwhat a form library stores; parse it where the number is consumed.\n\nIt is an [Input](?path=/docs/components-input--docs) driven by\n`react-number-format`, so it keeps the caret in place while it reformats\nand takes its wiring from a surrounding `FieldText` as a plain `Input`\ndoes. Every standard `<input>` attribute is accepted and forwarded, such as\n`placeholder`, `required`, `maxLength` or `aria-invalid`; `type` is limited\nto `text`, `tel` and `password`, and `value` and `defaultValue` also take a\nnumber or `null`. The invalid state comes from `aria-invalid`, which paints\nthe destructive border. Place it inside an\n[InputGroup](?path=/docs/components-inputgroup--docs) to show a currency or\nunit affix.\n\nDo not use it for a phone number, which is formatted by `InputTel`, nor for\nfree text with some digits in it, which is a plain `Input`. It is not a\n`type=\"number\"` input on purpose: that one drops leading zeros and cannot\nshow separators, which identifiers and amounts both need.\n\nIt renders from `value`, not from the DOM, so bind it to a form through a\ncontrolled value (react-hook-form's `Controller` or `useController`), not\nthrough `register`: a value `register` writes into the `<input>`, such as a\ndefault or a `reset`, is wiped on the next render.",
6698
+ "jsDocTags": {
6699
+ "summary": [
6700
+ "Numeric input formatting as you type, built on react-number-format"
6701
+ ],
6702
+ "dataAttribute": [
6703
+ "{string} data-slot - Always set to \"input-number\""
6704
+ ],
6705
+ "example": [
6706
+ "<FieldText>\n <Label>Annual premium</Label>\n <InputNumber name=\"premium\" thousandSeparator allowLeadingZeros={false} />\n</FieldText>"
6707
+ ]
6708
+ },
6709
+ "props": {
6710
+ "size": {
6711
+ "name": "size",
6712
+ "required": false,
6713
+ "type": {
6714
+ "name": "enum",
6715
+ "raw": "\"sm\" | \"md\" | \"lg\"",
6716
+ "value": [
6717
+ {
6718
+ "value": "\"sm\""
6719
+ },
6720
+ {
6721
+ "value": "\"md\""
6722
+ },
6723
+ {
6724
+ "value": "\"lg\""
6725
+ }
6726
+ ]
6727
+ },
6728
+ "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`.",
6729
+ "defaultValue": null,
6730
+ "parent": {
6731
+ "name": "InputProps",
6732
+ "fileName": "src/input/input.tsx"
6733
+ },
6734
+ "declarations": [
6735
+ {
6736
+ "name": "TypeLiteral",
6737
+ "fileName": "src/input/input.tsx"
6738
+ }
6739
+ ]
6740
+ },
6741
+ "maxNumber": {
6742
+ "name": "maxNumber",
6743
+ "required": false,
6744
+ "type": {
6745
+ "name": "number"
6746
+ },
6747
+ "description": "Highest value the field accepts. A keystroke or paste that would take\nthe number above it is dropped before it reaches the field, so `onChange`\nis not called for it. No limit by default.",
6748
+ "defaultValue": null,
6749
+ "parent": {
6750
+ "name": "InputNumberProps",
6751
+ "fileName": "src/input-number/input-number.tsx"
6752
+ },
6753
+ "declarations": [
6754
+ {
6755
+ "name": "TypeLiteral",
6756
+ "fileName": "src/input-number/input-number.tsx"
6757
+ }
6758
+ ]
6759
+ },
6760
+ "thousandSeparator": {
6761
+ "name": "thousandSeparator",
6762
+ "required": false,
6763
+ "type": {
6764
+ "name": "boolean"
6765
+ },
6766
+ "description": "Groups the integer part in thousands with a comma as the user types\n(`1,234,567`). The separators are part of `event.target.value`. Defaults\nto `false`.",
6767
+ "defaultValue": {
6768
+ "value": "false"
6769
+ },
6770
+ "parent": {
6771
+ "name": "InputNumberProps",
6772
+ "fileName": "src/input-number/input-number.tsx"
6773
+ },
6774
+ "declarations": [
6775
+ {
6776
+ "name": "TypeLiteral",
6777
+ "fileName": "src/input-number/input-number.tsx"
6778
+ }
6779
+ ]
6780
+ },
6781
+ "allowLeadingZeros": {
6782
+ "name": "allowLeadingZeros",
6783
+ "required": false,
6784
+ "type": {
6785
+ "name": "boolean"
6786
+ },
6787
+ "description": "Keeps zeros typed in front of the number (`007`), which an identifier\nsuch as an NPN or a ZIP code needs. Set it to `false` for an amount, so\n`007` becomes `7` once the field loses focus, and `onChange` fires with\nthe trimmed value. Defaults to `true`.",
6788
+ "defaultValue": {
6789
+ "value": "true"
6790
+ },
6791
+ "parent": {
6792
+ "name": "InputNumberProps",
6793
+ "fileName": "src/input-number/input-number.tsx"
6794
+ },
6795
+ "declarations": [
6796
+ {
6797
+ "name": "TypeLiteral",
6798
+ "fileName": "src/input-number/input-number.tsx"
6799
+ }
6800
+ ]
6801
+ },
6802
+ "type": {
6803
+ "name": "type",
6804
+ "required": false,
6805
+ "type": {
6806
+ "name": "enum",
6807
+ "raw": "\"text\" | \"tel\" | \"password\"",
6808
+ "value": [
6809
+ {
6810
+ "value": "\"text\""
6811
+ },
6812
+ {
6813
+ "value": "\"tel\""
6814
+ },
6815
+ {
6816
+ "value": "\"password\""
6817
+ }
6818
+ ]
6819
+ },
6820
+ "description": "The underlying `<input>` type. Defaults to `'text'`.\n- `text` — the general case.\n- `tel` — brings up the numeric keypad on phones, for codes and\n identifiers that are digits only.\n- `password` — masks the digits, for a PIN.",
6821
+ "defaultValue": null,
6822
+ "parent": {
6823
+ "name": "InputNumberProps",
6824
+ "fileName": "src/input-number/input-number.tsx"
6825
+ },
6826
+ "declarations": [
6827
+ {
6828
+ "name": "TypeLiteral",
6829
+ "fileName": "src/input-number/input-number.tsx"
6830
+ }
6831
+ ]
6832
+ },
6833
+ "value": {
6834
+ "name": "value",
6835
+ "required": false,
6836
+ "type": {
6837
+ "name": "string | number | null | undefined"
6838
+ },
6839
+ "description": "The number to show, as a number or as a string of digits. It is displayed\nformatted according to `thousandSeparator`. `null` and `''` both clear the\nfield.",
6840
+ "defaultValue": null,
6841
+ "parent": {
6842
+ "name": "InputNumberProps",
6843
+ "fileName": "src/input-number/input-number.tsx"
6844
+ },
6845
+ "declarations": [
6846
+ {
6847
+ "name": "TypeLiteral",
6848
+ "fileName": "src/input-number/input-number.tsx"
6849
+ }
6850
+ ]
6851
+ },
6852
+ "defaultValue": {
6853
+ "name": "defaultValue",
6854
+ "required": false,
6855
+ "type": {
6856
+ "name": "string | number | null | undefined"
6857
+ },
6858
+ "description": "Initial value of an uncontrolled field, in the same forms as `value`.",
6859
+ "defaultValue": null,
6860
+ "parent": {
6861
+ "name": "InputNumberProps",
6862
+ "fileName": "src/input-number/input-number.tsx"
6863
+ },
6864
+ "declarations": [
6865
+ {
6866
+ "name": "TypeLiteral",
6867
+ "fileName": "src/input-number/input-number.tsx"
6868
+ }
6869
+ ]
6870
+ }
6871
+ }
6872
+ },
6873
+ "stories": [
6874
+ {
6875
+ "id": "components-inputnumber--default",
6876
+ "name": "Default",
6877
+ "snippet": "const Default = () => <InputNumber\n aria-label=\"Premium\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n ref={inputRef} />;",
6878
+ "description": "Only digits and a decimal point make it into the field: letters and a minus\nsign are ignored as they are typed. `onChange` reports the text as it is shown, and the `ref`\npoints at the `<input>` element.",
6879
+ "summary": "Numeric field that ignores letters and signs"
6880
+ },
6881
+ {
6882
+ "id": "components-inputnumber--type-tel",
6883
+ "name": "Type Tel",
6884
+ "snippet": "const TypeTel = () => <InputNumber\n aria-label=\"NPN\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n type=\"tel\" />;",
6885
+ "description": "`type=\"tel\"` brings up the numeric keypad on phones. The field still\nformats and filters the same way; only the underlying input type changes.",
6886
+ "summary": "Numeric keypad on phones through the tel input type"
6887
+ },
6888
+ {
6889
+ "id": "components-inputnumber--thousand-separator",
6890
+ "name": "Thousand Separator",
6891
+ "snippet": "const ThousandSeparator = () => <InputNumber\n aria-label=\"Premium\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n thousandSeparator />;",
6892
+ "description": "With `thousandSeparator` the integer part is grouped with commas as the\nuser types, and the commas are part of what `onChange` reports.",
6893
+ "summary": "Thousands grouped with commas as the user types"
6894
+ },
6895
+ {
6896
+ "id": "components-inputnumber--max-number",
6897
+ "name": "Max Number",
6898
+ "snippet": "const MaxNumber = () => <InputNumber\n aria-label=\"Commission rate\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n maxNumber={100} />;",
6899
+ "description": "`maxNumber` caps the value: a keystroke that would push the number above\nit is dropped, so the field keeps its previous value and `onChange` is not\ncalled for it.",
6900
+ "summary": "Keystrokes that would exceed a maximum are dropped"
6901
+ },
6902
+ {
6903
+ "id": "components-inputnumber--leading-zeros",
6904
+ "name": "Leading Zeros",
6905
+ "snippet": "const LeadingZeros = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputNumber\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n aria-label=\"ZIP code\" />\n <InputNumber\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n aria-label=\"Amount\"\n allowLeadingZeros={false} />\n</div>;",
6906
+ "description": "Leading zeros are kept by default, as an NPN or a ZIP code needs. With\n`allowLeadingZeros` off they are dropped once the field loses focus, which\nis what an amount wants, and `onChange` reports the trimmed value.",
6907
+ "summary": "Leading zeros kept by default, dropped on blur when turned off"
6908
+ },
6909
+ {
6910
+ "id": "components-inputnumber--controlled",
6911
+ "name": "Controlled",
6912
+ "snippet": "const Controlled = () => {\n const [premium, setPremium] = useState<number | string>(1234);\n\n return (\n <InputNumber\n aria-label=\"Premium\"\n thousandSeparator\n value={premium}\n onChange={event => {\n setPremium(event.target.value);\n args.onChange?.(event);\n }} />\n );\n};",
6913
+ "description": "A controlled field takes `value` as a number and shows it formatted. The\nstate keeps what `onChange` reports, so it holds the formatted text.",
6914
+ "summary": "Controlled field showing a numeric value formatted"
6915
+ },
6916
+ {
6917
+ "id": "components-inputnumber--inside-input-group",
6918
+ "name": "Inside Input Group",
6919
+ "snippet": "const InsideInputGroup = () => <InputGroup.Root>\n <InputGroup.Text>$</InputGroup.Text>\n <InputNumber\n aria-label=\"Premium\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n thousandSeparator\n allowLeadingZeros={false}\n placeholder=\"0\" />\n</InputGroup.Root>;",
6920
+ "description": "Inside an `InputGroup` the field sits behind a currency affix and the\nframe takes its focus, disabled and invalid states from the input.",
6921
+ "summary": "Amount with a currency prefix inside an input group"
6922
+ },
6923
+ {
6924
+ "id": "components-inputnumber--inside-field-text",
6925
+ "name": "Inside Field Text",
6926
+ "snippet": "const InsideFieldText = () => (\n <div style={{ display: 'grid', gap: '1.5rem' }}>\n <FieldText required>\n <Label>NPN</Label>\n <InputNumber type=\"tel\" />\n <Field.Description>The 7 to 10 digit National Producer Number.</Field.Description>\n </FieldText>\n <FieldText invalid>\n <Label>Years in business</Label>\n <InputNumber defaultValue={150} />\n <Field.Error errors={[{ message: 'Enter at most 100 years.' }]} />\n </FieldText>\n <FieldText disabled>\n <Label>Agency code</Label>\n <InputNumber defaultValue=\"00412\" />\n </FieldText>\n </div>\n);",
6927
+ "description": "Inside a `FieldText` the input takes its label, description and states from\nthe field. `invalid` on the field sets `aria-invalid`, which paints the\ndestructive border, and `disabled` reaches the input the same way.",
6928
+ "summary": "Label, messages and states reaching the input from a FieldText"
6929
+ },
6930
+ {
6931
+ "id": "components-inputnumber--disabled",
6932
+ "name": "Disabled",
6933
+ "snippet": "const Disabled = () => <InputNumber\n aria-label=\"Premium\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n disabled\n thousandSeparator\n defaultValue={12500} />;",
6934
+ "description": "A disabled field keeps its formatted value and takes no input.",
6935
+ "summary": "Disabled field with a formatted value"
6936
+ },
6937
+ {
6938
+ "id": "components-inputnumber--invalid",
6939
+ "name": "Invalid",
6940
+ "snippet": "const Invalid = () => <InputNumber\n aria-label=\"Premium\"\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n aria-invalid\n defaultValue={0} />;",
6941
+ "description": "`aria-invalid` paints the destructive border, the same way it does on a\nplain `Input`.",
6942
+ "summary": "Invalid state driven by aria-invalid"
6943
+ },
6944
+ {
6945
+ "id": "components-inputnumber--all-sizes",
6946
+ "name": "All Sizes",
6947
+ "snippet": "const AllSizes = () => <div style={{ display: 'grid', gap: '1rem' }}>\n <InputNumber\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n thousandSeparator\n defaultValue={1234}\n size=\"sm\"\n aria-label=\"Small\" />\n <InputNumber\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n thousandSeparator\n defaultValue={1234}\n size=\"md\"\n aria-label=\"Medium\" />\n <InputNumber\n onChange={fn((event: ChangeEvent<HTMLInputElement>) => event.target.value)}\n thousandSeparator\n defaultValue={1234}\n size=\"lg\"\n aria-label=\"Large\" />\n</div>;",
6948
+ "description": "The three sizes match Input's: 32, 40 or 48px tall.",
6949
+ "summary": "The three sizes side by side"
6950
+ }
6951
+ ],
6952
+ "import": "import { Controller, Field, FieldText, InputContext, InputGroup, InputNumber, Label } from \"@agentero/design-system\";"
6953
+ },
6676
6954
  "components-inputpassword": {
6677
6955
  "id": "components-inputpassword",
6678
6956
  "name": "InputPassword",
@@ -10223,6 +10501,6 @@
10223
10501
  },
10224
10502
  "meta": {
10225
10503
  "docgen": "react-component-meta",
10226
- "durationMs": 3866
10504
+ "durationMs": 5136
10227
10505
  }
10228
10506
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.35.1",
3
+ "version": "0.36.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",
@@ -97,6 +97,10 @@
97
97
  "types": "./src/input-group/index.d.ts",
98
98
  "import": "./src/input-group/index.js"
99
99
  },
100
+ "./input-number": {
101
+ "types": "./src/input-number/index.d.ts",
102
+ "import": "./src/input-number/index.js"
103
+ },
100
104
  "./input-password": {
101
105
  "types": "./src/input-password/index.d.ts",
102
106
  "import": "./src/input-password/index.js"
@@ -214,6 +218,7 @@
214
218
  "@radix-ui/react-tooltip": "^1.2.11",
215
219
  "clsx": "^2.1.1",
216
220
  "cmdk": "^1.1.1",
221
+ "react-number-format": "5.4.5",
217
222
  "sonner": "^2.0.7",
218
223
  "tailwind-merge": "^3.5.0",
219
224
  "tailwind-variants": "^3.2.2",
@@ -6,12 +6,21 @@ import { InputProps } from './input';
6
6
  * and `ref`. An `Input` with no provider above it reads `null` and uses only
7
7
  * its own props.
8
8
  */
9
- export declare const InputContext: import('react').Context<Partial<InputProps> | null>;
9
+ export type InputContextValue = Pick<InputProps, 'id' | 'aria-describedby' | 'aria-invalid' | 'required' | 'disabled' | 'readOnly' | 'name' | 'onChange' | 'onBlur' | 'ref'> & {
10
+ value?: string | number;
11
+ };
12
+ export declare const InputContext: import('react').Context<InputContextValue | null>;
10
13
  /**
11
14
  * Merges the surrounding `InputContext`, if any, under the input's own props.
12
15
  * Own props win; handlers present on both sides are chained and
13
16
  * `aria-describedby` ids are concatenated. In development it warns when the
14
17
  * input carries an `id` that differs from the one the container provides,
15
18
  * since the label would then point elsewhere.
19
+ *
20
+ * Generic over the props so a wrapper whose props narrow or widen the
21
+ * input's (a `type` limited to a few values, a `value` that takes `null`)
22
+ * gets its own type back, with the context's wiring merged in. The props must
23
+ * accept everything the context can carry; when they do not, `props` resolves
24
+ * to `never` and the call does not compile.
16
25
  */
17
- export declare const useInputContext: (props: InputProps) => InputProps;
26
+ export declare const useInputContext: <P extends Pick<InputProps, "id">>(props: P & (InputContextValue extends Partial<P> ? unknown : never)) => P;
@@ -1,3 +1,4 @@
1
1
  export { InputContext, useInputContext } from './context';
2
+ export type { InputContextValue } from './context';
2
3
  export { Input, inputRecipe } from './input';
3
4
  export type { InputProps, InputSize, InputVariants } from './input';
@@ -0,0 +1,2 @@
1
+ export { InputNumber } from './input-number';
2
+ export type { InputNumberProps } from './input-number';
@@ -0,0 +1,2 @@
1
+ import { InputNumber as e } from "./input-number.js";
2
+ export { e as InputNumber };
@@ -0,0 +1,82 @@
1
+ import { InputProps } from '../input';
2
+ export type InputNumberProps = Omit<InputProps, 'type' | 'value' | 'defaultValue'> & {
3
+ /**
4
+ * Highest value the field accepts. A keystroke or paste that would take
5
+ * the number above it is dropped before it reaches the field, so `onChange`
6
+ * is not called for it. No limit by default.
7
+ */
8
+ maxNumber?: number;
9
+ /**
10
+ * Groups the integer part in thousands with a comma as the user types
11
+ * (`1,234,567`). The separators are part of `event.target.value`. Defaults
12
+ * to `false`.
13
+ */
14
+ thousandSeparator?: boolean;
15
+ /**
16
+ * Keeps zeros typed in front of the number (`007`), which an identifier
17
+ * such as an NPN or a ZIP code needs. Set it to `false` for an amount, so
18
+ * `007` becomes `7` once the field loses focus, and `onChange` fires with
19
+ * the trimmed value. Defaults to `true`.
20
+ */
21
+ allowLeadingZeros?: boolean;
22
+ /**
23
+ * The underlying `<input>` type. Defaults to `'text'`.
24
+ * - `text` — the general case.
25
+ * - `tel` — brings up the numeric keypad on phones, for codes and
26
+ * identifiers that are digits only.
27
+ * - `password` — masks the digits, for a PIN.
28
+ */
29
+ type?: 'password' | 'tel' | 'text';
30
+ /**
31
+ * The number to show, as a number or as a string of digits. It is displayed
32
+ * formatted according to `thousandSeparator`. `null` and `''` both clear the
33
+ * field.
34
+ */
35
+ value?: number | string | null;
36
+ /** Initial value of an uncontrolled field, in the same forms as `value`. */
37
+ defaultValue?: number | string | null;
38
+ };
39
+ /**
40
+ * InputNumber is a text field that only takes digits and a decimal point and
41
+ * formats them as the user types: a thousands separator when
42
+ * `thousandSeparator` is set, leading zeros kept unless `allowLeadingZeros` is
43
+ * off, no sign, no decimal rounding.
44
+ * Anything that is not a digit or the decimal point is ignored, and
45
+ * `maxNumber` drops any edit that would exceed it. `onChange` fires with the
46
+ * formatted text in `event.target.value` (`"1,234"`, not `1234`), which is
47
+ * what a form library stores; parse it where the number is consumed.
48
+ *
49
+ * It is an [Input](?path=/docs/components-input--docs) driven by
50
+ * `react-number-format`, so it keeps the caret in place while it reformats
51
+ * and takes its wiring from a surrounding `FieldText` as a plain `Input`
52
+ * does. Every standard `<input>` attribute is accepted and forwarded, such as
53
+ * `placeholder`, `required`, `maxLength` or `aria-invalid`; `type` is limited
54
+ * to `text`, `tel` and `password`, and `value` and `defaultValue` also take a
55
+ * number or `null`. The invalid state comes from `aria-invalid`, which paints
56
+ * the destructive border. Place it inside an
57
+ * [InputGroup](?path=/docs/components-inputgroup--docs) to show a currency or
58
+ * unit affix.
59
+ *
60
+ * Do not use it for a phone number, which is formatted by `InputTel`, nor for
61
+ * free text with some digits in it, which is a plain `Input`. It is not a
62
+ * `type="number"` input on purpose: that one drops leading zeros and cannot
63
+ * show separators, which identifiers and amounts both need.
64
+ *
65
+ * It renders from `value`, not from the DOM, so bind it to a form through a
66
+ * controlled value (react-hook-form's `Controller` or `useController`), not
67
+ * through `register`: a value `register` writes into the `<input>`, such as a
68
+ * default or a `reset`, is wiped on the next render.
69
+ *
70
+ * @summary Numeric input formatting as you type, built on react-number-format
71
+ * @dataAttribute {string} data-slot - Always set to "input-number"
72
+ *
73
+ * @example
74
+ * <FieldText>
75
+ * <Label>Annual premium</Label>
76
+ * <InputNumber name="premium" thousandSeparator allowLeadingZeros={false} />
77
+ * </FieldText>
78
+ */
79
+ export declare const InputNumber: {
80
+ (props: InputNumberProps): import("react/jsx-runtime").JSX.Element;
81
+ displayName: string;
82
+ };
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { InputContext as e, useInputContext as t } from "../input/context.js";
3
+ import { Input as n } from "../input/input.js";
4
+ import { jsx as r } from "react/jsx-runtime";
5
+ import { NumericFormat as i } from "react-number-format";
6
+ //#region src/input-number/input-number.tsx
7
+ var a = (t) => /* @__PURE__ */ r(e, {
8
+ value: null,
9
+ children: /* @__PURE__ */ r(n, {
10
+ "data-slot": "input-number",
11
+ ...t
12
+ })
13
+ }), o = (e) => {
14
+ let { ref: n, maxNumber: o, thousandSeparator: s = !1, allowLeadingZeros: c = !0, onChange: l, ...u } = t(e);
15
+ return /* @__PURE__ */ r(i, {
16
+ ...u,
17
+ customInput: a,
18
+ getInputRef: n,
19
+ thousandSeparator: s,
20
+ allowLeadingZeros: c,
21
+ allowNegative: !1,
22
+ onChange: l,
23
+ onValueChange: ({ formattedValue: e }, { event: t, source: n }) => {
24
+ if (n !== "event" || t?.type !== "blur") return;
25
+ let r = t.target;
26
+ r.value = e, l?.(Object.assign(Object.create(t), t, { type: "change" }));
27
+ },
28
+ isAllowed: ({ floatValue: e }) => o === void 0 || e === void 0 || e <= o
29
+ });
30
+ };
31
+ o.displayName = "InputNumber";
32
+ //#endregion
33
+ export { o as InputNumber };