@passui/react 0.1.45 → 0.1.46

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 (140) hide show
  1. package/README.md +57 -46
  2. package/dist/types/src/components/accordion/accordion.d.ts +1 -1
  3. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
  4. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
  5. package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
  6. package/dist/types/src/components/popover/index.d.ts +41 -0
  7. package/dist/types/src/components/popover/popover-content.d.ts +32 -0
  8. package/dist/types/src/components/popover/popover-root.d.ts +22 -0
  9. package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
  10. package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
  11. package/dist/types/src/components/table/table.d.ts +1 -1
  12. package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/machines/popover.machine.d.ts +30 -0
  15. package/package.json +307 -307
  16. package/src/appearance/index.ts +2 -2
  17. package/src/appearance/platform.tsx +76 -60
  18. package/src/appearance/theme.tsx +409 -377
  19. package/src/components/accordion/accordion.tsx +58 -49
  20. package/src/components/ambient/ambient.tsx +25 -25
  21. package/src/components/ambient/index.ts +1 -1
  22. package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
  23. package/src/components/breadcrumb/breadcrumb.tsx +63 -57
  24. package/src/components/button/button-styles.ts +39 -32
  25. package/src/components/button/button.tsx +91 -84
  26. package/src/components/button/icon-button.tsx +27 -25
  27. package/src/components/button/pending-spinner.tsx +8 -8
  28. package/src/components/button/use-pending-indicator.ts +11 -11
  29. package/src/components/chip/chip.tsx +35 -30
  30. package/src/components/code-block/code-block.tsx +40 -39
  31. package/src/components/combo-box/combo-box.tsx +5 -5
  32. package/src/components/command/command.tsx +124 -94
  33. package/src/components/compact-date-picker/calendar-month.ts +75 -53
  34. package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
  35. package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
  36. package/src/components/compact-time-picker/time-value.ts +32 -29
  37. package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
  38. package/src/components/content-view/content-view.tsx +168 -159
  39. package/src/components/content-view/index.ts +1 -1
  40. package/src/components/context-menu/conceal-menu-source.ts +25 -25
  41. package/src/components/context-menu/context-menu-host.tsx +439 -402
  42. package/src/components/context-menu/context-menu-store.ts +125 -123
  43. package/src/components/context-menu/menu-navigation.ts +25 -18
  44. package/src/components/context-menu/menu-position.ts +33 -29
  45. package/src/components/context-menu/menu-reveal.ts +32 -32
  46. package/src/components/context-menu/use-context-menu.ts +63 -56
  47. package/src/components/conversation/conversation.tsx +265 -263
  48. package/src/components/conversation/index.ts +1 -1
  49. package/src/components/date-picker/date-picker.tsx +95 -94
  50. package/src/components/date-picker/date-value.ts +20 -10
  51. package/src/components/dialog/dialog-host.tsx +105 -103
  52. package/src/components/dialog/dialog-store.ts +37 -36
  53. package/src/components/dialog/dialog.tsx +192 -179
  54. package/src/components/dialog/index.ts +1 -1
  55. package/src/components/disclosure-group/disclosure-group.tsx +93 -86
  56. package/src/components/divider/divider.tsx +24 -22
  57. package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
  58. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
  59. package/src/components/labeled-content/labeled-content.tsx +68 -68
  60. package/src/components/locale-switcher/index.ts +1 -1
  61. package/src/components/locale-switcher/locale-switcher.tsx +279 -251
  62. package/src/components/menu/menu.tsx +75 -58
  63. package/src/components/morph/index.ts +2 -2
  64. package/src/components/morph/morph.tsx +227 -201
  65. package/src/components/morph/use-morph-disclosure.ts +19 -19
  66. package/src/components/one-time-code/one-time-code.tsx +216 -200
  67. package/src/components/picker/picker-trigger.ts +16 -13
  68. package/src/components/picker/picker.tsx +371 -345
  69. package/src/components/popover/anchored-surface.tsx +253 -229
  70. package/src/components/popover/index.ts +50 -0
  71. package/src/components/popover/popover-content.tsx +334 -0
  72. package/src/components/popover/popover-fit.ts +58 -53
  73. package/src/components/popover/popover-root.tsx +97 -0
  74. package/src/components/popover/popover-trigger.tsx +71 -0
  75. package/src/components/profile-image/profile-image.tsx +80 -64
  76. package/src/components/progress-view/progress-view.tsx +61 -59
  77. package/src/components/progress-view/use-liquid-progress.ts +38 -38
  78. package/src/components/radio-group/radio-group.tsx +71 -68
  79. package/src/components/search-field/expanding-search-field.tsx +191 -177
  80. package/src/components/search-field/search-field.tsx +59 -50
  81. package/src/components/search-list/search-list.tsx +246 -236
  82. package/src/components/section/section.tsx +58 -58
  83. package/src/components/secure-field/secure-field.tsx +3 -3
  84. package/src/components/segmented-control/segmented-control.tsx +136 -136
  85. package/src/components/sheet/modal-environment.ts +71 -64
  86. package/src/components/sheet/sheet-portal.tsx +58 -53
  87. package/src/components/sheet/sheet.tsx +146 -141
  88. package/src/components/sheet/use-sheet-focus.ts +97 -82
  89. package/src/components/sidebar/sidebar.tsx +78 -78
  90. package/src/components/skeleton/skeleton.tsx +30 -21
  91. package/src/components/slider/slider.tsx +113 -104
  92. package/src/components/slosh-slider/liquid-step.ts +32 -31
  93. package/src/components/slosh-slider/slosh-slider.tsx +265 -228
  94. package/src/components/stat-tile/index.ts +1 -1
  95. package/src/components/stat-tile/stat-tile.tsx +79 -68
  96. package/src/components/status-indicator/status-indicator.tsx +40 -37
  97. package/src/components/stepper/rolling-digit.tsx +82 -68
  98. package/src/components/stepper/stepper.tsx +122 -113
  99. package/src/components/swatch-picker/swatch-picker.tsx +120 -120
  100. package/src/components/tab-bar/tab-bar.tsx +149 -148
  101. package/src/components/tab-view/tab-view.tsx +140 -137
  102. package/src/components/table/table.tsx +117 -98
  103. package/src/components/text-editor/text-editor.tsx +34 -29
  104. package/src/components/text-field/text-field.tsx +182 -180
  105. package/src/components/toast/toast-store.ts +127 -115
  106. package/src/components/toast/toaster.tsx +361 -347
  107. package/src/components/toggle/toggle.tsx +297 -282
  108. package/src/components/tooltip/tooltip-host.tsx +157 -150
  109. package/src/components/view-controller/index.ts +1 -1
  110. package/src/components/view-controller/view-controller.tsx +64 -53
  111. package/src/components/wake-slider/wake-slider.tsx +220 -205
  112. package/src/hooks/scroll/minimize-decision.ts +11 -7
  113. package/src/hooks/use-anchored-disclosure.ts +19 -19
  114. package/src/hooks/use-anchored-placement.ts +29 -25
  115. package/src/hooks/use-dismiss.ts +33 -29
  116. package/src/hooks/use-floating-dismiss.ts +24 -22
  117. package/src/hooks/use-media-query.ts +67 -60
  118. package/src/hooks/use-minimize-on-scroll.ts +69 -60
  119. package/src/hooks/use-visual-viewport.ts +63 -63
  120. package/src/hooks/viewport/types.ts +16 -16
  121. package/src/hooks/viewport/viewport-metrics.ts +16 -10
  122. package/src/icons/icons.tsx +440 -328
  123. package/src/index.ts +91 -91
  124. package/src/lib/anchored-placement.ts +70 -65
  125. package/src/lib/class-names.ts +14 -13
  126. package/src/lib/clipboard.ts +8 -7
  127. package/src/lib/date.ts +61 -60
  128. package/src/lib/floating-layers.ts +39 -24
  129. package/src/lib/haptics.ts +10 -10
  130. package/src/lib/language.ts +9 -9
  131. package/src/lib/range-value.ts +27 -21
  132. package/src/lib/screen-edges.ts +33 -30
  133. package/src/lib/shapes.ts +38 -34
  134. package/src/machines/popover.machine.ts +51 -0
  135. package/src/motion/transitions.ts +46 -43
  136. package/src/styles/code-block.css +69 -43
  137. package/src/styles/motion-controls.css +393 -191
  138. package/src/tokens.ts +1 -1
  139. package/dist/types/src/components/popover/popover.d.ts +0 -21
  140. package/src/components/popover/popover.tsx +0 -334
@@ -1,218 +1,234 @@
1
1
  import {
2
- type CSSProperties,
3
- type InputHTMLAttributes,
4
- type Ref,
5
- useEffect,
6
- useId,
7
- useRef,
8
- } from "react"
9
- import { CheckIcon } from "../../icons/icons"
10
- import { cn } from "../../lib/class-names"
2
+ type CSSProperties,
3
+ type InputHTMLAttributes,
4
+ type Ref,
5
+ useEffect,
6
+ useId,
7
+ useRef,
8
+ } from "react";
9
+ import { CheckIcon } from "../../icons/icons";
10
+ import { cn } from "../../lib/class-names";
11
11
 
12
- export type OneTimeCodeStatus = "idle" | "verifying" | "success" | "error"
13
- export type OneTimeCodeFormat = "numeric" | "alphanumeric"
14
- export type OneTimeCodeSize = "sm" | "md" | "lg"
12
+ export type OneTimeCodeStatus = "idle" | "verifying" | "success" | "error";
13
+ export type OneTimeCodeFormat = "numeric" | "alphanumeric";
14
+ export type OneTimeCodeSize = "sm" | "md" | "lg";
15
15
 
16
16
  const checkSizes: Record<OneTimeCodeSize, number> = {
17
- sm: 20,
18
- md: 24,
19
- lg: 28,
20
- }
17
+ sm: 20,
18
+ md: 24,
19
+ lg: 28,
20
+ };
21
21
  const statusText: Record<OneTimeCodeSize, string> = {
22
- sm: "mt-1.5 text-caption2",
23
- md: "mt-2 text-footnote",
24
- lg: "mt-2.5 text-subheadline",
25
- }
22
+ sm: "mt-1.5 text-caption2",
23
+ md: "mt-2 text-footnote",
24
+ lg: "mt-2.5 text-subheadline",
25
+ };
26
26
  export interface OneTimeCodeProps
27
- extends Omit<
28
- InputHTMLAttributes<HTMLInputElement>,
29
- "value" | "defaultValue" | "onChange" | "type" | "maxLength" | "size"
30
- > {
31
- label: string
32
- value: string
33
- onChange: (value: string) => void
34
- /** Called when a complete code is entered, including paste and autofill. */
35
- onComplete?: (value: string) => void
36
- /** Number of characters, 3–8. @default 6 */
37
- length?: number
38
- /** Accepted characters. Alphanumeric codes use uppercase ASCII letters and digits. @default "numeric" */
39
- format?: OneTimeCodeFormat
40
- /** Ref to the single native input, for focus and selection. */
41
- ref?: Ref<HTMLInputElement>
42
- status?: OneTimeCodeStatus
43
- /** Localized verification feedback announced without reading decorative cells. */
44
- statusLabel?: string
45
- /** Cell height, digit text scale and check icon size. @default "md" */
46
- size?: OneTimeCodeSize
27
+ extends Omit<
28
+ InputHTMLAttributes<HTMLInputElement>,
29
+ "value" | "defaultValue" | "onChange" | "type" | "maxLength" | "size"
30
+ > {
31
+ label: string;
32
+ value: string;
33
+ onChange: (value: string) => void;
34
+ /** Called when a complete code is entered, including paste and autofill. */
35
+ onComplete?: (value: string) => void;
36
+ /** Number of characters, 3–8. @default 6 */
37
+ length?: number;
38
+ /** Accepted characters. Alphanumeric codes use uppercase ASCII letters and digits. @default "numeric" */
39
+ format?: OneTimeCodeFormat;
40
+ /** Ref to the single native input, for focus and selection. */
41
+ ref?: Ref<HTMLInputElement>;
42
+ status?: OneTimeCodeStatus;
43
+ /** Localized verification feedback announced without reading decorative cells. */
44
+ statusLabel?: string;
45
+ /** Cell height, digit text scale and check icon size. @default "md" */
46
+ size?: OneTimeCodeSize;
47
47
  }
48
48
 
49
49
  /** Normalize common numeric keyboards and optional ASCII letters in one native field. */
50
50
  export function normalizeCode(
51
- value: string,
52
- length: number,
53
- format: OneTimeCodeFormat = "numeric"
51
+ value: string,
52
+ length: number,
53
+ format: OneTimeCodeFormat = "numeric",
54
54
  ) {
55
- return Array.from(value, (digit) => {
56
- const point = digit.codePointAt(0) ?? 0
57
- for (const base of [0x30, 0x660, 0x6f0, 0xff10]) {
58
- if (point >= base && point < base + 10) return String(point - base)
59
- }
60
- return format === "alphanumeric" && /^[a-z]$/i.test(digit) ? digit.toUpperCase() : ""
61
- })
62
- .join("")
63
- .slice(0, length)
55
+ return Array.from(value, (digit) => {
56
+ const point = digit.codePointAt(0) ?? 0;
57
+ for (const base of [0x30, 0x660, 0x6f0, 0xff10]) {
58
+ if (point >= base && point < base + 10) return String(point - base);
59
+ }
60
+ return format === "alphanumeric" && /^[a-z]$/i.test(digit)
61
+ ? digit.toUpperCase()
62
+ : "";
63
+ })
64
+ .join("")
65
+ .slice(0, length);
64
66
  }
65
67
 
66
68
  /** Six cells, one field: paste, autofill, selection and Backspace stay native. */
67
69
  export function OneTimeCode({
68
- label,
69
- value,
70
- onChange,
71
- onComplete,
72
- length = 6,
73
- format = "numeric",
74
- ref,
75
- status = "idle",
76
- statusLabel,
77
- size = "md",
78
- className,
79
- disabled,
80
- id: suppliedId,
81
- ...props
70
+ label,
71
+ value,
72
+ onChange,
73
+ onComplete,
74
+ length = 6,
75
+ format = "numeric",
76
+ ref,
77
+ status = "idle",
78
+ statusLabel,
79
+ size = "md",
80
+ className,
81
+ disabled,
82
+ id: suppliedId,
83
+ ...props
82
84
  }: OneTimeCodeProps) {
83
- const generated = useId()
84
- const id = suppliedId ?? generated
85
- const filter = `otp-goo-${generated.replace(/:/g, "")}`
86
- const input = useRef<HTMLInputElement>(null)
87
- const count = Number.isFinite(length) ? Math.max(3, Math.min(8, Math.round(length))) : 6
88
- const code = normalizeCode(value, count, format)
89
- const locked = disabled || props.readOnly || status === "verifying" || status === "success"
90
- useEffect(() => {
91
- if (status === "error") input.current?.select()
92
- }, [status])
93
- const update = (next: string) => {
94
- if (locked) return
95
- const normalized = normalizeCode(next, count, format)
96
- onChange(normalized)
97
- if (normalized.length === count && normalized !== code) onComplete?.(normalized)
98
- }
99
- return (
100
- <div
101
- className={cn("passui-one-time-code not-typeset", className)}
102
- data-status={status}
103
- data-size={size}
104
- style={{ "--otp-length": count, "--otp-middle": (count - 1) / 2 } as CSSProperties}
105
- >
106
- <svg aria-hidden="true" width="0" height="0" className="absolute">
107
- <defs>
108
- <filter
109
- id={filter}
110
- x="-20%"
111
- y="-50%"
112
- width="140%"
113
- height="200%"
114
- colorInterpolationFilters="sRGB"
115
- >
116
- <feGaussianBlur stdDeviation="3" result="smear" />
117
- <feColorMatrix
118
- in="smear"
119
- type="matrix"
120
- values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 24 -10"
121
- />
122
- </filter>
123
- </defs>
124
- </svg>
125
- <div className="passui-otp-row" dir="ltr">
126
- {/* A right code fuses: cells converge on one point while a capsule
85
+ const generated = useId();
86
+ const id = suppliedId ?? generated;
87
+ const filter = `otp-goo-${generated.replace(/:/g, "")}`;
88
+ const input = useRef<HTMLInputElement>(null);
89
+ const count = Number.isFinite(length)
90
+ ? Math.max(3, Math.min(8, Math.round(length)))
91
+ : 6;
92
+ const code = normalizeCode(value, count, format);
93
+ const locked =
94
+ disabled ||
95
+ props.readOnly ||
96
+ status === "verifying" ||
97
+ status === "success";
98
+ useEffect(() => {
99
+ if (status === "error") input.current?.select();
100
+ }, [status]);
101
+ const update = (next: string) => {
102
+ if (locked) return;
103
+ const normalized = normalizeCode(next, count, format);
104
+ onChange(normalized);
105
+ if (normalized.length === count && normalized !== code)
106
+ onComplete?.(normalized);
107
+ };
108
+ return (
109
+ <div
110
+ className={cn("passui-one-time-code not-typeset", className)}
111
+ data-status={status}
112
+ data-size={size}
113
+ style={
114
+ {
115
+ "--otp-length": count,
116
+ "--otp-middle": (count - 1) / 2,
117
+ } as CSSProperties
118
+ }
119
+ >
120
+ <svg aria-hidden="true" width="0" height="0" className="absolute">
121
+ <defs>
122
+ <filter
123
+ id={filter}
124
+ x="-20%"
125
+ y="-50%"
126
+ width="140%"
127
+ height="200%"
128
+ colorInterpolationFilters="sRGB"
129
+ >
130
+ <feGaussianBlur stdDeviation="3" result="smear" />
131
+ <feColorMatrix
132
+ in="smear"
133
+ type="matrix"
134
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 24 -10"
135
+ />
136
+ </filter>
137
+ </defs>
138
+ </svg>
139
+ <div className="passui-otp-row" dir="ltr">
140
+ {/* A right code fuses: cells converge on one point while a capsule
127
141
  grows there. The filter joins the material, never the digits. */}
128
- <div
129
- aria-hidden="true"
130
- className="passui-otp-materials"
131
- style={{ filter: `url(#${filter})` }}
132
- >
133
- {Array.from({ length: count }, (_, index) => (
134
- <span
135
- key={index}
136
- className="passui-otp-cell"
137
- style={{ "--otp-index": index } as CSSProperties}
138
- />
139
- ))}
140
- <span className="passui-otp-capsule" />
141
- </div>
142
- <div aria-hidden="true" className="passui-otp-digits">
143
- {Array.from({ length: count }, (_, index) => (
144
- <span
145
- key={index}
146
- className="passui-otp-digit"
147
- data-active={index === Math.min(code.length, count - 1) || undefined}
148
- style={{ "--otp-index": index } as CSSProperties}
149
- >
150
- {code[index] && (
151
- <span key={`${index}-${code[index]}`} className="passui-otp-glyph">
152
- {code[index]}
153
- </span>
154
- )}
155
- </span>
156
- ))}
157
- </div>
158
- <CheckIcon
159
- aria-hidden="true"
160
- className="passui-otp-check"
161
- size={checkSizes[size]}
162
- />
163
- <input
164
- {...props}
165
- ref={(node) => {
166
- input.current = node
167
- if (typeof ref === "function") return ref(node)
168
- if (ref) ref.current = node
169
- }}
170
- id={id}
171
- type="text"
172
- inputMode={format === "numeric" ? "numeric" : "text"}
173
- autoComplete={props.autoComplete ?? "one-time-code"}
174
- autoCapitalize={format === "alphanumeric" ? "characters" : "off"}
175
- spellCheck={false}
176
- pattern={format === "numeric" ? "[0-9]*" : "[A-Za-z0-9]*"}
177
- maxLength={count}
178
- minLength={count}
179
- aria-label={label}
180
- value={code}
181
- disabled={disabled}
182
- readOnly={locked}
183
- aria-busy={status === "verifying" || undefined}
184
- aria-invalid={status === "error" || props["aria-invalid"]}
185
- aria-describedby={
186
- [props["aria-describedby"], statusLabel && `${id}-status`]
187
- .filter(Boolean)
188
- .join(" ") || undefined
189
- }
190
- onChange={(event) => update(event.target.value)}
191
- onPaste={(event) => {
192
- props.onPaste?.(event)
193
- if (event.defaultPrevented || locked) return
194
- const pasted = event.clipboardData.getData("text")
195
- const normalized = normalizeCode(pasted, count, format)
196
- if (normalized && normalized !== pasted) {
197
- event.preventDefault()
198
- update(normalized)
199
- }
200
- }}
201
- className="passui-otp-input"
202
- />
203
- </div>
204
- {statusLabel && (
205
- <p
206
- id={`${id}-status`}
207
- role="status"
208
- className={cn(
209
- statusText[size],
210
- status === "error" ? "text-danger" : "text-label-secondary"
211
- )}
212
- >
213
- {statusLabel}
214
- </p>
215
- )}
216
- </div>
217
- )
142
+ <div
143
+ aria-hidden="true"
144
+ className="passui-otp-materials"
145
+ style={{ filter: `url(#${filter})` }}
146
+ >
147
+ {Array.from({ length: count }, (_, index) => (
148
+ <span
149
+ key={index}
150
+ className="passui-otp-cell"
151
+ style={{ "--otp-index": index } as CSSProperties}
152
+ />
153
+ ))}
154
+ <span className="passui-otp-capsule" />
155
+ </div>
156
+ <div aria-hidden="true" className="passui-otp-digits">
157
+ {Array.from({ length: count }, (_, index) => (
158
+ <span
159
+ key={index}
160
+ className="passui-otp-digit"
161
+ data-active={
162
+ index === Math.min(code.length, count - 1) || undefined
163
+ }
164
+ style={{ "--otp-index": index } as CSSProperties}
165
+ >
166
+ {code[index] && (
167
+ <span key={code[index] ?? index} className="passui-otp-glyph">
168
+ {code[index]}
169
+ </span>
170
+ )}
171
+ </span>
172
+ ))}
173
+ </div>
174
+ <CheckIcon
175
+ aria-hidden="true"
176
+ className="passui-otp-check"
177
+ size={checkSizes[size]}
178
+ />
179
+ <input
180
+ {...props}
181
+ ref={(node) => {
182
+ input.current = node;
183
+ if (typeof ref === "function") return ref(node);
184
+ if (ref) ref.current = node;
185
+ }}
186
+ id={id}
187
+ type="text"
188
+ inputMode={format === "numeric" ? "numeric" : "text"}
189
+ autoComplete={props.autoComplete ?? "one-time-code"}
190
+ autoCapitalize={format === "alphanumeric" ? "characters" : "off"}
191
+ spellCheck={false}
192
+ pattern={format === "numeric" ? "[0-9]*" : "[A-Za-z0-9]*"}
193
+ maxLength={count}
194
+ minLength={count}
195
+ aria-label={label}
196
+ value={code}
197
+ disabled={disabled}
198
+ readOnly={locked}
199
+ aria-busy={status === "verifying" || undefined}
200
+ aria-invalid={status === "error" || props["aria-invalid"]}
201
+ aria-describedby={
202
+ [props["aria-describedby"], statusLabel && `${id}-status`]
203
+ .filter(Boolean)
204
+ .join(" ") || undefined
205
+ }
206
+ onChange={(event) => update(event.target.value)}
207
+ onPaste={(event) => {
208
+ props.onPaste?.(event);
209
+ if (event.defaultPrevented || locked) return;
210
+ const pasted = event.clipboardData.getData("text");
211
+ const normalized = normalizeCode(pasted, count, format);
212
+ if (normalized && normalized !== pasted) {
213
+ event.preventDefault();
214
+ update(normalized);
215
+ }
216
+ }}
217
+ className="passui-otp-input"
218
+ />
219
+ </div>
220
+ {statusLabel && (
221
+ <p
222
+ id={`${id}-status`}
223
+ role="status"
224
+ className={cn(
225
+ statusText[size],
226
+ status === "error" ? "text-danger" : "text-label-secondary",
227
+ )}
228
+ >
229
+ {statusLabel}
230
+ </p>
231
+ )}
232
+ </div>
233
+ );
218
234
  }
@@ -1,19 +1,22 @@
1
- import { cn } from "../../lib/class-names"
1
+ import { cn } from "../../lib/class-names";
2
2
 
3
- export type PickerSize = "sm" | "md" | "lg"
3
+ export type PickerSize = "sm" | "md" | "lg";
4
4
 
5
5
  const triggerSizes: Record<PickerSize, string> = {
6
- sm: "px-2.5 py-1.5 text-caption rounded-lg",
7
- md: "px-3 py-2 text-subheadline rounded-lg",
8
- lg: "px-4 py-3 text-body rounded-xl",
9
- }
6
+ sm: "px-2.5 py-1.5 text-caption rounded-lg",
7
+ md: "px-3 py-2 text-subheadline rounded-lg",
8
+ lg: "px-4 py-3 text-body rounded-xl",
9
+ };
10
10
 
11
11
  /** Shared field treatment for the popup-based Picker, date and time controls. */
12
- export function pickerTriggerStyle(className?: string, size: PickerSize = "md") {
13
- return cn(
14
- "flex w-full min-w-0 items-center gap-2 control-field text-start text-label",
15
- "disabled:cursor-not-allowed disabled:opacity-40",
16
- triggerSizes[size],
17
- className
18
- )
12
+ export function pickerTriggerStyle(
13
+ className?: string,
14
+ size: PickerSize = "md",
15
+ ) {
16
+ return cn(
17
+ "control-field text-label flex w-full min-w-0 items-center gap-2 text-start",
18
+ "disabled:cursor-not-allowed disabled:opacity-40",
19
+ triggerSizes[size],
20
+ className,
21
+ );
19
22
  }