@ngrok/mantle 0.72.0 → 0.73.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +0 -33
  2. package/dist/accordion.d.ts +1 -1
  3. package/dist/agent.json +2 -1
  4. package/dist/alert-dialog.d.ts +4 -4
  5. package/dist/alert.d.ts +3 -3
  6. package/dist/anchor.d.ts +1 -1
  7. package/dist/{as-child-DQHfEmYB.d.ts → as-child-C2PttRwz.d.ts} +1 -1
  8. package/dist/badge.d.ts +2 -2
  9. package/dist/{button-BMgAxAwM.d.ts → button-CoGmk7_d.d.ts} +3 -3
  10. package/dist/button.d.ts +3 -3
  11. package/dist/card.d.ts +1 -1
  12. package/dist/checkbox.d.ts +1 -1
  13. package/dist/checkbox.js +1 -1
  14. package/dist/checkbox.js.map +1 -1
  15. package/dist/code-block.d.ts +40 -14
  16. package/dist/code-block.js +2 -2
  17. package/dist/code-block.js.map +1 -1
  18. package/dist/code-block_highlight-utils.d.ts +1 -1
  19. package/dist/code-block_highlight-utils.js +1 -1
  20. package/dist/code.d.ts +1 -1
  21. package/dist/color.d.ts +1 -1
  22. package/dist/combobox.d.ts +2 -2
  23. package/dist/combobox.js +1 -1
  24. package/dist/combobox.js.map +1 -1
  25. package/dist/command.d.ts +3 -3
  26. package/dist/command.js +1 -1
  27. package/dist/compose-refs-DZ3cPi47.js.map +1 -1
  28. package/dist/{copy-to-clipboard-DjOD_Mwb.js → copy-to-clipboard-CNMRyck4.js} +1 -1
  29. package/dist/{copy-to-clipboard-DjOD_Mwb.js.map → copy-to-clipboard-CNMRyck4.js.map} +1 -1
  30. package/dist/data-table.d.ts +3 -3
  31. package/dist/data-table.js +1 -1
  32. package/dist/data-table.js.map +1 -1
  33. package/dist/{deep-non-nullable-VFm1T3JZ.d.ts → deep-non-nullable-CT7hWCFG.d.ts} +1 -1
  34. package/dist/description-list.d.ts +1 -1
  35. package/dist/{dialog-BHzl9eye.js → dialog-B1KCB7JT.js} +2 -2
  36. package/dist/dialog-B1KCB7JT.js.map +1 -0
  37. package/dist/dialog.d.ts +2 -2
  38. package/dist/dialog.js +1 -1
  39. package/dist/{direction-DtBAQn7p.d.ts → direction-CVntIxOS.d.ts} +1 -1
  40. package/dist/{direction-DsB-pD9V.js → direction-HqPHXGIs.js} +1 -1
  41. package/dist/{direction-DsB-pD9V.js.map → direction-HqPHXGIs.js.map} +1 -1
  42. package/dist/{dropdown-menu-CzUNYIfA.d.ts → dropdown-menu-DVvNlA72.d.ts} +2 -2
  43. package/dist/{dropdown-menu-Ducs2SEn.js → dropdown-menu-DY4w933w.js} +2 -2
  44. package/dist/{dropdown-menu-Ducs2SEn.js.map → dropdown-menu-DY4w933w.js.map} +1 -1
  45. package/dist/dropdown-menu.d.ts +1 -1
  46. package/dist/dropdown-menu.js +1 -1
  47. package/dist/empty.d.ts +2 -2
  48. package/dist/field.d.ts +601 -0
  49. package/dist/field.js +2 -0
  50. package/dist/field.js.map +1 -0
  51. package/dist/hooks.d.ts +3 -3
  52. package/dist/hooks.js +1 -1
  53. package/dist/hooks.js.map +1 -1
  54. package/dist/{icon-DKMJm20j.d.ts → icon-D_BMDi_q.d.ts} +2 -2
  55. package/dist/{icon-button-BnK4K7YK.d.ts → icon-button-Dty-yfE2.d.ts} +3 -3
  56. package/dist/icon.d.ts +3 -3
  57. package/dist/icons.d.ts +3 -3
  58. package/dist/icons.js +1 -1
  59. package/dist/{in-view-pia_SVdE.js → in-view-BLZVEGFC.js} +1 -1
  60. package/dist/{in-view-pia_SVdE.js.map → in-view-BLZVEGFC.js.map} +1 -1
  61. package/dist/{in-view-Da08Bx6l.d.ts → in-view-DdIrfU4u.d.ts} +1 -1
  62. package/dist/{index-DkMUaYsw.d.ts → index-CVk4t5hk.d.ts} +1 -1
  63. package/dist/{index-DOJUH34Z.d.ts → index-DIBURJqf.d.ts} +3 -3
  64. package/dist/{index-rtz7SwEq.d.ts → index-TI92Xpg5.d.ts} +1 -1
  65. package/dist/index-j46YISoN.d.ts +22 -0
  66. package/dist/input.d.ts +192 -3
  67. package/dist/input.js +1 -1
  68. package/dist/input.js.map +1 -1
  69. package/dist/{is-input-CUEWaxtA.js → is-input-CEEoHxXN.js} +1 -1
  70. package/dist/{is-input-CUEWaxtA.js.map → is-input-CEEoHxXN.js.map} +1 -1
  71. package/dist/{kbd-CAVUiqBT.js → kbd-CbMxDL9E.js} +1 -1
  72. package/dist/{kbd-CAVUiqBT.js.map → kbd-CbMxDL9E.js.map} +1 -1
  73. package/dist/kbd.js +1 -1
  74. package/dist/label-x6FcOpxc.js +2 -0
  75. package/dist/label-x6FcOpxc.js.map +1 -0
  76. package/dist/label.d.ts +9 -0
  77. package/dist/label.js +1 -2
  78. package/dist/llms.txt +2 -1
  79. package/dist/media-object.d.ts +1 -1
  80. package/dist/multi-select.d.ts +2 -2
  81. package/dist/multi-select.js +1 -1
  82. package/dist/multi-select.js.map +1 -1
  83. package/dist/otp-input.d.ts +2 -2
  84. package/dist/otp-input.js +1 -1
  85. package/dist/otp-input.js.map +1 -1
  86. package/dist/pagination.d.ts +3 -3
  87. package/dist/pagination.js +1 -1
  88. package/dist/popover-CoZxokw_.js +2 -0
  89. package/dist/popover-CoZxokw_.js.map +1 -0
  90. package/dist/popover.js +1 -2
  91. package/dist/{primitive-tyw4V7Vf.d.ts → primitive-Ed9cel2r.d.ts} +1 -1
  92. package/dist/radio-group.d.ts +1 -1
  93. package/dist/radio-group.js +1 -1
  94. package/dist/{resolve-pre-rendered-props-Bqg41IkV.js → resolve-pre-rendered-props-BfWe69-w.js} +1 -1
  95. package/dist/{resolve-pre-rendered-props-Bqg41IkV.js.map → resolve-pre-rendered-props-BfWe69-w.js.map} +1 -1
  96. package/dist/{resolve-pre-rendered-props-CdqAcY5m.d.ts → resolve-pre-rendered-props-DxvamgE6.d.ts} +2 -2
  97. package/dist/sandboxed-on-click.d.ts +1 -1
  98. package/dist/{select-DZutJxyr.d.ts → select-8ymlL8kC.d.ts} +3 -3
  99. package/dist/select-BBB_e15a.js +2 -0
  100. package/dist/select-BBB_e15a.js.map +1 -0
  101. package/dist/select.d.ts +1 -1
  102. package/dist/select.js +1 -1
  103. package/dist/{separator-DSOIrnhj.js → separator-awchG4LI.js} +1 -1
  104. package/dist/{separator-DSOIrnhj.js.map → separator-awchG4LI.js.map} +1 -1
  105. package/dist/separator.d.ts +1 -1
  106. package/dist/separator.js +1 -1
  107. package/dist/sheet.d.ts +2 -2
  108. package/dist/sheet.js +1 -1
  109. package/dist/sheet.js.map +1 -1
  110. package/dist/slot.d.ts +2 -22
  111. package/dist/{sort-DzCsa6Qj.js → sort-mXo37xN2.js} +2 -2
  112. package/dist/{sort-DzCsa6Qj.js.map → sort-mXo37xN2.js.map} +1 -1
  113. package/dist/split-button.d.ts +3 -3
  114. package/dist/split-button.js +1 -1
  115. package/dist/{svg-only-BtBvFy-N.d.ts → svg-only-CLbMy439.d.ts} +2 -2
  116. package/dist/switch.d.ts +2 -1
  117. package/dist/switch.js +1 -1
  118. package/dist/switch.js.map +1 -1
  119. package/dist/{table-BsNJBKiq.d.ts → table-BWxS7pXj.d.ts} +1 -1
  120. package/dist/{table-Cl4nlRMR.js → table-CHd39aT-.js} +1 -1
  121. package/dist/{table-Cl4nlRMR.js.map → table-CHd39aT-.js.map} +1 -1
  122. package/dist/table.d.ts +1 -1
  123. package/dist/table.js +1 -1
  124. package/dist/tabs.js +1 -1
  125. package/dist/text-area.d.ts +1 -1
  126. package/dist/text-area.js +1 -1
  127. package/dist/text-area.js.map +1 -1
  128. package/dist/theme.d.ts +2 -2
  129. package/dist/{themes-DIEYkvNl.d.ts → themes-f2W5S6xS.d.ts} +1 -1
  130. package/dist/toast.d.ts +3 -3
  131. package/dist/{traffic-policy-file-C6LHYrIU.js → traffic-policy-file-BwHHdhWJ.js} +1 -1
  132. package/dist/{traffic-policy-file-C6LHYrIU.js.map → traffic-policy-file-BwHHdhWJ.js.map} +1 -1
  133. package/dist/{types-DoV0R5Ja.d.ts → types-DnghL1WE.d.ts} +1 -1
  134. package/dist/types.d.ts +5 -5
  135. package/dist/use-copy-to-clipboard-CTgtLjUg.js +2 -0
  136. package/dist/{use-copy-to-clipboard-C7vsjJe-.js.map → use-copy-to-clipboard-CTgtLjUg.js.map} +1 -1
  137. package/dist/use-isomorphic-layout-effect-CNSD0lhi.js +2 -0
  138. package/dist/use-isomorphic-layout-effect-CNSD0lhi.js.map +1 -0
  139. package/dist/{use-prefers-reduced-motion-aXfsyo-k.js → use-prefers-reduced-motion-YUurmkwx.js} +1 -1
  140. package/dist/{use-prefers-reduced-motion-aXfsyo-k.js.map → use-prefers-reduced-motion-YUurmkwx.js.map} +1 -1
  141. package/dist/utils.d.ts +2 -2
  142. package/dist/utils.js +1 -1
  143. package/dist/validation-BYME8rWN.js +2 -0
  144. package/dist/validation-BYME8rWN.js.map +1 -0
  145. package/dist/validation-DF1z7YDr.d.ts +108 -0
  146. package/dist/{variant-props-DUmSIQK8.d.ts → variant-props-B4io4uA_.d.ts} +2 -2
  147. package/dist/{with-style-props-3iFrBR08.d.ts → with-style-props-CW8buMhK.d.ts} +1 -1
  148. package/package.json +15 -10
  149. package/dist/dialog-BHzl9eye.js.map +0 -1
  150. package/dist/index-C91lxoX9.d.ts +0 -146
  151. package/dist/label.js.map +0 -1
  152. package/dist/popover.js.map +0 -1
  153. package/dist/select-DOgdZO0Q.js +0 -2
  154. package/dist/select-DOgdZO0Q.js.map +0 -1
  155. package/dist/types-DG0WQLTL.d.ts +0 -78
  156. package/dist/use-copy-to-clipboard-C7vsjJe-.js +0 -2
package/dist/input.d.ts CHANGED
@@ -1,3 +1,192 @@
1
- import { a as WithInputType, i as WithAutoComplete, n as InputType, o as WithValidation, r as Validation, t as AutoComplete } from "./types-DG0WQLTL.js";
2
- import { a as InputCapture, i as Input, n as PasswordInput, o as InputCaptureProps, r as PasswordInputProps, s as InputProps, t as isInput } from "./index-C91lxoX9.js";
3
- export { AutoComplete, Input, InputCapture, InputCaptureProps, InputProps, InputType, PasswordInput, PasswordInputProps, Validation, WithAutoComplete, WithInputType, WithValidation, isInput };
1
+ import { o as WithValidation, r as Validation } from "./validation-DF1z7YDr.js";
2
+ import * as _$react from "react";
3
+ import { InputHTMLAttributes, PropsWithChildren } from "react";
4
+
5
+ //#region src/components/input/types.d.ts
6
+ /**
7
+ * (Not a Boolean attribute!) The autocomplete attribute takes as its value a space-separated string that describes what,
8
+ * if any, type of autocomplete functionality the input should provide. A typical implementation of autocomplete recalls
9
+ * previous values entered in the same input field, but more complex forms of autocomplete can exist. For instance, a
10
+ * browser could integrate with a device's contacts list to autocomplete email addresses in an email input field.
11
+ *
12
+ * The autocomplete attribute is valid on hidden, text, search, url, tel, email, date, month, week, time, datetime-local,
13
+ * number, range, color, and password. This attribute has no effect on input types that do not return numeric or text
14
+ * data, being valid for all input types except checkbox, radio, file, or any of the button types.
15
+ *
16
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values
17
+ */
18
+ type AutoComplete = "off" | "on" | "name" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "email" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "url" | "photo";
19
+ type WithAutoComplete = {
20
+ /**
21
+ * (Not a Boolean attribute!) The autocomplete attribute takes as its value a space-separated string that describes what,
22
+ * if any, type of autocomplete functionality the input should provide. A typical implementation of autocomplete recalls
23
+ * previous values entered in the same input field, but more complex forms of autocomplete can exist. For instance, a
24
+ * browser could integrate with a device's contacts list to autocomplete email addresses in an email input field.
25
+ *
26
+ * The autocomplete attribute is valid on hidden, text, search, url, tel, email, date, month, week, time, datetime-local,
27
+ * number, range, color, and password. This attribute has no effect on input types that do not return numeric or text
28
+ * data, being valid for all input types except checkbox, radio, file, or any of the button types.
29
+ *
30
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values
31
+ */
32
+ autoComplete?: AutoComplete;
33
+ };
34
+ /**
35
+ * A string specifying the type of control to render. For example, to create a checkbox, a value of `"checkbox"` is used.
36
+ * If omitted (or an unknown value is specified), the input type `"text"` is used, creating a plaintext input field.
37
+ *
38
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
39
+ */
40
+ type InputType = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
41
+ type WithInputType = {
42
+ /**
43
+ * A string specifying the type of control to render. For example, to create a checkbox, a value of `"checkbox"` is used.
44
+ * If omitted (or an unknown value is specified), the input type `"text"` is used, creating a plaintext input field.
45
+ *
46
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
47
+ */
48
+ type?: InputType;
49
+ };
50
+ //#endregion
51
+ //#region src/components/input/input.d.ts
52
+ type BaseProps = WithAutoComplete & WithInputType & WithValidation;
53
+ /**
54
+ * The props for the `Input` component.
55
+ */
56
+ type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & BaseProps & PropsWithChildren;
57
+ /**
58
+ * Used to create interactive controls for web-based forms in order to accept data from the user.
59
+ * A versatile input element that supports various types, validation states, and can be composed with other elements.
60
+ *
61
+ * @see https://mantle.ngrok.com/components/input
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * <Input
66
+ * type="email"
67
+ * placeholder="Enter your email"
68
+ * validation="success"
69
+ * />
70
+ * ```
71
+ */
72
+ declare const Input: _$react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & {
73
+ children?: _$react.ReactNode | undefined;
74
+ } & _$react.RefAttributes<HTMLInputElement>>;
75
+ type InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & BaseProps;
76
+ /**
77
+ * The actual <input /> element that captures user input.
78
+ * Used internally by Input component or when you need direct control over the input element.
79
+ *
80
+ * @see https://mantle.ngrok.com/components/input
81
+ *
82
+ * @example
83
+ * ```tsx
84
+ * <Input>
85
+ * <InputCapture />
86
+ * <Icon svg={<SearchIcon />} />
87
+ * </Input>
88
+ * ```
89
+ */
90
+ declare const InputCapture: _$react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & _$react.RefAttributes<HTMLInputElement>>;
91
+ //#endregion
92
+ //#region src/components/input/password-input.d.ts
93
+ type PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
94
+ /**
95
+ * Callback for when the visibility of the password value changes.
96
+ */
97
+ onValueVisibilityChange?: (visible: boolean) => void;
98
+ /**
99
+ * Show/hide the password value as a controlled state.
100
+ * @default false
101
+ */
102
+ showValue?: boolean;
103
+ };
104
+ /**
105
+ * An input optimized for password and other sensitive-value entry. Renders a
106
+ * native `<input type="password">` with a built-in trailing button that
107
+ * toggles between hidden (`••••`) and revealed (`text`) display.
108
+ *
109
+ * **When to use**
110
+ * - Password fields on login, signup, and reset flows.
111
+ * - One-time tokens, recovery codes, or secrets the user needs to type
112
+ * accurately and may want to verify visually before submitting.
113
+ *
114
+ * **When not to use**
115
+ * - For values that are never sensitive — use a plain {@link https://mantle.ngrok.com/components/input Input}.
116
+ * - For controls where the toggle would be confusing (e.g. masked input
117
+ * formatting like phone numbers).
118
+ *
119
+ * **Visibility state.** The toggle is uncontrolled by default. Pass
120
+ * `showValue` to control the visibility from the outside (useful when one
121
+ * UI control toggles multiple password fields), and `onValueVisibilityChange`
122
+ * to be notified when the user toggles via the built-in button.
123
+ *
124
+ * **Accessibility.** Always pair with a {@link https://mantle.ngrok.com/components/label Label}.
125
+ * The toggle button has its own accessible name announcing the current
126
+ * state. The input keeps `autocomplete="current-password"` /
127
+ * `"new-password"` semantics — set `autoComplete` explicitly per flow.
128
+ *
129
+ * **Browser password managers.** When revealed, the input switches to
130
+ * `type="text"` — some password managers may pause autofill in this state,
131
+ * which is the intended security tradeoff.
132
+ *
133
+ * @see https://mantle.ngrok.com/components/password-input
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * import { PasswordInput } from "@ngrok/mantle/input";
138
+ * import { Label } from "@ngrok/mantle/label";
139
+ * import { useState } from "react";
140
+ *
141
+ * // Basic — uncontrolled visibility.
142
+ * <Label className="grid gap-1">
143
+ * <span>Password</span>
144
+ * <PasswordInput name="password" autoComplete="current-password" />
145
+ * </Label>
146
+ *
147
+ * // Validation state.
148
+ * <PasswordInput validation="error" />
149
+ *
150
+ * // Controlled visibility — one toggle reveals multiple fields.
151
+ * function PasswordPair() {
152
+ * const [show, setShow] = useState(false);
153
+ * return (
154
+ * <>
155
+ * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />
156
+ * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />
157
+ * </>
158
+ * );
159
+ * }
160
+ * ```
161
+ */
162
+ declare const PasswordInput: _$react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
163
+ /**
164
+ * Callback for when the visibility of the password value changes.
165
+ */
166
+ onValueVisibilityChange?: (visible: boolean) => void;
167
+ /**
168
+ * Show/hide the password value as a controlled state.
169
+ * @default false
170
+ */
171
+ showValue?: boolean;
172
+ } & _$react.RefAttributes<HTMLInputElement>>;
173
+ //#endregion
174
+ //#region src/components/input/is-input.d.ts
175
+ /**
176
+ * Type guard for an HTMLInputElement.
177
+ *
178
+ * @example
179
+ * ```tsx
180
+ * function handleElement(element: HTMLElement) {
181
+ * if (isInput(element)) {
182
+ * // TypeScript now knows element is HTMLInputElement
183
+ * element.value = "new value";
184
+ * element.focus();
185
+ * }
186
+ * }
187
+ * ```
188
+ */
189
+ declare function isInput(value: unknown): value is HTMLInputElement;
190
+ //#endregion
191
+ export { type AutoComplete, Input, InputCapture, type InputCaptureProps, type InputProps, type InputType, PasswordInput, type PasswordInputProps, type Validation, type WithAutoComplete, type WithInputType, type WithValidation, isInput };
192
+ //# sourceMappingURL=input.d.ts.map
package/dist/input.js CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./icon-bWc5yC3-.js";import{t as n}from"./compose-refs-DZ3cPi47.js";import{t as r}from"./use-prefers-reduced-motion-aXfsyo-k.js";import{t as i}from"./is-input-CUEWaxtA.js";import{createContext as a,forwardRef as o,useContext as s,useEffect as c,useRef as l,useState as u}from"react";import d from"clsx";import{jsx as f,jsxs as p}from"react/jsx-runtime";import{CheckCircleIcon as m}from"@phosphor-icons/react/CheckCircle";import{WarningIcon as h}from"@phosphor-icons/react/Warning";import{WarningDiamondIcon as g}from"@phosphor-icons/react/WarningDiamond";import{EyeIcon as _}from"@phosphor-icons/react/Eye";import{EyeClosedIcon as v}from"@phosphor-icons/react/EyeClosed";import{flushSync as y}from"react-dom";const b=o(({children:e,className:t,...n},r)=>{let i=!!e,a=l(null);return i?f(C,{className:t,forwardedRef:r,innerRef:a,...n,children:e}):f(C,{...n,className:t,forwardedRef:r,innerRef:a,children:f(x,{...n})})});b.displayName=`Input`;const x=o(({"aria-invalid":t,className:r,validation:i,...a},o)=>{let{"aria-invalid":c,forwardedRef:l,innerRef:u,validation:d,...p}=s(S),m=d??i,h=(typeof m==`function`?m():m)||void 0,g=c??t??m===`error`,_={...p,...a,type:a.type??p.type??`text`};return f(`input`,{"aria-invalid":g,"data-slot":`input-capture`,"data-validation":h,className:e(`placeholder:text-placeholder min-w-0 flex-1 bg-transparent text-left autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] focus:outline-hidden`,r),ref:n(o,l,u),..._})});x.displayName=`InputCapture`;const S=a({validation:void 0,innerRef:{current:null}}),C=({"aria-invalid":t,"aria-disabled":n,"data-slot":r=`input`,children:i,className:a,disabled:o,forwardedRef:s,innerRef:c,style:l,type:u,validation:d,...m})=>{let h=t!=null&&t!==`false`?`error`:typeof d==`function`?d():d;return f(S.Provider,{value:{"aria-invalid":t,"aria-disabled":n,disabled:o,type:u,validation:h,...m,forwardedRef:s,innerRef:c},children:p(`div`,{role:`none`,"data-slot":r,"data-disabled":(o??n)||void 0,"data-validation":h||void 0,className:e(`pointer-coarse:text-base h-9 text-sm`,`bg-form relative flex w-full items-center gap-1.5 rounded-md border px-3 py-2 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-within:outline-hidden focus-within:ring-4`,`data-disabled:opacity-50`,`has-[input:not(:first-child)]:ps-2.5 has-[input:not(:last-child)]:pe-2.5 [&>:not(input)]:shrink-0 [&_svg]:size-5`,`border-form text-strong focus-within:border-accent-600 focus-within:ring-focus-accent`,`data-validation-success:border-success-600 focus-within:data-validation-success:border-success-600 focus-within:data-validation-success:ring-focus-success`,`data-validation-warning:border-warning-600 focus-within:data-validation-warning:border-warning-600 focus-within:data-validation-warning:ring-focus-warning`,`data-validation-error:border-danger-600 focus-within:data-validation-error:border-danger-600 focus-within:data-validation-error:ring-focus-danger`,`autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] has-autofill:bg-blue-50 has-autofill:[-webkit-text-fill-color:var(--text-color-strong)]`,a),onMouseDown:e=>{e.target!==c?.current&&e.preventDefault()},onClick:()=>{c?.current?.focus()},onKeyDown:()=>{c?.current!==document.activeElement&&c?.current?.focus()},style:l,children:[i,f(w,{name:m.name,validation:h})]})})};C.displayName=`InputContainer`;const w=({name:e,validation:n})=>{switch(n){case`error`:return p(`div`,{className:`text-danger-600 order-last select-none`,children:[f(`span`,{className:`sr-only`,children:d(`The value entered for the`,e,`input has failed validation.`)}),f(t,{svg:f(h,{"aria-hidden":!0,weight:`fill`})})]});case`success`:return f(`div`,{className:`text-success-600 order-last select-none`,children:f(t,{svg:f(m,{weight:`fill`})})});case`warning`:return f(`div`,{className:`text-warning-600 order-last select-none`,children:f(t,{svg:f(g,{weight:`fill`})})});default:return null}};w.displayName=`ValidationFeedback`;const T=o(({onValueVisibilityChange:e,showValue:n=!1,...i},a)=>{let[o,s]=u(n),d=o?`text`:`password`,m=o?_:v,h=l(null),g=l(null);return c(()=>{s(n)},[n]),p(b,{"data-slot":`password-input`,type:d,ref:a,...i,children:[f(x,{}),p(`button`,{type:`button`,tabIndex:-1,className:`text-body hover:text-strong ml-1 cursor-pointer bg-inherit p-0`,onClick:()=>{g.current&&=(g.current.cancel(),null);let t=!o;y(()=>{s(t)}),e?.(t);let n=h.current;n&&!r()&&(g.current=n.animate([{transform:`scaleY(0)`},{transform:`scaleY(1)`}],{duration:200,easing:`ease-out`}),g.current.onfinish=()=>{g.current=null})},children:[p(`span`,{className:`sr-only`,children:[`Turn password visibility `,o?`off`:`on`]}),f(t,{ref:h,svg:f(m,{"aria-hidden":!0})})]})]})});T.displayName=`PasswordInput`;export{b as Input,x as InputCapture,T as PasswordInput,i as isInput};
1
+ import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./icon-bWc5yC3-.js";import{t as n}from"./compose-refs-DZ3cPi47.js";import{a as r,r as i}from"./validation-BYME8rWN.js";import{t as a}from"./use-prefers-reduced-motion-YUurmkwx.js";import{t as o}from"./is-input-CEEoHxXN.js";import{createContext as s,forwardRef as c,useContext as l,useEffect as u,useRef as d,useState as f}from"react";import p from"clsx";import{jsx as m,jsxs as h}from"react/jsx-runtime";import{CheckCircleIcon as g}from"@phosphor-icons/react/CheckCircle";import{WarningIcon as _}from"@phosphor-icons/react/Warning";import{WarningDiamondIcon as v}from"@phosphor-icons/react/WarningDiamond";import{EyeIcon as y}from"@phosphor-icons/react/Eye";import{EyeClosedIcon as b}from"@phosphor-icons/react/EyeClosed";import{flushSync as x}from"react-dom";const S=c(({children:e,className:t,...n},r)=>{let i=!!e,a=d(null);return i?m(T,{className:t,forwardedRef:r,innerRef:a,...n,children:e}):m(T,{...n,className:t,forwardedRef:r,innerRef:a,children:m(C,{...n})})});S.displayName=`Input`;const C=c(({"aria-invalid":t,className:a,validation:o,...s},c)=>{let{"aria-invalid":u,forwardedRef:d,innerRef:f,validation:p,...h}=l(w),g=r(),{ariaInvalid:_,validation:v}=i({"aria-invalid":u??t,validation:p??o??g}),y={...h,...s,type:s.type??h.type??`text`};return m(`input`,{"aria-invalid":_,"data-slot":`input-capture`,"data-validation":v,className:e(`placeholder:text-placeholder min-w-0 flex-1 bg-transparent text-left autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] focus:outline-hidden`,a),ref:n(c,d,f),...y})});C.displayName=`InputCapture`;const w=s({validation:void 0,innerRef:{current:null}}),T=({"aria-invalid":t,"aria-disabled":n,"data-slot":a=`input`,children:o,className:s,disabled:c,forwardedRef:l,innerRef:u,style:d,type:f,validation:p,...g})=>{let _=r(),{validation:v}=i({"aria-invalid":t,validation:p??_});return m(w.Provider,{value:{"aria-invalid":t,"aria-disabled":n,disabled:c,type:f,validation:p,...g,forwardedRef:l,innerRef:u},children:h(`div`,{role:`none`,"data-slot":a,"data-disabled":(c??n)||void 0,"data-validation":v||void 0,className:e(`pointer-coarse:text-base h-9 text-sm`,`bg-form relative flex w-full items-center gap-1.5 rounded-md border px-3 py-2 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-within:outline-hidden focus-within:ring-4`,`data-disabled:opacity-50`,`has-[input:not(:first-child)]:ps-2.5 has-[input:not(:last-child)]:pe-2.5 [&>:not(input)]:shrink-0 [&_svg]:size-5`,`border-form text-strong focus-within:border-accent-600 focus-within:ring-focus-accent`,`data-validation-success:border-success-600 focus-within:data-validation-success:border-success-600 focus-within:data-validation-success:ring-focus-success`,`data-validation-warning:border-warning-600 focus-within:data-validation-warning:border-warning-600 focus-within:data-validation-warning:ring-focus-warning`,`data-validation-error:border-danger-600 focus-within:data-validation-error:border-danger-600 focus-within:data-validation-error:ring-focus-danger`,`autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] has-autofill:bg-blue-50 has-autofill:[-webkit-text-fill-color:var(--text-color-strong)]`,s),onMouseDown:e=>{e.target!==u?.current&&e.preventDefault()},onClick:()=>{u?.current?.focus()},onKeyDown:()=>{u?.current!==document.activeElement&&u?.current?.focus()},style:d,children:[o,m(E,{name:g.name,validation:v})]})})};T.displayName=`InputContainer`;const E=({name:e,validation:n})=>{switch(n){case`error`:return h(`div`,{className:`text-danger-600 order-last select-none`,children:[m(`span`,{className:`sr-only`,children:p(`The value entered for the`,e,`input has failed validation.`)}),m(t,{svg:m(_,{"aria-hidden":!0,weight:`fill`})})]});case`success`:return m(`div`,{className:`text-success-600 order-last select-none`,children:m(t,{svg:m(g,{weight:`fill`})})});case`warning`:return m(`div`,{className:`text-warning-600 order-last select-none`,children:m(t,{svg:m(v,{weight:`fill`})})});default:return null}};E.displayName=`ValidationFeedback`;const D=c(({onValueVisibilityChange:e,showValue:n=!1,...r},i)=>{let[o,s]=f(n),c=o?`text`:`password`,l=o?y:b,p=d(null),g=d(null);return u(()=>{s(n)},[n]),h(S,{"data-slot":`password-input`,type:c,ref:i,...r,children:[m(C,{}),h(`button`,{type:`button`,tabIndex:-1,className:`text-body hover:text-strong ml-1 cursor-pointer bg-inherit p-0`,onClick:()=>{g.current&&=(g.current.cancel(),null);let t=!o;x(()=>{s(t)}),e?.(t);let n=p.current;n&&!a()&&(g.current=n.animate([{transform:`scaleY(0)`},{transform:`scaleY(1)`}],{duration:200,easing:`ease-out`}),g.current.onfinish=()=>{g.current=null})},children:[h(`span`,{className:`sr-only`,children:[`Turn password visibility `,o?`off`:`on`]}),m(t,{ref:p,svg:m(l,{"aria-hidden":!0})})]})]})});D.displayName=`PasswordInput`;export{S as Input,C as InputCapture,D as PasswordInput,o as isInput};
2
2
  //# sourceMappingURL=input.js.map
package/dist/input.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"input.js","names":["clsx"],"sources":["../src/components/input/input.tsx","../src/components/input/password-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircleIcon } from \"@phosphor-icons/react/CheckCircle\";\nimport { WarningIcon } from \"@phosphor-icons/react/Warning\";\nimport { WarningDiamondIcon } from \"@phosphor-icons/react/WarningDiamond\";\nimport clsx from \"clsx\";\nimport type {\n\tComponentRef,\n\tForwardedRef,\n\tInputHTMLAttributes,\n\tMutableRefObject,\n\tPropsWithChildren,\n} from \"react\";\nimport { createContext, forwardRef, useContext, useRef } from \"react\";\nimport { composeRefs } from \"../../utils/compose-refs/compose-refs.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Icon } from \"../icon/icon.js\";\nimport type { Validation, WithAutoComplete, WithInputType, WithValidation } from \"./types.js\";\n\ntype BaseProps = WithAutoComplete & WithInputType & WithValidation;\n\n/**\n * The props for the `Input` component.\n */\ntype InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps &\n\tPropsWithChildren;\n\n/**\n * Used to create interactive controls for web-based forms in order to accept data from the user.\n * A versatile input element that supports various types, validation states, and can be composed with other elements.\n *\n * @see https://mantle.ngrok.com/components/input\n *\n * @example\n * ```tsx\n * <Input\n * type=\"email\"\n * placeholder=\"Enter your email\"\n * validation=\"success\"\n * />\n * ```\n */\nconst Input = forwardRef<HTMLInputElement, InputProps>(\n\t({ children, className, ...props }, forwardedRef) => {\n\t\tconst hasChildren = Boolean(children);\n\t\tconst innerRef = useRef<ComponentRef<\"input\">>(null);\n\n\t\tif (hasChildren) {\n\t\t\treturn (\n\t\t\t\t<InputContainer\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tforwardedRef={forwardedRef}\n\t\t\t\t\tinnerRef={innerRef}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</InputContainer>\n\t\t\t);\n\t\t}\n\n\t\treturn (\n\t\t\t<InputContainer\n\t\t\t\t{...props}\n\t\t\t\tclassName={className}\n\t\t\t\tforwardedRef={forwardedRef}\n\t\t\t\tinnerRef={innerRef}\n\t\t\t>\n\t\t\t\t<InputCapture {...props} />\n\t\t\t</InputContainer>\n\t\t);\n\t},\n);\nInput.displayName = \"Input\";\n\ntype InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps;\n\n/**\n * The actual <input /> element that captures user input.\n * Used internally by Input component or when you need direct control over the input element.\n *\n * @see https://mantle.ngrok.com/components/input\n *\n * @example\n * ```tsx\n * <Input>\n * <InputCapture />\n * <Icon svg={<SearchIcon />} />\n * </Input>\n * ```\n */\nconst InputCapture = forwardRef<HTMLInputElement, InputCaptureProps>(\n\t({ \"aria-invalid\": _ariaInvalid, className, validation: _validation, ...restProps }, ref) => {\n\t\tconst {\n\t\t\t\"aria-invalid\": ctxAriaInvalid,\n\t\t\tforwardedRef: ctxForwardedRef,\n\t\t\tinnerRef: ctxInnerRef,\n\t\t\tvalidation: ctxValidation,\n\t\t\t...ctx\n\t\t} = useContext(InputContext);\n\n\t\tconst validation = ctxValidation ?? _validation;\n\t\tconst validationValue =\n\t\t\t(typeof validation === \"function\" ? validation() : validation) || undefined;\n\t\tconst ariaInvalid = ctxAriaInvalid ?? _ariaInvalid ?? validation === \"error\";\n\t\tconst props = {\n\t\t\t...ctx,\n\t\t\t...restProps,\n\t\t\ttype: restProps.type ?? ctx.type ?? \"text\",\n\t\t};\n\n\t\treturn (\n\t\t\t<input\n\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\tdata-slot=\"input-capture\"\n\t\t\t\tdata-validation={validationValue}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"placeholder:text-placeholder min-w-0 flex-1 bg-transparent text-left autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] focus:outline-hidden\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={composeRefs(ref, ctxForwardedRef, ctxInnerRef)}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nInputCapture.displayName = \"InputCapture\";\n\ntype InputContextType = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps & {\n\t\t/**\n\t\t * inner ref for the input element, controlled by `Input`\n\t\t */\n\t\tinnerRef: MutableRefObject<HTMLInputElement | null>;\n\t\t/**\n\t\t * forwarded ref to the input element, forwarded from `Input` to `InputCapture`\n\t\t */\n\t\tforwardedRef?: ForwardedRef<HTMLInputElement>;\n\t};\n\nconst InputContext = createContext<InputContextType>({\n\tvalidation: undefined,\n\tinnerRef: { current: null },\n});\n\ntype InputContainerProps = InputHTMLAttributes<HTMLInputElement> &\n\tBaseProps & {\n\t\t/**\n\t\t * @private inner ref for the input element, controlled by `Input`\n\t\t */\n\t\tinnerRef: MutableRefObject<HTMLInputElement | null>;\n\t\t/**\n\t\t * @private ref to the input element, forwarded from `Input` to `InputCapture`\n\t\t */\n\t\tforwardedRef: ForwardedRef<HTMLInputElement>;\n\t};\n\n/**\n * The container for the input element.\n */\nconst InputContainer = ({\n\t\"aria-invalid\": _ariaInvalid,\n\t\"aria-disabled\": _ariaDisabled,\n\t\"data-slot\": dataSlot = \"input\",\n\tchildren,\n\tclassName,\n\tdisabled,\n\tforwardedRef,\n\tinnerRef,\n\tstyle,\n\ttype,\n\tvalidation: _validation,\n\t...props\n}: InputContainerProps & { \"data-slot\"?: string }) => {\n\tconst isInvalid = _ariaInvalid != null && _ariaInvalid !== \"false\";\n\tconst validation = isInvalid\n\t\t? \"error\"\n\t\t: typeof _validation === \"function\"\n\t\t\t? _validation()\n\t\t\t: _validation;\n\treturn (\n\t\t<InputContext.Provider\n\t\t\tvalue={{\n\t\t\t\t\"aria-invalid\": _ariaInvalid,\n\t\t\t\t\"aria-disabled\": _ariaDisabled,\n\t\t\t\tdisabled,\n\t\t\t\ttype,\n\t\t\t\tvalidation,\n\t\t\t\t...props,\n\t\t\t\tforwardedRef,\n\t\t\t\tinnerRef,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\trole=\"none\"\n\t\t\t\tdata-slot={dataSlot}\n\t\t\t\tdata-disabled={(disabled ?? _ariaDisabled) || undefined}\n\t\t\t\tdata-validation={validation || undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"pointer-coarse:text-base h-9 text-sm\",\n\t\t\t\t\t\"bg-form relative flex w-full items-center gap-1.5 rounded-md border px-3 py-2 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-within:outline-hidden focus-within:ring-4\",\n\t\t\t\t\t\"data-disabled:opacity-50\",\n\t\t\t\t\t\"has-[input:not(:first-child)]:ps-2.5 has-[input:not(:last-child)]:pe-2.5 [&>:not(input)]:shrink-0 [&_svg]:size-5\",\n\t\t\t\t\t\"border-form text-strong focus-within:border-accent-600 focus-within:ring-focus-accent\",\n\t\t\t\t\t\"data-validation-success:border-success-600 focus-within:data-validation-success:border-success-600 focus-within:data-validation-success:ring-focus-success\",\n\t\t\t\t\t\"data-validation-warning:border-warning-600 focus-within:data-validation-warning:border-warning-600 focus-within:data-validation-warning:ring-focus-warning\",\n\t\t\t\t\t\"data-validation-error:border-danger-600 focus-within:data-validation-error:border-danger-600 focus-within:data-validation-error:ring-focus-danger\",\n\t\t\t\t\t\"autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] has-autofill:bg-blue-50 has-autofill:[-webkit-text-fill-color:var(--text-color-strong)]\", // Autofill styling on the input itself and any children with autofill styling\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonMouseDown={(event) => {\n\t\t\t\t\t// Prevent mousedown on non-input children (icons, buttons, etc.) from\n\t\t\t\t\t// blurring the input, which would cause the focus ring to flicker.\n\t\t\t\t\tif (event.target !== innerRef?.current) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tonClick={() => {\n\t\t\t\t\tinnerRef?.current?.focus();\n\t\t\t\t}}\n\t\t\t\tonKeyDown={() => {\n\t\t\t\t\tif (innerRef?.current !== document.activeElement) {\n\t\t\t\t\t\tinnerRef?.current?.focus();\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tstyle={style}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t<ValidationFeedback name={props.name} validation={validation} />\n\t\t\t</div>\n\t\t</InputContext.Provider>\n\t);\n};\nInputContainer.displayName = \"InputContainer\";\n\nexport { Input, InputCapture };\nexport type { InputProps, InputCaptureProps };\n\nconst ValidationFeedback = ({\n\tname,\n\tvalidation,\n}: {\n\tname?: string;\n\tvalidation: Validation | undefined;\n}) => {\n\tswitch (validation) {\n\t\tcase \"error\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-danger-600 order-last select-none\">\n\t\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\t\t{clsx(\"The value entered for the\", name, \"input has failed validation.\")}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Icon svg={<WarningIcon aria-hidden weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tcase \"success\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-success-600 order-last select-none\">\n\t\t\t\t\t<Icon svg={<CheckCircleIcon weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tcase \"warning\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-warning-600 order-last select-none\">\n\t\t\t\t\t<Icon svg={<WarningDiamondIcon weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\nValidationFeedback.displayName = \"ValidationFeedback\";\n","\"use client\";\n\nimport { EyeIcon } from \"@phosphor-icons/react/Eye\";\nimport { EyeClosedIcon } from \"@phosphor-icons/react/EyeClosed\";\nimport { forwardRef, useEffect, useRef, useState } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { getPrefersReducedMotion } from \"../../hooks/use-prefers-reduced-motion.js\";\nimport { Icon } from \"../icon/icon.js\";\nimport { Input, InputCapture } from \"./input.js\";\nimport type { InputType, WithAutoComplete, WithValidation } from \"./types.js\";\n\ntype PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tWithValidation &\n\tWithAutoComplete & {\n\t\t/**\n\t\t * Callback for when the visibility of the password value changes.\n\t\t */\n\t\tonValueVisibilityChange?: (visible: boolean) => void;\n\t\t/**\n\t\t * Show/hide the password value as a controlled state.\n\t\t * @default false\n\t\t */\n\t\tshowValue?: boolean;\n\t};\n\ntype PasswordInputType = Extract<InputType, \"text\" | \"password\">;\n\n/**\n * An input optimized for password and other sensitive-value entry. Renders a\n * native `<input type=\"password\">` with a built-in trailing button that\n * toggles between hidden (`••••`) and revealed (`text`) display.\n *\n * **When to use**\n * - Password fields on login, signup, and reset flows.\n * - One-time tokens, recovery codes, or secrets the user needs to type\n * accurately and may want to verify visually before submitting.\n *\n * **When not to use**\n * - For values that are never sensitive — use a plain {@link https://mantle.ngrok.com/components/input Input}.\n * - For controls where the toggle would be confusing (e.g. masked input\n * formatting like phone numbers).\n *\n * **Visibility state.** The toggle is uncontrolled by default. Pass\n * `showValue` to control the visibility from the outside (useful when one\n * UI control toggles multiple password fields), and `onValueVisibilityChange`\n * to be notified when the user toggles via the built-in button.\n *\n * **Accessibility.** Always pair with a {@link https://mantle.ngrok.com/components/label Label}.\n * The toggle button has its own accessible name announcing the current\n * state. The input keeps `autocomplete=\"current-password\"` /\n * `\"new-password\"` semantics — set `autoComplete` explicitly per flow.\n *\n * **Browser password managers.** When revealed, the input switches to\n * `type=\"text\"` — some password managers may pause autofill in this state,\n * which is the intended security tradeoff.\n *\n * @see https://mantle.ngrok.com/components/password-input\n *\n * @example\n * ```tsx\n * import { PasswordInput } from \"@ngrok/mantle/input\";\n * import { Label } from \"@ngrok/mantle/label\";\n * import { useState } from \"react\";\n *\n * // Basic — uncontrolled visibility.\n * <Label className=\"grid gap-1\">\n * <span>Password</span>\n * <PasswordInput name=\"password\" autoComplete=\"current-password\" />\n * </Label>\n *\n * // Validation state.\n * <PasswordInput validation=\"error\" />\n *\n * // Controlled visibility — one toggle reveals multiple fields.\n * function PasswordPair() {\n * const [show, setShow] = useState(false);\n * return (\n * <>\n * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />\n * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />\n * </>\n * );\n * }\n * ```\n */\nconst PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(\n\t({ onValueVisibilityChange, showValue = false, ...props }, ref) => {\n\t\tconst [showPassword, setShowPassword] = useState<boolean>(showValue);\n\t\tconst type: PasswordInputType = showPassword ? \"text\" : \"password\";\n\t\tconst EyeCon = showPassword ? EyeIcon : EyeClosedIcon;\n\t\tconst iconRef = useRef<SVGSVGElement>(null);\n\t\tconst animationRef = useRef<Animation | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tsetShowPassword(showValue);\n\t\t}, [showValue]);\n\n\t\treturn (\n\t\t\t<Input data-slot=\"password-input\" type={type} ref={ref} {...props}>\n\t\t\t\t<InputCapture />\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\tclassName=\"text-body hover:text-strong ml-1 cursor-pointer bg-inherit p-0\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t// Cancel any in-flight animation so rapid clicks are never blocked\n\t\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Flush synchronously so React commits the new icon to the DOM before we animate\n\t\t\t\t\t\tconst nextShowPassword = !showPassword;\n\t\t\t\t\t\tflushSync(() => {\n\t\t\t\t\t\t\tsetShowPassword(nextShowPassword);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tonValueVisibilityChange?.(nextShowPassword);\n\n\t\t\t\t\t\tconst icon = iconRef.current;\n\t\t\t\t\t\tif (icon && !getPrefersReducedMotion()) {\n\t\t\t\t\t\t\tanimationRef.current = icon.animate(\n\t\t\t\t\t\t\t\t[{ transform: \"scaleY(0)\" }, { transform: \"scaleY(1)\" }],\n\t\t\t\t\t\t\t\t{ duration: 200, easing: \"ease-out\" },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tanimationRef.current.onfinish = () => {\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"sr-only\">Turn password visibility {showPassword ? \"off\" : \"on\"}</span>\n\t\t\t\t\t<Icon ref={iconRef} svg={<EyeCon aria-hidden />} />\n\t\t\t\t</button>\n\t\t\t</Input>\n\t\t);\n\t},\n);\nPasswordInput.displayName = \"PasswordInput\";\n\nexport { PasswordInput };\nexport type { PasswordInputProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"input.js","names":["clsx"],"sources":["../src/components/input/input.tsx","../src/components/input/password-input.tsx"],"sourcesContent":["\"use client\";\n\nimport { CheckCircleIcon } from \"@phosphor-icons/react/CheckCircle\";\nimport { WarningIcon } from \"@phosphor-icons/react/Warning\";\nimport { WarningDiamondIcon } from \"@phosphor-icons/react/WarningDiamond\";\nimport clsx from \"clsx\";\nimport type {\n\tComponentRef,\n\tForwardedRef,\n\tInputHTMLAttributes,\n\tMutableRefObject,\n\tPropsWithChildren,\n} from \"react\";\nimport { createContext, forwardRef, useContext, useRef } from \"react\";\nimport { composeRefs } from \"../../utils/compose-refs/compose-refs.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { parseValidation, useFieldValidation } from \"../field/validation.js\";\nimport type { Validation, WithValidation } from \"../field/validation.js\";\nimport { Icon } from \"../icon/icon.js\";\nimport type { WithAutoComplete, WithInputType } from \"./types.js\";\n\ntype BaseProps = WithAutoComplete & WithInputType & WithValidation;\n\n/**\n * The props for the `Input` component.\n */\ntype InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps &\n\tPropsWithChildren;\n\n/**\n * Used to create interactive controls for web-based forms in order to accept data from the user.\n * A versatile input element that supports various types, validation states, and can be composed with other elements.\n *\n * @see https://mantle.ngrok.com/components/input\n *\n * @example\n * ```tsx\n * <Input\n * type=\"email\"\n * placeholder=\"Enter your email\"\n * validation=\"success\"\n * />\n * ```\n */\nconst Input = forwardRef<HTMLInputElement, InputProps>(\n\t({ children, className, ...props }, forwardedRef) => {\n\t\tconst hasChildren = Boolean(children);\n\t\tconst innerRef = useRef<ComponentRef<\"input\">>(null);\n\n\t\tif (hasChildren) {\n\t\t\treturn (\n\t\t\t\t<InputContainer\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tforwardedRef={forwardedRef}\n\t\t\t\t\tinnerRef={innerRef}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</InputContainer>\n\t\t\t);\n\t\t}\n\n\t\treturn (\n\t\t\t<InputContainer\n\t\t\t\t{...props}\n\t\t\t\tclassName={className}\n\t\t\t\tforwardedRef={forwardedRef}\n\t\t\t\tinnerRef={innerRef}\n\t\t\t>\n\t\t\t\t<InputCapture {...props} />\n\t\t\t</InputContainer>\n\t\t);\n\t},\n);\nInput.displayName = \"Input\";\n\ntype InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps;\n\n/**\n * The actual <input /> element that captures user input.\n * Used internally by Input component or when you need direct control over the input element.\n *\n * @see https://mantle.ngrok.com/components/input\n *\n * @example\n * ```tsx\n * <Input>\n * <InputCapture />\n * <Icon svg={<SearchIcon />} />\n * </Input>\n * ```\n */\nconst InputCapture = forwardRef<HTMLInputElement, InputCaptureProps>(\n\t({ \"aria-invalid\": _ariaInvalid, className, validation: _validation, ...restProps }, ref) => {\n\t\tconst {\n\t\t\t\"aria-invalid\": ctxAriaInvalid,\n\t\t\tforwardedRef: ctxForwardedRef,\n\t\t\tinnerRef: ctxInnerRef,\n\t\t\tvalidation: ctxValidation,\n\t\t\t...ctx\n\t\t} = useContext(InputContext);\n\t\tconst fieldValidation = useFieldValidation();\n\n\t\tconst { ariaInvalid, validation } = parseValidation({\n\t\t\t\"aria-invalid\": ctxAriaInvalid ?? _ariaInvalid,\n\t\t\tvalidation: ctxValidation ?? _validation ?? fieldValidation,\n\t\t});\n\t\tconst props = {\n\t\t\t...ctx,\n\t\t\t...restProps,\n\t\t\ttype: restProps.type ?? ctx.type ?? \"text\",\n\t\t};\n\n\t\treturn (\n\t\t\t<input\n\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\tdata-slot=\"input-capture\"\n\t\t\t\tdata-validation={validation}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"placeholder:text-placeholder min-w-0 flex-1 bg-transparent text-left autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] focus:outline-hidden\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={composeRefs(ref, ctxForwardedRef, ctxInnerRef)}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nInputCapture.displayName = \"InputCapture\";\n\ntype InputContextType = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tBaseProps & {\n\t\t/**\n\t\t * inner ref for the input element, controlled by `Input`\n\t\t */\n\t\tinnerRef: MutableRefObject<HTMLInputElement | null>;\n\t\t/**\n\t\t * forwarded ref to the input element, forwarded from `Input` to `InputCapture`\n\t\t */\n\t\tforwardedRef?: ForwardedRef<HTMLInputElement>;\n\t};\n\nconst InputContext = createContext<InputContextType>({\n\tvalidation: undefined,\n\tinnerRef: { current: null },\n});\n\ntype InputContainerProps = InputHTMLAttributes<HTMLInputElement> &\n\tBaseProps & {\n\t\t/**\n\t\t * @private inner ref for the input element, controlled by `Input`\n\t\t */\n\t\tinnerRef: MutableRefObject<HTMLInputElement | null>;\n\t\t/**\n\t\t * @private ref to the input element, forwarded from `Input` to `InputCapture`\n\t\t */\n\t\tforwardedRef: ForwardedRef<HTMLInputElement>;\n\t};\n\n/**\n * The container for the input element.\n */\nconst InputContainer = ({\n\t\"aria-invalid\": _ariaInvalid,\n\t\"aria-disabled\": _ariaDisabled,\n\t\"data-slot\": dataSlot = \"input\",\n\tchildren,\n\tclassName,\n\tdisabled,\n\tforwardedRef,\n\tinnerRef,\n\tstyle,\n\ttype,\n\tvalidation: _validation,\n\t...props\n}: InputContainerProps & { \"data-slot\"?: string }) => {\n\tconst fieldValidation = useFieldValidation();\n\tconst { validation } = parseValidation({\n\t\t\"aria-invalid\": _ariaInvalid,\n\t\tvalidation: _validation ?? fieldValidation,\n\t});\n\treturn (\n\t\t<InputContext.Provider\n\t\t\tvalue={{\n\t\t\t\t\"aria-invalid\": _ariaInvalid,\n\t\t\t\t\"aria-disabled\": _ariaDisabled,\n\t\t\t\tdisabled,\n\t\t\t\ttype,\n\t\t\t\tvalidation: _validation,\n\t\t\t\t...props,\n\t\t\t\tforwardedRef,\n\t\t\t\tinnerRef,\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\trole=\"none\"\n\t\t\t\tdata-slot={dataSlot}\n\t\t\t\tdata-disabled={(disabled ?? _ariaDisabled) || undefined}\n\t\t\t\tdata-validation={validation || undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"pointer-coarse:text-base h-9 text-sm\",\n\t\t\t\t\t\"bg-form relative flex w-full items-center gap-1.5 rounded-md border px-3 py-2 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-within:outline-hidden focus-within:ring-4\",\n\t\t\t\t\t\"data-disabled:opacity-50\",\n\t\t\t\t\t\"has-[input:not(:first-child)]:ps-2.5 has-[input:not(:last-child)]:pe-2.5 [&>:not(input)]:shrink-0 [&_svg]:size-5\",\n\t\t\t\t\t\"border-form text-strong focus-within:border-accent-600 focus-within:ring-focus-accent\",\n\t\t\t\t\t\"data-validation-success:border-success-600 focus-within:data-validation-success:border-success-600 focus-within:data-validation-success:ring-focus-success\",\n\t\t\t\t\t\"data-validation-warning:border-warning-600 focus-within:data-validation-warning:border-warning-600 focus-within:data-validation-warning:ring-focus-warning\",\n\t\t\t\t\t\"data-validation-error:border-danger-600 focus-within:data-validation-error:border-danger-600 focus-within:data-validation-error:ring-focus-danger\",\n\t\t\t\t\t\"autofill:shadow-[inset_0_0_0px_1000px_var(--color-blue-50)] has-autofill:bg-blue-50 has-autofill:[-webkit-text-fill-color:var(--text-color-strong)]\", // Autofill styling on the input itself and any children with autofill styling\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonMouseDown={(event) => {\n\t\t\t\t\t// Prevent mousedown on non-input children (icons, buttons, etc.) from\n\t\t\t\t\t// blurring the input, which would cause the focus ring to flicker.\n\t\t\t\t\tif (event.target !== innerRef?.current) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tonClick={() => {\n\t\t\t\t\tinnerRef?.current?.focus();\n\t\t\t\t}}\n\t\t\t\tonKeyDown={() => {\n\t\t\t\t\tif (innerRef?.current !== document.activeElement) {\n\t\t\t\t\t\tinnerRef?.current?.focus();\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tstyle={style}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t<ValidationFeedback name={props.name} validation={validation} />\n\t\t\t</div>\n\t\t</InputContext.Provider>\n\t);\n};\nInputContainer.displayName = \"InputContainer\";\n\nexport { Input, InputCapture };\nexport type { InputProps, InputCaptureProps };\n\nconst ValidationFeedback = ({\n\tname,\n\tvalidation,\n}: {\n\tname?: string;\n\tvalidation: Validation | undefined;\n}) => {\n\tswitch (validation) {\n\t\tcase \"error\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-danger-600 order-last select-none\">\n\t\t\t\t\t<span className=\"sr-only\">\n\t\t\t\t\t\t{clsx(\"The value entered for the\", name, \"input has failed validation.\")}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Icon svg={<WarningIcon aria-hidden weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tcase \"success\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-success-600 order-last select-none\">\n\t\t\t\t\t<Icon svg={<CheckCircleIcon weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tcase \"warning\":\n\t\t\treturn (\n\t\t\t\t<div className=\"text-warning-600 order-last select-none\">\n\t\t\t\t\t<Icon svg={<WarningDiamondIcon weight=\"fill\" />} />\n\t\t\t\t</div>\n\t\t\t);\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\nValidationFeedback.displayName = \"ValidationFeedback\";\n","\"use client\";\n\nimport { EyeIcon } from \"@phosphor-icons/react/Eye\";\nimport { EyeClosedIcon } from \"@phosphor-icons/react/EyeClosed\";\nimport { forwardRef, useEffect, useRef, useState } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { getPrefersReducedMotion } from \"../../hooks/use-prefers-reduced-motion.js\";\nimport type { WithValidation } from \"../field/validation.js\";\nimport { Icon } from \"../icon/icon.js\";\nimport { Input, InputCapture } from \"./input.js\";\nimport type { InputType, WithAutoComplete } from \"./types.js\";\n\ntype PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"autoComplete\" | \"type\"> &\n\tWithValidation &\n\tWithAutoComplete & {\n\t\t/**\n\t\t * Callback for when the visibility of the password value changes.\n\t\t */\n\t\tonValueVisibilityChange?: (visible: boolean) => void;\n\t\t/**\n\t\t * Show/hide the password value as a controlled state.\n\t\t * @default false\n\t\t */\n\t\tshowValue?: boolean;\n\t};\n\ntype PasswordInputType = Extract<InputType, \"text\" | \"password\">;\n\n/**\n * An input optimized for password and other sensitive-value entry. Renders a\n * native `<input type=\"password\">` with a built-in trailing button that\n * toggles between hidden (`••••`) and revealed (`text`) display.\n *\n * **When to use**\n * - Password fields on login, signup, and reset flows.\n * - One-time tokens, recovery codes, or secrets the user needs to type\n * accurately and may want to verify visually before submitting.\n *\n * **When not to use**\n * - For values that are never sensitive — use a plain {@link https://mantle.ngrok.com/components/input Input}.\n * - For controls where the toggle would be confusing (e.g. masked input\n * formatting like phone numbers).\n *\n * **Visibility state.** The toggle is uncontrolled by default. Pass\n * `showValue` to control the visibility from the outside (useful when one\n * UI control toggles multiple password fields), and `onValueVisibilityChange`\n * to be notified when the user toggles via the built-in button.\n *\n * **Accessibility.** Always pair with a {@link https://mantle.ngrok.com/components/label Label}.\n * The toggle button has its own accessible name announcing the current\n * state. The input keeps `autocomplete=\"current-password\"` /\n * `\"new-password\"` semantics — set `autoComplete` explicitly per flow.\n *\n * **Browser password managers.** When revealed, the input switches to\n * `type=\"text\"` — some password managers may pause autofill in this state,\n * which is the intended security tradeoff.\n *\n * @see https://mantle.ngrok.com/components/password-input\n *\n * @example\n * ```tsx\n * import { PasswordInput } from \"@ngrok/mantle/input\";\n * import { Label } from \"@ngrok/mantle/label\";\n * import { useState } from \"react\";\n *\n * // Basic — uncontrolled visibility.\n * <Label className=\"grid gap-1\">\n * <span>Password</span>\n * <PasswordInput name=\"password\" autoComplete=\"current-password\" />\n * </Label>\n *\n * // Validation state.\n * <PasswordInput validation=\"error\" />\n *\n * // Controlled visibility — one toggle reveals multiple fields.\n * function PasswordPair() {\n * const [show, setShow] = useState(false);\n * return (\n * <>\n * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />\n * <PasswordInput showValue={show} onValueVisibilityChange={setShow} />\n * </>\n * );\n * }\n * ```\n */\nconst PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(\n\t({ onValueVisibilityChange, showValue = false, ...props }, ref) => {\n\t\tconst [showPassword, setShowPassword] = useState<boolean>(showValue);\n\t\tconst type: PasswordInputType = showPassword ? \"text\" : \"password\";\n\t\tconst EyeCon = showPassword ? EyeIcon : EyeClosedIcon;\n\t\tconst iconRef = useRef<SVGSVGElement>(null);\n\t\tconst animationRef = useRef<Animation | null>(null);\n\n\t\tuseEffect(() => {\n\t\t\tsetShowPassword(showValue);\n\t\t}, [showValue]);\n\n\t\treturn (\n\t\t\t<Input data-slot=\"password-input\" type={type} ref={ref} {...props}>\n\t\t\t\t<InputCapture />\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\tclassName=\"text-body hover:text-strong ml-1 cursor-pointer bg-inherit p-0\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t// Cancel any in-flight animation so rapid clicks are never blocked\n\t\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Flush synchronously so React commits the new icon to the DOM before we animate\n\t\t\t\t\t\tconst nextShowPassword = !showPassword;\n\t\t\t\t\t\tflushSync(() => {\n\t\t\t\t\t\t\tsetShowPassword(nextShowPassword);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tonValueVisibilityChange?.(nextShowPassword);\n\n\t\t\t\t\t\tconst icon = iconRef.current;\n\t\t\t\t\t\tif (icon && !getPrefersReducedMotion()) {\n\t\t\t\t\t\t\tanimationRef.current = icon.animate(\n\t\t\t\t\t\t\t\t[{ transform: \"scaleY(0)\" }, { transform: \"scaleY(1)\" }],\n\t\t\t\t\t\t\t\t{ duration: 200, easing: \"ease-out\" },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tanimationRef.current.onfinish = () => {\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"sr-only\">Turn password visibility {showPassword ? \"off\" : \"on\"}</span>\n\t\t\t\t\t<Icon ref={iconRef} svg={<EyeCon aria-hidden />} />\n\t\t\t\t</button>\n\t\t\t</Input>\n\t\t);\n\t},\n);\nPasswordInput.displayName = \"PasswordInput\";\n\nexport { PasswordInput };\nexport type { PasswordInputProps };\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
1
  function e(e){return e!=null&&e instanceof HTMLInputElement}export{e as t};
2
- //# sourceMappingURL=is-input-CUEWaxtA.js.map
2
+ //# sourceMappingURL=is-input-CEEoHxXN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"is-input-CUEWaxtA.js","names":[],"sources":["../src/components/input/is-input.ts"],"sourcesContent":["/**\n * Type guard for an HTMLInputElement.\n *\n * @example\n * ```tsx\n * function handleElement(element: HTMLElement) {\n * if (isInput(element)) {\n * // TypeScript now knows element is HTMLInputElement\n * element.value = \"new value\";\n * element.focus();\n * }\n * }\n * ```\n */\nexport function isInput(value: unknown): value is HTMLInputElement {\n\treturn value != null && value instanceof HTMLInputElement;\n}\n"],"mappings":"AAcA,SAAgB,EAAQ,EAA2C,CAClE,OAAO,GAAS,MAAQ,aAAiB"}
1
+ {"version":3,"file":"is-input-CEEoHxXN.js","names":[],"sources":["../src/components/input/is-input.ts"],"sourcesContent":["/**\n * Type guard for an HTMLInputElement.\n *\n * @example\n * ```tsx\n * function handleElement(element: HTMLElement) {\n * if (isInput(element)) {\n * // TypeScript now knows element is HTMLInputElement\n * element.value = \"new value\";\n * element.focus();\n * }\n * }\n * ```\n */\nexport function isInput(value: unknown): value is HTMLInputElement {\n\treturn value != null && value instanceof HTMLInputElement;\n}\n"],"mappings":"AAcA,SAAgB,EAAQ,EAA2C,CAClE,OAAO,GAAS,MAAQ,aAAiB"}
@@ -1,2 +1,2 @@
1
1
  import{t as e}from"./cx-D1HYnpvA.js";import{jsx as t}from"react/jsx-runtime";function n({children:n,className:r,...i}){return t(`kbd`,{"data-slot":`kbd`,className:e(`[font-kerning:normal] [font-variant-ligatures:common-ligatures_contextual]`,`appearance-none tabular-nums inline-grid place-items-center size-5 bg-neutral-500/15 px-1 rounded text-mono leading-none font-mono`,r),...i,children:n})}export{n as t};
2
- //# sourceMappingURL=kbd-CAVUiqBT.js.map
2
+ //# sourceMappingURL=kbd-CbMxDL9E.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"kbd-CAVUiqBT.js","names":[],"sources":["../src/components/kbd/kbd.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport { cx } from \"../../utils/cx/cx.js\";\n\n/**\n * A square, centered keyboard \"key\" chip for rendering shortcut hints —\n * \"K\", \"⌘\", \"⌃\", \"Enter\". Renders a native `<kbd>` element so screen\n * readers announce it as keyboard input. Sized so letters and modifier\n * symbols share a consistent visual height, width, and baseline.\n *\n * **When to use**\n * - Documenting keyboard shortcuts in copy or tooltips.\n * - Inside menu items and command palettes alongside the action label.\n * - Inline with prose: \"Press `Kbd K` to open search.\"\n *\n * **When not to use**\n * - For arbitrary monospace text — use {@link https://mantle.ngrok.com/components/code Code}.\n * - For chord-style multi-key shortcuts as a single chip — render multiple\n * `<Kbd>` elements separated by `+` text instead.\n *\n * **Accessibility.** Symbol-only glyphs (`⌘`, `⌃`, `↵`) are not announced\n * meaningfully by screen readers. Provide an accessible name via\n * `aria-label` on the `<Kbd>` or include a visually-hidden label inside,\n * and mark the visible glyph `aria-hidden`.\n *\n * @see https://mantle.ngrok.com/components/kbd\n *\n * @example\n * ```tsx\n * import { Kbd } from \"@ngrok/mantle/kbd\";\n *\n * // Letter key.\n * <Kbd>K</Kbd>\n *\n * // Chord — render each key separately.\n * <span>\n * <Kbd aria-label=\"Command\">⌘</Kbd> + <Kbd>K</Kbd>\n * </span>\n *\n * // Symbol with sr-only label.\n * <Kbd>\n * <span className=\"sr-only\">Enter</span>\n * <span aria-hidden>↵</span>\n * </Kbd>\n * ```\n */\nfunction Kbd({ children, className, ...props }: ComponentProps<\"kbd\">) {\n\treturn (\n\t\t<kbd\n\t\t\tdata-slot=\"kbd\"\n\t\t\tclassName={cx(\n\t\t\t\t\"[font-kerning:normal] [font-variant-ligatures:common-ligatures_contextual]\",\n\t\t\t\t\"appearance-none tabular-nums inline-grid place-items-center size-5 bg-neutral-500/15 px-1 rounded text-mono leading-none font-mono\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</kbd>\n\t);\n}\n\nexport {\n\t//,\n\tKbd,\n};\n"],"mappings":"6EA6CA,SAAS,EAAI,CAAE,WAAU,YAAW,GAAG,GAAgC,CACtE,OACC,EAAC,MAAD,CACC,YAAU,MACV,UAAW,EACV,6EACA,qIACA,EACA,CACD,GAAI,EAEH,WACI,CAAA"}
1
+ {"version":3,"file":"kbd-CbMxDL9E.js","names":[],"sources":["../src/components/kbd/kbd.tsx"],"sourcesContent":["import type { ComponentProps } from \"react\";\nimport { cx } from \"../../utils/cx/cx.js\";\n\n/**\n * A square, centered keyboard \"key\" chip for rendering shortcut hints —\n * \"K\", \"⌘\", \"⌃\", \"Enter\". Renders a native `<kbd>` element so screen\n * readers announce it as keyboard input. Sized so letters and modifier\n * symbols share a consistent visual height, width, and baseline.\n *\n * **When to use**\n * - Documenting keyboard shortcuts in copy or tooltips.\n * - Inside menu items and command palettes alongside the action label.\n * - Inline with prose: \"Press `Kbd K` to open search.\"\n *\n * **When not to use**\n * - For arbitrary monospace text — use {@link https://mantle.ngrok.com/components/code Code}.\n * - For chord-style multi-key shortcuts as a single chip — render multiple\n * `<Kbd>` elements separated by `+` text instead.\n *\n * **Accessibility.** Symbol-only glyphs (`⌘`, `⌃`, `↵`) are not announced\n * meaningfully by screen readers. Provide an accessible name via\n * `aria-label` on the `<Kbd>` or include a visually-hidden label inside,\n * and mark the visible glyph `aria-hidden`.\n *\n * @see https://mantle.ngrok.com/components/kbd\n *\n * @example\n * ```tsx\n * import { Kbd } from \"@ngrok/mantle/kbd\";\n *\n * // Letter key.\n * <Kbd>K</Kbd>\n *\n * // Chord — render each key separately.\n * <span>\n * <Kbd aria-label=\"Command\">⌘</Kbd> + <Kbd>K</Kbd>\n * </span>\n *\n * // Symbol with sr-only label.\n * <Kbd>\n * <span className=\"sr-only\">Enter</span>\n * <span aria-hidden>↵</span>\n * </Kbd>\n * ```\n */\nfunction Kbd({ children, className, ...props }: ComponentProps<\"kbd\">) {\n\treturn (\n\t\t<kbd\n\t\t\tdata-slot=\"kbd\"\n\t\t\tclassName={cx(\n\t\t\t\t\"[font-kerning:normal] [font-variant-ligatures:common-ligatures_contextual]\",\n\t\t\t\t\"appearance-none tabular-nums inline-grid place-items-center size-5 bg-neutral-500/15 px-1 rounded text-mono leading-none font-mono\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</kbd>\n\t);\n}\n\nexport {\n\t//,\n\tKbd,\n};\n"],"mappings":"6EA6CA,SAAS,EAAI,CAAE,WAAU,YAAW,GAAG,GAAgC,CACtE,OACC,EAAC,MAAD,CACC,YAAU,MACV,UAAW,EACV,6EACA,qIACA,EACA,CACD,GAAI,EAEH,WACI,CAAA"}
package/dist/kbd.js CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./kbd-CAVUiqBT.js";export{e as Kbd};
1
+ import{t as e}from"./kbd-CbMxDL9E.js";export{e as Kbd};
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./cx-D1HYnpvA.js";import{forwardRef as t}from"react";import{jsx as n}from"react/jsx-runtime";const r=t(({"aria-disabled":t,children:r,className:i,disabled:a,onMouseDown:o,...s},c)=>n(`label`,{"aria-disabled":a??t,"data-slot":`label`,className:e(`text-strong cursor-pointer text-sm peer-disabled:cursor-default has-disabled:cursor-default aria-disabled:cursor-default font-sans`,`[:where(&:not(:has(input,textarea,select,button,[contenteditable])))]:font-medium`,i),onMouseDown:e=>{e.target.closest(`button, input, select, textarea`)||(o?.(e),!e.defaultPrevented&&e.detail>1&&e.preventDefault())},ref:c,...s,children:r}));r.displayName=`Label`;export{r as t};
2
+ //# sourceMappingURL=label-x6FcOpxc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-x6FcOpxc.js","names":[],"sources":["../src/components/label/label.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { ComponentPropsWithoutRef, ComponentRef } from \"react\";\nimport { cx } from \"../../utils/cx/cx.js\";\n\ntype LabelProps = ComponentPropsWithoutRef<\"label\"> & {\n\t/**\n\t * If set, the label will appear disabled.\n\t */\n\tdisabled?: boolean;\n};\n\n/**\n * A caption for a form control — input, checkbox, radio, switch, select.\n * Renders a native `<label>`. Pair every form control with a `Label` so the\n * control has an accessible name, clicks on the label focus the control, and\n * screen readers announce the field correctly.\n *\n * **When to use**\n * - Every visible form control. Always.\n * - Above or beside an input to describe it (\"Email\", \"API key\").\n * - Wrapping a checkbox or radio next to its descriptive text.\n *\n * **When not to use**\n * - For static UI text that isn't labeling a control — use a heading or\n * plain `<p>`/`<span>`.\n * - As a substitute for `aria-label` on non-`<input>` widgets that don't\n * support `<label for>` association.\n *\n * **Two ways to associate.** Either wrap the control inside the `<Label>`\n * (implicit association — simplest) or set `htmlFor` to the control's `id`\n * (explicit — required when the control isn't a child).\n *\n * **Disabled state.** Pass `disabled` to render the label in a disabled\n * style. Typically you'll want this to mirror the underlying control's\n * disabled state so the visual treatment stays consistent.\n *\n * **Font weight.** A `Label` automatically gets `font-medium` when it does\n * **not** contain a nested form control (`<input>`, `<textarea>`, `<select>`,\n * `<button>`, or `[contenteditable]`). When the label *does* wrap a control,\n * the auto default is intentionally skipped so the control's own typography\n * isn't bolded — apply `font-medium` to your own caption element (e.g. a\n * `<span>` or `<p>`) inside the label. Override the default at any time by\n * passing a font-weight utility on the `Label` itself, e.g.\n * `<Label className=\"font-bold\">`.\n *\n * @see https://mantle.ngrok.com/components/label\n *\n * @example\n * ```tsx\n * import { Label } from \"@ngrok/mantle/label\";\n * import { Input } from \"@ngrok/mantle/input\";\n *\n * // Implicit — control nested inside the label.\n * <Label className=\"grid gap-1\">\n * <span>Email</span>\n * <Input type=\"email\" name=\"email\" />\n * </Label>\n *\n * // Explicit — htmlFor matches the control's id.\n * <div className=\"grid gap-1\">\n * <Label htmlFor=\"api-key\">API key</Label>\n * <Input id=\"api-key\" name=\"apiKey\" />\n * </div>\n *\n * // Inline label for a checkbox.\n * <Label className=\"flex items-center gap-2\">\n * <Checkbox name=\"terms\" />\n * <span>I agree to the terms</span>\n * </Label>\n * ```\n */\nconst Label = forwardRef<ComponentRef<\"label\">, LabelProps>(\n\t(\n\t\t{ \"aria-disabled\": _ariaDisabled, children, className, disabled, onMouseDown, ...props },\n\t\tref,\n\t) => (\n\t\t// biome-ignore lint/a11y/noLabelWithoutControl: this is a composable label component\n\t\t<label\n\t\t\taria-disabled={disabled ?? _ariaDisabled}\n\t\t\tdata-slot=\"label\"\n\t\t\tclassName={cx(\n\t\t\t\t\"text-strong cursor-pointer text-sm peer-disabled:cursor-default has-disabled:cursor-default aria-disabled:cursor-default font-sans\",\n\t\t\t\t// Default to font-medium when the label isn't wrapping a form control. The\n\t\t\t\t// arbitrary variant wraps the *entire* matched selector — class + the\n\t\t\t\t// `:not(:has(...))` check — in `:where()`, flattening total specificity to 0.\n\t\t\t\t// That lets a user-supplied font-weight utility (`font-bold`, `font-normal`,\n\t\t\t\t// etc.) at (0,1,0) override cleanly, even though `[contenteditable]` is an\n\t\t\t\t// attribute selector that would otherwise lift the rule to (0,1,0) and tie.\n\t\t\t\t\"[:where(&:not(:has(input,textarea,select,button,[contenteditable])))]:font-medium\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tonMouseDown={(event) => {\n\t\t\t\t// only prevent text selection if clicking inside the label itself\n\t\t\t\tconst target = event.target as HTMLElement;\n\t\t\t\tif (target.closest(\"button, input, select, textarea\")) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tonMouseDown?.(event);\n\n\t\t\t\t// prevent text selection when double clicking label\n\t\t\t\tif (!event.defaultPrevented && event.detail > 1) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}}\n\t\t\tref={ref}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</label>\n\t),\n);\nLabel.displayName = \"Label\";\n\nexport {\n\t//\n\tLabel,\n};\n"],"mappings":"gHAuEA,MAAM,EAAQ,GAEZ,CAAE,gBAAiB,EAAe,WAAU,YAAW,WAAU,cAAa,GAAG,GACjF,IAGA,EAAC,QAAD,CACC,gBAAe,GAAY,EAC3B,YAAU,QACV,UAAW,EACV,qIAOA,oFACA,EACA,CACD,YAAc,GAAU,CAER,EAAM,OACV,QAAQ,kCAAkC,GAIrD,IAAc,EAAM,CAGhB,CAAC,EAAM,kBAAoB,EAAM,OAAS,GAC7C,EAAM,gBAAgB,GAGnB,MACL,GAAI,EAEH,WACM,CAAA,CAET,CACD,EAAM,YAAc"}
package/dist/label.d.ts CHANGED
@@ -26,6 +26,15 @@ import * as _$react from "react";
26
26
  * style. Typically you'll want this to mirror the underlying control's
27
27
  * disabled state so the visual treatment stays consistent.
28
28
  *
29
+ * **Font weight.** A `Label` automatically gets `font-medium` when it does
30
+ * **not** contain a nested form control (`<input>`, `<textarea>`, `<select>`,
31
+ * `<button>`, or `[contenteditable]`). When the label *does* wrap a control,
32
+ * the auto default is intentionally skipped so the control's own typography
33
+ * isn't bolded — apply `font-medium` to your own caption element (e.g. a
34
+ * `<span>` or `<p>`) inside the label. Override the default at any time by
35
+ * passing a font-weight utility on the `Label` itself, e.g.
36
+ * `<Label className="font-bold">`.
37
+ *
29
38
  * @see https://mantle.ngrok.com/components/label
30
39
  *
31
40
  * @example
package/dist/label.js CHANGED
@@ -1,2 +1 @@
1
- import{t as e}from"./cx-D1HYnpvA.js";import{forwardRef as t}from"react";import{jsx as n}from"react/jsx-runtime";const r=t(({"aria-disabled":t,children:r,className:i,disabled:a,onMouseDown:o,...s},c)=>n(`label`,{"aria-disabled":a??t,"data-slot":`label`,className:e(`text-strong cursor-pointer text-sm peer-disabled:cursor-default has-disabled:cursor-default aria-disabled:cursor-default font-sans`,i),onMouseDown:e=>{e.target.closest(`button, input, select, textarea`)||(o?.(e),!e.defaultPrevented&&e.detail>1&&e.preventDefault())},ref:c,...s,children:r}));r.displayName=`Label`;export{r as Label};
2
- //# sourceMappingURL=label.js.map
1
+ import{t as e}from"./label-x6FcOpxc.js";export{e as Label};
package/dist/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @ngrok/mantle (0.72.0)
1
+ # @ngrok/mantle (0.73.1)
2
2
 
3
3
  > Offline discovery hint shipped inside the @ngrok/mantle npm package. Authoritative metadata lives at https://mantle.ngrok.com/for-ai-agents.
4
4
 
@@ -40,6 +40,7 @@ Full text: https://mantle.ngrok.com/llms-full.txt
40
40
  - `@ngrok/mantle/dialog`
41
41
  - `@ngrok/mantle/dropdown-menu`
42
42
  - `@ngrok/mantle/empty`
43
+ - `@ngrok/mantle/field`
43
44
  - `@ngrok/mantle/flag`
44
45
  - `@ngrok/mantle/highlight-utils`
45
46
  - `@ngrok/mantle/hooks`
@@ -1,4 +1,4 @@
1
- import { t as WithAsChild } from "./as-child-DQHfEmYB.js";
1
+ import { t as WithAsChild } from "./as-child-C2PttRwz.js";
2
2
  import * as _$react from "react";
3
3
  import { ComponentProps } from "react";
4
4
 
@@ -1,5 +1,5 @@
1
- import { t as WithAsChild } from "./as-child-DQHfEmYB.js";
2
- import { o as WithValidation } from "./types-DG0WQLTL.js";
1
+ import { t as WithAsChild } from "./as-child-C2PttRwz.js";
2
+ import { o as WithValidation } from "./validation-DF1z7YDr.js";
3
3
  import * as _$react from "react";
4
4
  import { ComponentProps, ReactNode } from "react";
5
5
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
@@ -1,2 +1,2 @@
1
- import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./icon-bWc5yC3-.js";import{t as n}from"./slot-D_ZUrdEW.js";import{t as r}from"./compose-refs-DZ3cPi47.js";import{n as i}from"./separator-DSOIrnhj.js";import{t as a}from"./use-prefers-reduced-motion-aXfsyo-k.js";import{createContext as o,forwardRef as s,useCallback as c,useContext as l,useEffect as u,useMemo as d,useRef as f}from"react";import{Fragment as p,jsx as m,jsxs as h}from"react/jsx-runtime";import{XIcon as g}from"@phosphor-icons/react/X";import{CheckIcon as _}from"@phosphor-icons/react/Check";import*as v from"@ariakit/react";import{LockIcon as y}from"@phosphor-icons/react/Lock";const b=e=>Array.isArray(e)&&e.every(e=>typeof e==`string`),x=[],S=o({current:null}),C=o({current:[]}),w=o({onInputKeyDownRef:{current:void 0},inputRef:{current:null}}),T=({children:e,defaultSelectedValue:t=[],...n})=>{let r=f(null),i=f(void 0),a=f(null),o=f([]),s=d(()=>({onInputKeyDownRef:i,inputRef:a}),[]);return m(S.Provider,{value:r,children:m(w.Provider,{value:s,children:m(C.Provider,{value:o,children:m(v.ComboboxProvider,{defaultSelectedValue:t,...n,children:e})})})})};T.displayName=`MultiSelect`;const E=s(({"aria-invalid":t,className:n,children:i,onKeyDown:a,onMouseDown:o,validation:s,...c},u)=>{let d=l(S),{inputRef:f}=l(w),p=v.useComboboxContext(),h=t!=null&&t!==`false`?`error`:typeof s==`function`?s():s;return m(`div`,{role:`group`,"data-slot":`multi-select-trigger`,className:e(`cursor-text select-none font-sans text-sm`,`border-form bg-form text-strong flex min-h-9 w-full flex-wrap items-center gap-1 rounded-md border px-3 py-1 has-[[data-slot=multi-select-tag]]:px-1`,`has-focus:outline-hidden has-focus-within:ring-4 has-aria-expanded:ring-4`,`has-focus-within:border-accent-600 has-focus-within:ring-focus-accent has-aria-expanded:border-accent-600 has-aria-expanded:ring-focus-accent`,`data-validation-success:border-success-600 data-validation-success:has-focus-within:border-success-600 data-validation-success:has-focus-within:ring-focus-success data-validation-success:has-aria-expanded:border-success-600 data-validation-success:has-aria-expanded:ring-focus-success`,`data-validation-warning:border-warning-600 data-validation-warning:has-focus-within:border-warning-600 data-validation-warning:has-focus-within:ring-focus-warning data-validation-warning:has-aria-expanded:border-warning-600 data-validation-warning:has-aria-expanded:ring-focus-warning`,`data-validation-error:border-danger-600 data-validation-error:has-focus-within:border-danger-600 data-validation-error:has-focus-within:ring-focus-danger data-validation-error:has-aria-expanded:border-danger-600 data-validation-error:has-aria-expanded:ring-focus-danger`,n),"data-validation":h||void 0,onKeyDown:e=>{e.key===`Escape`&&p?.getState().open&&(e.preventDefault(),p.hide()),a?.(e)},onMouseDown:e=>{e.target instanceof HTMLElement&&!e.target.closest(`button, input, [role='option']`)&&(e.preventDefault(),f.current?.focus()),o?.(e)},ref:r(d,u),...c,children:i})});E.displayName=`MultiSelectTrigger`;const D=s(({className:n,value:i,onRemove:a,locked:o=!1,onKeyDown:s,...c},l)=>{let u=f(null);return h(`span`,{ref:r(u,l),role:`option`,"aria-selected":!0,tabIndex:-1,"data-slot":`multi-select-tag`,"data-locked":o||void 0,className:e(`cursor-default bg-neutral-500/10 border border-neutral-500/20 rounded-xs text-strong inline-flex items-center gap-1 pl-2 pr-0.5 py-0.5 text-sm font-normal`,`focus-visible:outline-hidden focus-visible:border-accent-600/50 focus-visible:ring-3 focus-visible:ring-focus-accent`,n),onKeyDown:e=>{if(o&&(e.key===`Backspace`||e.key===`Delete`)){e.preventDefault(),z(e.currentTarget);return}s?.(e)},...c,children:[i,m(`button`,{type:`button`,"aria-label":`Remove ${i}`,tabIndex:-1,"aria-disabled":o||void 0,className:e(`cursor-pointer text-strong/40 hover:bg-neutral-500/15 hover:text-strong rounded-xs p-0.5`,`aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-strong/40`),onClick:e=>{if(e.stopPropagation(),o){let e=u.current;e&&z(e);return}a?.()},onMouseDown:e=>{e.preventDefault()},children:m(t,{svg:o?m(y,{}):m(g,{weight:`bold`}),className:`size-4`})})]})});D.displayName=`MultiSelectTag`;const O=({children:e,lockedValues:t=[]})=>{let n=v.useComboboxContext(),r=v.useStoreState(n,`selectedValue`),i=(b(r)?r:void 0)??x,a=f(i);a.current=i;let o=l(C);o.current=t;let s=d(()=>new Set(t),[t]),c=f(new Map),{onInputKeyDownRef:h,inputRef:g}=l(w),_=f(new Set);u(()=>()=>{_.current.forEach(cancelAnimationFrame)},[]);let y=e=>{let t;t=requestAnimationFrame(()=>{_.current.delete(t),e()}),_.current.add(t)},S=e=>{if(n){let t=n.getState().selectedValue;if(!b(t))return;n.setSelectedValue(t.filter(t=>t!==e))}},T=e=>{let t=a.current[e];if(t==null)return;let r=c.current.get(t);r&&(r.focus(),n?.show())},E=()=>{g.current?.focus()},O=(e,t)=>{let n=i[t];switch(e.key){case`ArrowLeft`:e.preventDefault(),t>0&&T(t-1);break;case`ArrowRight`:e.preventDefault(),t<i.length-1?T(t+1):E();break;case`Backspace`:case`Delete`:if(e.preventDefault(),n!=null){if(s.has(n)){let e=c.current.get(n);e&&z(e);break}if(S(n),e.key===`Backspace`)if(t>0){let e=t-1;y(()=>T(e))}else y(()=>{a.current.length>0?T(0):E()});else y(()=>{t<a.current.length?T(t):E()})}break;case`ArrowUp`:case`ArrowDown`:e.preventDefault(),E(),g.current?.dispatchEvent(new KeyboardEvent(`keydown`,{key:e.key,bubbles:!0,cancelable:!0,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,altKey:e.altKey}));break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&E();break}};return h.current=e=>{if(e.key===`ArrowLeft`&&e.currentTarget.selectionStart===0&&e.currentTarget.selectionEnd===0&&i.length>0){e.preventDefault(),T(i.length-1);return}if(e.key===`Backspace`&&e.currentTarget.value===``&&i.length>0){let e=i[i.length-1];if(e!=null)if(o.current.includes(e)){let t=c.current.get(e);t&&z(t)}else S(e)}},m(p,{children:i.map((t,n)=>{let r={value:t,locked:s.has(t),onRemove:()=>{if(s.has(t)){let e=c.current.get(t);e&&z(e);return}S(t)},ref:e=>{e?c.current.set(t,e):c.current.delete(t)},onKeyDown:e=>O(e,n),onClick:()=>T(n)};return e?e(r):m(D,{...r},t)})})};O.displayName=`MultiSelectTagValues`;const k=s(({className:t,onBlur:n,onChange:i,onFocus:a,onKeyDown:o,onValueChange:s,placeholder:c,...u},d)=>{let f=v.useComboboxContext(),{onInputKeyDownRef:p,inputRef:h}=l(w),g=v.useStoreState(f,`selectedValue`),_=((b(g)?g:void 0)?.length??0)>0;return m(v.Combobox,{autoSelect:!0,"data-slot":`multi-select-input`,className:e(`pointer-coarse:text-base min-w-20 flex-1 select-text border-0 bg-transparent text-sm outline-hidden`,`placeholder:select-none placeholder:text-placeholder`,t),onChange:e=>{s?.(e.target.value),i?.(e)},onKeyDown:e=>{p.current?.(e),o?.(e)},onBlur:e=>{e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest(`[data-slot="multi-select-tag"]`)&&f?.show(),n?.(e)},onFocus:e=>{f?.show(),a?.(e)},placeholder:_?void 0:c,ref:r(h,d),...u})});k.displayName=`MultiSelectInput`;const A=s(({asChild:t=!1,backdrop:r=!1,children:i,className:a,modal:o=!0,portalElement:s,sameWidth:u=!0,unmountOnHide:d=!0,...f},p)=>{let h=l(S),g=c(()=>h.current?.getBoundingClientRect()??null,[h]),_=c(e=>typeof s==`function`?s(e):s??h.current?.closest(`[data-mantle-modal-content]`)??e.ownerDocument.body,[s,h]),y=c(e=>!(e.target instanceof Node&&h.current?.contains(e.target)),[h]);return m(v.ComboboxPopover,{"data-slot":`multi-select-content`,className:e(`border-popover bg-popover relative z-50 max-h-96 min-w-32 scrollbar overflow-y-scroll overflow-x-hidden overscroll-y-none rounded-md border shadow-md pt-1 pb-1 has-data-content-footer:pb-0 font-sans flex flex-col gap-px focus:outline-hidden`,a),backdrop:r,getAnchorRect:g,gutter:4,hideOnInteractOutside:y,modal:o,portalElement:_,ref:p,render:t?({ref:e,...t})=>m(n,{ref:e,...t}):void 0,sameWidth:u,unmountOnHide:d,...f,children:i})});A.displayName=`MultiSelectContent`;const j=s(({asChild:r=!1,children:i,className:a,focusOnHover:o=!0,value:s,onClick:c,...u},d)=>{let f=l(C),p=s!=null&&f.current.includes(s);return h(v.ComboboxItem,{"data-slot":`multi-select-item`,className:e(`relative mx-1 cursor-pointer rounded-md pl-2 pr-8 py-1.5 text-strong text-sm font-normal flex min-w-0 items-center gap-2`,`[[role=option]+&]:mt-px`,`data-active-item:bg-active-menu-item`,`aria-disabled:opacity-50`,`aria-selected:bg-selected-menu-item aria-selected:data-active-item:bg-active-selected-menu-item`,a),focusOnHover:o,onClick:e=>{if(p){e.preventDefault();return}c?.(e)},ref:d,render:r?({ref:e,...t})=>m(n,{ref:e,...t}):void 0,resetValueOnSelect:!0,value:s,...u,children:[i,m(v.ComboboxItemCheck,{className:`absolute right-2 flex h-3.5 w-3.5 items-center justify-center`,children:m(t,{svg:m(_,{weight:`bold`}),className:`size-4 text-accent-600`})})]})});j.displayName=`MultiSelectItem`;const M=s(({asChild:e=!1,children:t,...r},i)=>m(v.ComboboxGroup,{"data-slot":`multi-select-group`,className:`mx-1`,ref:i,render:e?({ref:e,...t})=>m(n,{ref:e,...t}):void 0,...r,children:t}));M.displayName=`MultiSelectGroup`;const N=s(({asChild:t=!1,children:r,className:i,...a},o)=>m(v.ComboboxGroupLabel,{"data-slot":`multi-select-group-label`,className:e(`text-muted px-2 py-1 text-xs font-medium`,i),ref:o,render:t?({ref:e,...t})=>m(n,{ref:e,...t}):void 0,...a,children:r}));N.displayName=`MultiSelectGroupLabel`;const P=s(({className:t,children:n,...r},i)=>m(`p`,{"data-slot":`multi-select-group-description`,className:e(`text-muted px-2 pb-1 text-xs`,t),ref:i,...r,children:n}));P.displayName=`MultiSelectGroupDescription`;const F=s(({className:t,...n},r)=>m(i,{"data-slot":`multi-select-separator`,ref:r,className:e(`my-1 w-auto`,t),...n}));F.displayName=`MultiSelectSeparator`;const I=s(({className:t,children:n,...r},i)=>m(`div`,{"data-slot":`multi-select-empty`,className:e(`mx-1 text-muted px-2 py-6 text-center text-sm`,t),ref:i,role:`presentation`,...r,children:n}));I.displayName=`MultiSelectEmpty`;const L=s(({className:t,children:n,...r},i)=>m(`div`,{ref:i,"data-slot":`multi-select-content-footer`,"data-content-footer":!0,className:e(`bg-popover sticky bottom-0 border-t border-popover`,t),...r,children:n}));L.displayName=`MultiSelectContentFooter`;const R={Root:T,Trigger:E,TagValues:O,Input:k,Tag:D,Content:A,ContentFooter:L,Item:j,Group:M,GroupLabel:N,GroupDescription:P,Separator:F,Empty:I};function z(e){a()||e.animate([{transform:`translateX(0)`},{transform:`translateX(-4px)`},{transform:`translateX(4px)`},{transform:`translateX(-4px)`},{transform:`translateX(4px)`},{transform:`translateX(0)`}],{duration:300,easing:`ease-in-out`})}export{R as MultiSelect};
1
+ import{t as e}from"./cx-D1HYnpvA.js";import{t}from"./icon-bWc5yC3-.js";import{t as n}from"./slot-D_ZUrdEW.js";import{t as r}from"./compose-refs-DZ3cPi47.js";import{a as i,r as a}from"./validation-BYME8rWN.js";import{n as o}from"./separator-awchG4LI.js";import{t as s}from"./use-prefers-reduced-motion-YUurmkwx.js";import{createContext as c,forwardRef as l,useCallback as u,useContext as d,useEffect as f,useMemo as p,useRef as m}from"react";import{Fragment as h,jsx as g,jsxs as _}from"react/jsx-runtime";import{XIcon as v}from"@phosphor-icons/react/X";import{CheckIcon as y}from"@phosphor-icons/react/Check";import*as b from"@ariakit/react";import{LockIcon as x}from"@phosphor-icons/react/Lock";const S=e=>Array.isArray(e)&&e.every(e=>typeof e==`string`),C=[],w=c({current:null}),T=c({current:[]}),E=c({onInputKeyDownRef:{current:void 0},inputRef:{current:null}}),D=({children:e,defaultSelectedValue:t=[],...n})=>{let r=m(null),i=m(void 0),a=m(null),o=m([]),s=p(()=>({onInputKeyDownRef:i,inputRef:a}),[]);return g(w.Provider,{value:r,children:g(E.Provider,{value:s,children:g(T.Provider,{value:o,children:g(b.ComboboxProvider,{defaultSelectedValue:t,...n,children:e})})})})};D.displayName=`MultiSelect`;const O=l(({"aria-invalid":t,className:n,children:o,onKeyDown:s,onMouseDown:c,validation:l,...u},f)=>{let p=d(w),{inputRef:m}=d(E),h=b.useComboboxContext(),_=i(),{validation:v}=a({"aria-invalid":t,validation:l??_});return g(`div`,{role:`group`,"data-slot":`multi-select-trigger`,className:e(`cursor-text select-none font-sans text-sm`,`border-form bg-form text-strong flex min-h-9 w-full flex-wrap items-center gap-1 rounded-md border px-3 py-1 has-[[data-slot=multi-select-tag]]:px-1`,`has-focus:outline-hidden has-focus-within:ring-4 has-aria-expanded:ring-4`,`has-focus-within:border-accent-600 has-focus-within:ring-focus-accent has-aria-expanded:border-accent-600 has-aria-expanded:ring-focus-accent`,`data-validation-success:border-success-600 data-validation-success:has-focus-within:border-success-600 data-validation-success:has-focus-within:ring-focus-success data-validation-success:has-aria-expanded:border-success-600 data-validation-success:has-aria-expanded:ring-focus-success`,`data-validation-warning:border-warning-600 data-validation-warning:has-focus-within:border-warning-600 data-validation-warning:has-focus-within:ring-focus-warning data-validation-warning:has-aria-expanded:border-warning-600 data-validation-warning:has-aria-expanded:ring-focus-warning`,`data-validation-error:border-danger-600 data-validation-error:has-focus-within:border-danger-600 data-validation-error:has-focus-within:ring-focus-danger data-validation-error:has-aria-expanded:border-danger-600 data-validation-error:has-aria-expanded:ring-focus-danger`,n),"data-validation":v||void 0,onKeyDown:e=>{e.key===`Escape`&&h?.getState().open&&(e.preventDefault(),h.hide()),s?.(e)},onMouseDown:e=>{e.target instanceof HTMLElement&&!e.target.closest(`button, input, [role='option']`)&&(e.preventDefault(),m.current?.focus()),c?.(e)},ref:r(p,f),...u,children:o})});O.displayName=`MultiSelectTrigger`;const k=l(({className:n,value:i,onRemove:a,locked:o=!1,onKeyDown:s,...c},l)=>{let u=m(null);return _(`span`,{ref:r(u,l),role:`option`,"aria-selected":!0,tabIndex:-1,"data-slot":`multi-select-tag`,"data-locked":o||void 0,className:e(`cursor-default bg-neutral-500/10 border border-neutral-500/20 rounded-xs text-strong inline-flex items-center gap-1 pl-2 pr-0.5 py-0.5 text-sm font-normal`,`focus-visible:outline-hidden focus-visible:border-accent-600/50 focus-visible:ring-3 focus-visible:ring-focus-accent`,n),onKeyDown:e=>{if(o&&(e.key===`Backspace`||e.key===`Delete`)){e.preventDefault(),V(e.currentTarget);return}s?.(e)},...c,children:[i,g(`button`,{type:`button`,"aria-label":`Remove ${i}`,tabIndex:-1,"aria-disabled":o||void 0,className:e(`cursor-pointer text-strong/40 hover:bg-neutral-500/15 hover:text-strong rounded-xs p-0.5`,`aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-strong/40`),onClick:e=>{if(e.stopPropagation(),o){let e=u.current;e&&V(e);return}a?.()},onMouseDown:e=>{e.preventDefault()},children:g(t,{svg:o?g(x,{}):g(v,{weight:`bold`}),className:`size-4`})})]})});k.displayName=`MultiSelectTag`;const A=({children:e,lockedValues:t=[]})=>{let n=b.useComboboxContext(),r=b.useStoreState(n,`selectedValue`),i=(S(r)?r:void 0)??C,a=m(i);a.current=i;let o=d(T);o.current=t;let s=p(()=>new Set(t),[t]),c=m(new Map),{onInputKeyDownRef:l,inputRef:u}=d(E),_=m(new Set);f(()=>()=>{_.current.forEach(cancelAnimationFrame)},[]);let v=e=>{let t;t=requestAnimationFrame(()=>{_.current.delete(t),e()}),_.current.add(t)},y=e=>{if(n){let t=n.getState().selectedValue;if(!S(t))return;n.setSelectedValue(t.filter(t=>t!==e))}},x=e=>{let t=a.current[e];if(t==null)return;let r=c.current.get(t);r&&(r.focus(),n?.show())},w=()=>{u.current?.focus()},D=(e,t)=>{let n=i[t];switch(e.key){case`ArrowLeft`:e.preventDefault(),t>0&&x(t-1);break;case`ArrowRight`:e.preventDefault(),t<i.length-1?x(t+1):w();break;case`Backspace`:case`Delete`:if(e.preventDefault(),n!=null){if(s.has(n)){let e=c.current.get(n);e&&V(e);break}if(y(n),e.key===`Backspace`)if(t>0){let e=t-1;v(()=>x(e))}else v(()=>{a.current.length>0?x(0):w()});else v(()=>{t<a.current.length?x(t):w()})}break;case`ArrowUp`:case`ArrowDown`:e.preventDefault(),w(),u.current?.dispatchEvent(new KeyboardEvent(`keydown`,{key:e.key,bubbles:!0,cancelable:!0,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey,altKey:e.altKey}));break;default:e.key.length===1&&!e.ctrlKey&&!e.metaKey&&w();break}};return l.current=e=>{if(e.key===`ArrowLeft`&&e.currentTarget.selectionStart===0&&e.currentTarget.selectionEnd===0&&i.length>0){e.preventDefault(),x(i.length-1);return}if(e.key===`Backspace`&&e.currentTarget.value===``&&i.length>0){let e=i[i.length-1];if(e!=null)if(o.current.includes(e)){let t=c.current.get(e);t&&V(t)}else y(e)}},g(h,{children:i.map((t,n)=>{let r={value:t,locked:s.has(t),onRemove:()=>{if(s.has(t)){let e=c.current.get(t);e&&V(e);return}y(t)},ref:e=>{e?c.current.set(t,e):c.current.delete(t)},onKeyDown:e=>D(e,n),onClick:()=>x(n)};return e?e(r):g(k,{...r},t)})})};A.displayName=`MultiSelectTagValues`;const j=l(({className:t,onBlur:n,onChange:i,onFocus:a,onKeyDown:o,onValueChange:s,placeholder:c,...l},u)=>{let f=b.useComboboxContext(),{onInputKeyDownRef:p,inputRef:m}=d(E),h=b.useStoreState(f,`selectedValue`),_=((S(h)?h:void 0)?.length??0)>0;return g(b.Combobox,{autoSelect:!0,"data-slot":`multi-select-input`,className:e(`pointer-coarse:text-base min-w-20 flex-1 select-text border-0 bg-transparent text-sm outline-hidden`,`placeholder:select-none placeholder:text-placeholder`,t),onChange:e=>{s?.(e.target.value),i?.(e)},onKeyDown:e=>{p.current?.(e),o?.(e)},onBlur:e=>{e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest(`[data-slot="multi-select-tag"]`)&&f?.show(),n?.(e)},onFocus:e=>{f?.show(),a?.(e)},placeholder:_?void 0:c,ref:r(m,u),...l})});j.displayName=`MultiSelectInput`;const M=l(({asChild:t=!1,backdrop:r=!1,children:i,className:a,modal:o=!0,portalElement:s,sameWidth:c=!0,unmountOnHide:l=!0,...f},p)=>{let m=d(w),h=u(()=>m.current?.getBoundingClientRect()??null,[m]),_=u(e=>typeof s==`function`?s(e):s??m.current?.closest(`[data-mantle-modal-content]`)??e.ownerDocument.body,[s,m]),v=u(e=>!(e.target instanceof Node&&m.current?.contains(e.target)),[m]);return g(b.ComboboxPopover,{"data-slot":`multi-select-content`,className:e(`border-popover bg-popover relative z-50 max-h-96 min-w-32 scrollbar overflow-y-scroll overflow-x-hidden overscroll-y-none rounded-md border shadow-md pt-1 pb-1 has-data-content-footer:pb-0 font-sans flex flex-col gap-px focus:outline-hidden`,a),backdrop:r,getAnchorRect:h,gutter:4,hideOnInteractOutside:v,modal:o,portalElement:_,ref:p,render:t?({ref:e,...t})=>g(n,{ref:e,...t}):void 0,sameWidth:c,unmountOnHide:l,...f,children:i})});M.displayName=`MultiSelectContent`;const N=l(({asChild:r=!1,children:i,className:a,focusOnHover:o=!0,value:s,onClick:c,...l},u)=>{let f=d(T),p=s!=null&&f.current.includes(s);return _(b.ComboboxItem,{"data-slot":`multi-select-item`,className:e(`relative mx-1 cursor-pointer rounded-md pl-2 pr-8 py-1.5 text-strong text-sm font-normal flex min-w-0 items-center gap-2`,`[[role=option]+&]:mt-px`,`data-active-item:bg-active-menu-item`,`aria-disabled:opacity-50`,`aria-selected:bg-selected-menu-item aria-selected:data-active-item:bg-active-selected-menu-item`,a),focusOnHover:o,onClick:e=>{if(p){e.preventDefault();return}c?.(e)},ref:u,render:r?({ref:e,...t})=>g(n,{ref:e,...t}):void 0,resetValueOnSelect:!0,value:s,...l,children:[i,g(b.ComboboxItemCheck,{className:`absolute right-2 flex h-3.5 w-3.5 items-center justify-center`,children:g(t,{svg:g(y,{weight:`bold`}),className:`size-4 text-accent-600`})})]})});N.displayName=`MultiSelectItem`;const P=l(({asChild:e=!1,children:t,...r},i)=>g(b.ComboboxGroup,{"data-slot":`multi-select-group`,className:`mx-1`,ref:i,render:e?({ref:e,...t})=>g(n,{ref:e,...t}):void 0,...r,children:t}));P.displayName=`MultiSelectGroup`;const F=l(({asChild:t=!1,children:r,className:i,...a},o)=>g(b.ComboboxGroupLabel,{"data-slot":`multi-select-group-label`,className:e(`text-muted px-2 py-1 text-xs font-medium`,i),ref:o,render:t?({ref:e,...t})=>g(n,{ref:e,...t}):void 0,...a,children:r}));F.displayName=`MultiSelectGroupLabel`;const I=l(({className:t,children:n,...r},i)=>g(`p`,{"data-slot":`multi-select-group-description`,className:e(`text-muted px-2 pb-1 text-xs`,t),ref:i,...r,children:n}));I.displayName=`MultiSelectGroupDescription`;const L=l(({className:t,...n},r)=>g(o,{"data-slot":`multi-select-separator`,ref:r,className:e(`my-1 w-auto`,t),...n}));L.displayName=`MultiSelectSeparator`;const R=l(({className:t,children:n,...r},i)=>g(`div`,{"data-slot":`multi-select-empty`,className:e(`mx-1 text-muted px-2 py-6 text-center text-sm`,t),ref:i,role:`presentation`,...r,children:n}));R.displayName=`MultiSelectEmpty`;const z=l(({className:t,children:n,...r},i)=>g(`div`,{ref:i,"data-slot":`multi-select-content-footer`,"data-content-footer":!0,className:e(`bg-popover sticky bottom-0 border-t border-popover`,t),...r,children:n}));z.displayName=`MultiSelectContentFooter`;const B={Root:D,Trigger:O,TagValues:A,Input:j,Tag:k,Content:M,ContentFooter:z,Item:N,Group:P,GroupLabel:F,GroupDescription:I,Separator:L,Empty:R};function V(e){s()||e.animate([{transform:`translateX(0)`},{transform:`translateX(-4px)`},{transform:`translateX(4px)`},{transform:`translateX(-4px)`},{transform:`translateX(4px)`},{transform:`translateX(0)`}],{duration:300,easing:`ease-in-out`})}export{B as MultiSelect};
2
2
  //# sourceMappingURL=multi-select.js.map