@passui/react 0.0.0-stage → 0.1.15

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 (275) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -2
  3. package/package.json +96 -5
  4. package/src/appearance/index.ts +2 -0
  5. package/src/appearance/platform.tsx +123 -0
  6. package/src/appearance/theme.tsx +535 -0
  7. package/src/components/ambient/ambient.docs.tsx +68 -0
  8. package/src/components/ambient/ambient.tsx +43 -0
  9. package/src/components/ambient/examples/ambient-images.tsx +30 -0
  10. package/src/components/ambient/examples/ambient-now-playing.tsx +55 -0
  11. package/src/components/ambient/examples/ambient-showcase.tsx +33 -0
  12. package/src/components/ambient/examples/ambient-usage.tsx +19 -0
  13. package/src/components/ambient/index.ts +1 -0
  14. package/src/components/button/button-styles.ts +60 -0
  15. package/src/components/button/button.docs.tsx +116 -0
  16. package/src/components/button/button.tsx +91 -0
  17. package/src/components/button/examples/button-disabled.tsx +19 -0
  18. package/src/components/button/examples/button-icon.tsx +39 -0
  19. package/src/components/button/examples/button-link.tsx +12 -0
  20. package/src/components/button/examples/button-pending.tsx +27 -0
  21. package/src/components/button/examples/button-showcase.tsx +115 -0
  22. package/src/components/button/examples/button-sizes.tsx +12 -0
  23. package/src/components/button/examples/button-usage.tsx +12 -0
  24. package/src/components/button/examples/button-variants.tsx +15 -0
  25. package/src/components/button/examples/button-with-icon.tsx +32 -0
  26. package/src/components/button/examples/icon-button-player.tsx +38 -0
  27. package/src/components/button/examples/icon-button-showcase.tsx +83 -0
  28. package/src/components/button/examples/icon-button-usage.tsx +11 -0
  29. package/src/components/button/examples/icon-button-variants.tsx +39 -0
  30. package/src/components/button/icon-button.docs.tsx +68 -0
  31. package/src/components/button/icon-button.tsx +73 -0
  32. package/src/components/button/pending-spinner.tsx +14 -0
  33. package/src/components/button/use-pending-indicator.ts +27 -0
  34. package/src/components/chip/chip.docs.tsx +37 -0
  35. package/src/components/chip/chip.tsx +35 -0
  36. package/src/components/chip/examples/chip-in-rows.tsx +19 -0
  37. package/src/components/chip/examples/chip-showcase.tsx +51 -0
  38. package/src/components/chip/examples/chip-tones.tsx +13 -0
  39. package/src/components/chip/examples/chip-usage.tsx +5 -0
  40. package/src/components/compact-date-picker/calendar-month.ts +131 -0
  41. package/src/components/compact-date-picker/compact-date-picker.docs.tsx +81 -0
  42. package/src/components/compact-date-picker/compact-date-picker.tsx +438 -0
  43. package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +35 -0
  44. package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +22 -0
  45. package/src/components/compact-time-picker/compact-time-picker.docs.tsx +80 -0
  46. package/src/components/compact-time-picker/compact-time-picker.tsx +432 -0
  47. package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +31 -0
  48. package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +21 -0
  49. package/src/components/compact-time-picker/time-value.ts +67 -0
  50. package/src/components/content-view/content-view.docs.tsx +61 -0
  51. package/src/components/content-view/content-view.tsx +197 -0
  52. package/src/components/content-view/examples/content-view-header-action.tsx +33 -0
  53. package/src/components/content-view/examples/content-view-showcase.tsx +65 -0
  54. package/src/components/content-view/examples/content-view-stats.tsx +35 -0
  55. package/src/components/content-view/examples/content-view-usage.tsx +14 -0
  56. package/src/components/content-view/index.ts +1 -0
  57. package/src/components/context-menu/conceal-menu-source.ts +33 -0
  58. package/src/components/context-menu/context-menu-host.tsx +445 -0
  59. package/src/components/context-menu/context-menu-store.ts +165 -0
  60. package/src/components/context-menu/context-menu.docs.tsx +92 -0
  61. package/src/components/context-menu/examples/context-menu-checked.tsx +37 -0
  62. package/src/components/context-menu/examples/context-menu-list.tsx +85 -0
  63. package/src/components/context-menu/examples/context-menu-showcase.tsx +56 -0
  64. package/src/components/context-menu/examples/context-menu-toolbar.tsx +63 -0
  65. package/src/components/context-menu/examples/context-menu-usage.tsx +24 -0
  66. package/src/components/context-menu/menu-position.ts +55 -0
  67. package/src/components/context-menu/menu-reveal.ts +75 -0
  68. package/src/components/context-menu/use-context-menu.ts +90 -0
  69. package/src/components/conversation/conversation.docs.tsx +88 -0
  70. package/src/components/conversation/conversation.tsx +279 -0
  71. package/src/components/conversation/examples/conversation-composer.tsx +36 -0
  72. package/src/components/conversation/examples/conversation-days.tsx +48 -0
  73. package/src/components/conversation/examples/conversation-showcase.tsx +71 -0
  74. package/src/components/conversation/examples/conversation-usage.tsx +28 -0
  75. package/src/components/conversation/index.ts +1 -0
  76. package/src/components/date-picker/date-picker.docs.tsx +88 -0
  77. package/src/components/date-picker/date-picker.tsx +103 -0
  78. package/src/components/date-picker/date-value.ts +37 -0
  79. package/src/components/date-picker/examples/date-picker-locales.tsx +41 -0
  80. package/src/components/date-picker/examples/date-picker-past-only.tsx +29 -0
  81. package/src/components/date-picker/examples/date-picker-showcase.tsx +49 -0
  82. package/src/components/date-picker/examples/date-picker-usage.tsx +22 -0
  83. package/src/components/dialog/dialog-host.tsx +156 -0
  84. package/src/components/dialog/dialog-store.ts +56 -0
  85. package/src/components/dialog/dialog.docs.tsx +114 -0
  86. package/src/components/dialog/dialog.tsx +200 -0
  87. package/src/components/dialog/examples/dialog-confirm.tsx +24 -0
  88. package/src/components/dialog/examples/dialog-form.tsx +46 -0
  89. package/src/components/dialog/examples/dialog-inline.tsx +48 -0
  90. package/src/components/dialog/examples/dialog-showcase.tsx +53 -0
  91. package/src/components/dialog/examples/dialog-usage.tsx +31 -0
  92. package/src/components/dialog/index.ts +1 -0
  93. package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +37 -0
  94. package/src/components/labeled-content/examples/labeled-content-showcase.tsx +54 -0
  95. package/src/components/labeled-content/examples/labeled-content-usage.tsx +22 -0
  96. package/src/components/labeled-content/examples/labeled-content-values.tsx +26 -0
  97. package/src/components/labeled-content/examples/labeled-content-wide.tsx +46 -0
  98. package/src/components/labeled-content/labeled-content.docs.tsx +69 -0
  99. package/src/components/labeled-content/labeled-content.tsx +66 -0
  100. package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +30 -0
  101. package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +46 -0
  102. package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +35 -0
  103. package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +20 -0
  104. package/src/components/locale-switcher/index.ts +1 -0
  105. package/src/components/locale-switcher/locale-switcher.docs.tsx +91 -0
  106. package/src/components/locale-switcher/locale-switcher.tsx +252 -0
  107. package/src/components/picker/examples/picker-details.tsx +44 -0
  108. package/src/components/picker/examples/picker-searchable.tsx +48 -0
  109. package/src/components/picker/examples/picker-showcase.tsx +66 -0
  110. package/src/components/picker/examples/picker-usage.tsx +20 -0
  111. package/src/components/picker/picker.docs.tsx +68 -0
  112. package/src/components/picker/picker.tsx +493 -0
  113. package/src/components/popover/examples/popover-help.tsx +57 -0
  114. package/src/components/popover/examples/popover-menu.tsx +51 -0
  115. package/src/components/popover/examples/popover-showcase.tsx +61 -0
  116. package/src/components/popover/examples/popover-usage.tsx +31 -0
  117. package/src/components/popover/popover.docs.tsx +84 -0
  118. package/src/components/popover/popover.tsx +138 -0
  119. package/src/components/profile-image/examples/profile-image-fallbacks.tsx +29 -0
  120. package/src/components/profile-image/examples/profile-image-group.tsx +34 -0
  121. package/src/components/profile-image/examples/profile-image-showcase.tsx +30 -0
  122. package/src/components/profile-image/examples/profile-image-usage.tsx +14 -0
  123. package/src/components/profile-image/profile-image.docs.tsx +81 -0
  124. package/src/components/profile-image/profile-image.tsx +80 -0
  125. package/src/components/search-field/examples/search-field-filter.tsx +53 -0
  126. package/src/components/search-field/examples/search-field-showcase.tsx +56 -0
  127. package/src/components/search-field/examples/search-field-toolbar.tsx +27 -0
  128. package/src/components/search-field/examples/search-field-usage.tsx +18 -0
  129. package/src/components/search-field/search-field.docs.tsx +58 -0
  130. package/src/components/search-field/search-field.tsx +29 -0
  131. package/src/components/search-list/examples/search-list-dropdown.tsx +53 -0
  132. package/src/components/search-list/examples/search-list-groups.tsx +64 -0
  133. package/src/components/search-list/examples/search-list-showcase.tsx +67 -0
  134. package/src/components/search-list/examples/search-list-usage.tsx +27 -0
  135. package/src/components/search-list/search-list.docs.tsx +87 -0
  136. package/src/components/search-list/search-list.tsx +265 -0
  137. package/src/components/section/examples/section-actions.tsx +35 -0
  138. package/src/components/section/examples/section-groups.tsx +41 -0
  139. package/src/components/section/examples/section-showcase.tsx +63 -0
  140. package/src/components/section/examples/section-usage.tsx +19 -0
  141. package/src/components/section/section.docs.tsx +51 -0
  142. package/src/components/section/section.tsx +41 -0
  143. package/src/components/secure-field/examples/secure-field-new-password.tsx +56 -0
  144. package/src/components/secure-field/examples/secure-field-reveal.tsx +20 -0
  145. package/src/components/secure-field/examples/secure-field-showcase.tsx +62 -0
  146. package/src/components/secure-field/examples/secure-field-usage.tsx +19 -0
  147. package/src/components/secure-field/secure-field.docs.tsx +72 -0
  148. package/src/components/secure-field/secure-field.tsx +10 -0
  149. package/src/components/segmented-control/examples/segmented-control-overflow.tsx +28 -0
  150. package/src/components/segmented-control/examples/segmented-control-showcase.tsx +59 -0
  151. package/src/components/segmented-control/examples/segmented-control-usage.tsx +20 -0
  152. package/src/components/segmented-control/examples/segmented-control-views.tsx +32 -0
  153. package/src/components/segmented-control/segmented-control.docs.tsx +61 -0
  154. package/src/components/segmented-control/segmented-control.tsx +122 -0
  155. package/src/components/sheet/examples/sheet-choices.tsx +58 -0
  156. package/src/components/sheet/examples/sheet-share.tsx +51 -0
  157. package/src/components/sheet/examples/sheet-showcase.tsx +74 -0
  158. package/src/components/sheet/examples/sheet-usage.tsx +32 -0
  159. package/src/components/sheet/modal-environment.ts +113 -0
  160. package/src/components/sheet/sheet-portal.tsx +89 -0
  161. package/src/components/sheet/sheet.docs.tsx +75 -0
  162. package/src/components/sheet/sheet.tsx +112 -0
  163. package/src/components/sheet/use-sheet-focus.ts +130 -0
  164. package/src/components/sidebar/examples/sidebar-groups.tsx +133 -0
  165. package/src/components/sidebar/examples/sidebar-showcase.tsx +123 -0
  166. package/src/components/sidebar/examples/sidebar-unread.tsx +66 -0
  167. package/src/components/sidebar/examples/sidebar-usage.tsx +52 -0
  168. package/src/components/sidebar/sidebar.docs.tsx +135 -0
  169. package/src/components/sidebar/sidebar.tsx +69 -0
  170. package/src/components/stat-tile/examples/stat-tile-filters.tsx +32 -0
  171. package/src/components/stat-tile/examples/stat-tile-grid.tsx +21 -0
  172. package/src/components/stat-tile/examples/stat-tile-showcase.tsx +52 -0
  173. package/src/components/stat-tile/examples/stat-tile-usage.tsx +13 -0
  174. package/src/components/stat-tile/index.ts +1 -0
  175. package/src/components/stat-tile/stat-tile.docs.tsx +60 -0
  176. package/src/components/stat-tile/stat-tile.tsx +68 -0
  177. package/src/components/status-indicator/examples/status-indicator-live.tsx +34 -0
  178. package/src/components/status-indicator/examples/status-indicator-showcase.tsx +39 -0
  179. package/src/components/status-indicator/examples/status-indicator-states.tsx +17 -0
  180. package/src/components/status-indicator/examples/status-indicator-usage.tsx +9 -0
  181. package/src/components/status-indicator/status-indicator.docs.tsx +43 -0
  182. package/src/components/status-indicator/status-indicator.tsx +35 -0
  183. package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +42 -0
  184. package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +45 -0
  185. package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +70 -0
  186. package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +39 -0
  187. package/src/components/swatch-picker/swatch-picker.docs.tsx +60 -0
  188. package/src/components/swatch-picker/swatch-picker.tsx +96 -0
  189. package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +66 -0
  190. package/src/components/tab-bar/examples/tab-bar-minimize.tsx +71 -0
  191. package/src/components/tab-bar/examples/tab-bar-showcase.tsx +82 -0
  192. package/src/components/tab-bar/examples/tab-bar-usage.tsx +54 -0
  193. package/src/components/tab-bar/tab-bar.docs.tsx +136 -0
  194. package/src/components/tab-bar/tab-bar.tsx +148 -0
  195. package/src/components/tab-view/examples/tab-view-keyboard.tsx +39 -0
  196. package/src/components/tab-view/examples/tab-view-settings.tsx +56 -0
  197. package/src/components/tab-view/examples/tab-view-showcase.tsx +53 -0
  198. package/src/components/tab-view/examples/tab-view-usage.tsx +23 -0
  199. package/src/components/tab-view/tab-view.docs.tsx +77 -0
  200. package/src/components/tab-view/tab-view.tsx +130 -0
  201. package/src/components/table/examples/table-empty.tsx +53 -0
  202. package/src/components/table/examples/table-invoices.tsx +102 -0
  203. package/src/components/table/examples/table-selection.tsx +150 -0
  204. package/src/components/table/examples/table-showcase.tsx +115 -0
  205. package/src/components/table/examples/table-usage.tsx +67 -0
  206. package/src/components/table/examples/table-wide.tsx +75 -0
  207. package/src/components/table/table.docs.tsx +131 -0
  208. package/src/components/table/table.tsx +149 -0
  209. package/src/components/text-editor/examples/text-editor-comment.tsx +48 -0
  210. package/src/components/text-editor/examples/text-editor-count.tsx +38 -0
  211. package/src/components/text-editor/examples/text-editor-showcase.tsx +58 -0
  212. package/src/components/text-editor/examples/text-editor-usage.tsx +25 -0
  213. package/src/components/text-editor/text-editor.docs.tsx +61 -0
  214. package/src/components/text-editor/text-editor.tsx +19 -0
  215. package/src/components/text-field/examples/text-field-icons.tsx +72 -0
  216. package/src/components/text-field/examples/text-field-showcase.tsx +50 -0
  217. package/src/components/text-field/examples/text-field-usage.tsx +18 -0
  218. package/src/components/text-field/examples/text-field-validation.tsx +27 -0
  219. package/src/components/text-field/text-field.docs.tsx +118 -0
  220. package/src/components/text-field/text-field.tsx +135 -0
  221. package/src/components/toast/examples/toast-action.tsx +32 -0
  222. package/src/components/toast/examples/toast-promise.tsx +45 -0
  223. package/src/components/toast/examples/toast-showcase.tsx +49 -0
  224. package/src/components/toast/examples/toast-usage.tsx +13 -0
  225. package/src/components/toast/toast-store.ts +166 -0
  226. package/src/components/toast/toast.docs.tsx +79 -0
  227. package/src/components/toast/toaster.tsx +374 -0
  228. package/src/components/toggle/examples/toggle-checkboxes.tsx +43 -0
  229. package/src/components/toggle/examples/toggle-settings.tsx +31 -0
  230. package/src/components/toggle/examples/toggle-showcase.tsx +59 -0
  231. package/src/components/toggle/examples/toggle-usage.tsx +13 -0
  232. package/src/components/toggle/toggle.docs.tsx +69 -0
  233. package/src/components/toggle/toggle.tsx +85 -0
  234. package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +27 -0
  235. package/src/components/tooltip/examples/tooltip-shortcuts.tsx +38 -0
  236. package/src/components/tooltip/examples/tooltip-showcase.tsx +44 -0
  237. package/src/components/tooltip/examples/tooltip-usage.tsx +13 -0
  238. package/src/components/tooltip/tooltip-host.tsx +156 -0
  239. package/src/components/tooltip/tooltip.docs.tsx +56 -0
  240. package/src/components/view-controller/examples/view-controller-action.tsx +27 -0
  241. package/src/components/view-controller/examples/view-controller-settings.tsx +31 -0
  242. package/src/components/view-controller/examples/view-controller-showcase.tsx +45 -0
  243. package/src/components/view-controller/examples/view-controller-usage.tsx +19 -0
  244. package/src/components/view-controller/index.ts +1 -0
  245. package/src/components/view-controller/view-controller.docs.tsx +71 -0
  246. package/src/components/view-controller/view-controller.tsx +81 -0
  247. package/src/docs/index.ts +3 -0
  248. package/src/docs/registry.ts +113 -0
  249. package/src/docs/snippet.ts +17 -0
  250. package/src/docs/types.ts +180 -0
  251. package/src/hooks/scroll/minimize-decision.ts +13 -0
  252. package/src/hooks/use-anchored-placement.ts +43 -0
  253. package/src/hooks/use-dismiss.ts +58 -0
  254. package/src/hooks/use-media-query.ts +106 -0
  255. package/src/hooks/use-minimize-on-scroll.ts +92 -0
  256. package/src/hooks/use-visual-viewport.ts +76 -0
  257. package/src/hooks/viewport/types.ts +19 -0
  258. package/src/hooks/viewport/viewport-metrics.ts +25 -0
  259. package/src/icons/icons.tsx +298 -0
  260. package/src/index.ts +78 -0
  261. package/src/lib/anchored-placement.ts +92 -0
  262. package/src/lib/class-names.ts +21 -0
  263. package/src/lib/date.ts +96 -0
  264. package/src/lib/floating-layers.ts +28 -0
  265. package/src/lib/haptics.ts +25 -0
  266. package/src/lib/language.ts +21 -0
  267. package/src/lib/screen-edges.ts +45 -0
  268. package/src/lib/shapes.ts +70 -0
  269. package/src/motion/transitions.ts +30 -0
  270. package/src/styles/base.css +1 -0
  271. package/src/styles/styles.css +3 -0
  272. package/src/styles/theme.css +1 -0
  273. package/src/styles/typeset.css +1 -0
  274. package/src/styles/utilities.css +1 -0
  275. package/src/tokens.ts +1 -0
@@ -0,0 +1,72 @@
1
+ import {
2
+ Link01Icon,
3
+ Mail01Icon,
4
+ Search01Icon,
5
+ Tick02Icon,
6
+ } from "@hugeicons/core-free-icons"
7
+ import { HugeiconsIcon } from "@hugeicons/react"
8
+ import { TextField } from "@passui/react/text-field"
9
+ import { useState } from "react"
10
+
11
+ export function TextFieldIcons() {
12
+ const [email, setEmail] = useState("aiko@example.com")
13
+ const [site, setSite] = useState("")
14
+ const [query, setQuery] = useState("")
15
+ const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
16
+
17
+ return (
18
+ <div className="flex w-full max-w-xs flex-col gap-4">
19
+ <TextField
20
+ label="Email"
21
+ type="email"
22
+ icon={
23
+ <HugeiconsIcon
24
+ icon={Mail01Icon}
25
+ size={18}
26
+ />
27
+ }
28
+ trailingIcon={
29
+ valid && (
30
+ <HugeiconsIcon
31
+ icon={Tick02Icon}
32
+ size={18}
33
+ className="text-success"
34
+ />
35
+ )
36
+ }
37
+ value={email}
38
+ onChange={(event) =>
39
+ setEmail(event.target.value)
40
+ }
41
+ />
42
+ <TextField
43
+ label="Website"
44
+ placeholder="example.com"
45
+ icon={
46
+ <HugeiconsIcon
47
+ icon={Link01Icon}
48
+ size={18}
49
+ />
50
+ }
51
+ value={site}
52
+ onChange={(event) =>
53
+ setSite(event.target.value)
54
+ }
55
+ />
56
+ <TextField
57
+ label="Find a member"
58
+ placeholder="Name or email"
59
+ icon={
60
+ <HugeiconsIcon
61
+ icon={Search01Icon}
62
+ size={18}
63
+ />
64
+ }
65
+ value={query}
66
+ onChange={(event) =>
67
+ setQuery(event.target.value)
68
+ }
69
+ />
70
+ </div>
71
+ )
72
+ }
@@ -0,0 +1,50 @@
1
+ import { toast } from "@passui/react"
2
+ import { Button } from "@passui/react/button"
3
+ import { TextField } from "@passui/react/text-field"
4
+ import { useState } from "react"
5
+
6
+ export function TextFieldShowcase() {
7
+ const [name, setName] = useState("Aiko Kamado")
8
+ const [role, setRole] = useState("Product designer")
9
+ const [site, setSite] = useState("")
10
+
11
+ return (
12
+ <form
13
+ className="flex w-full max-w-sm flex-col gap-4 rounded-4xl bg-elevated p-6 shadow-sm ring-1 ring-separator/60"
14
+ onSubmit={(event) => {
15
+ event.preventDefault()
16
+ toast.success("Profile updated")
17
+ }}
18
+ >
19
+ <h2 className="text-headline font-semibold text-label">
20
+ Edit profile
21
+ </h2>
22
+ <TextField
23
+ label="Name"
24
+ value={name}
25
+ onChange={(event) =>
26
+ setName(event.target.value)
27
+ }
28
+ />
29
+ <TextField
30
+ label="Role"
31
+ value={role}
32
+ onChange={(event) =>
33
+ setRole(event.target.value)
34
+ }
35
+ />
36
+ <TextField
37
+ label="Website"
38
+ placeholder="example.com"
39
+ description="Shown on your public profile."
40
+ value={site}
41
+ onChange={(event) =>
42
+ setSite(event.target.value)
43
+ }
44
+ />
45
+ <Button type="submit" variant="accent">
46
+ Save profile
47
+ </Button>
48
+ </form>
49
+ )
50
+ }
@@ -0,0 +1,18 @@
1
+ import { TextField } from "@passui/react/text-field"
2
+ import { useState } from "react"
3
+
4
+ export function TextFieldUsage() {
5
+ const [name, setName] = useState("")
6
+ return (
7
+ <div className="w-full max-w-xs">
8
+ <TextField
9
+ label="Name"
10
+ placeholder="Aiko Kamado"
11
+ value={name}
12
+ onChange={(event) =>
13
+ setName(event.target.value)
14
+ }
15
+ />
16
+ </div>
17
+ )
18
+ }
@@ -0,0 +1,27 @@
1
+ import { TextField } from "@passui/react/text-field"
2
+ import { useState } from "react"
3
+
4
+ export function TextFieldValidation() {
5
+ const [email, setEmail] = useState("aiko@")
6
+ const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
7
+
8
+ return (
9
+ <div className="w-full max-w-xs">
10
+ <TextField
11
+ label="Email"
12
+ type="email"
13
+ description="We only use it to sign you in."
14
+ value={email}
15
+ aria-invalid={!valid}
16
+ errors={
17
+ valid
18
+ ? []
19
+ : ["Enter a full email address."]
20
+ }
21
+ onChange={(event) =>
22
+ setEmail(event.target.value)
23
+ }
24
+ />
25
+ </div>
26
+ )
27
+ }
@@ -0,0 +1,118 @@
1
+ import {
2
+ Mail01Icon,
3
+ Tick02Icon,
4
+ } from "@hugeicons/core-free-icons"
5
+ import { HugeiconsIcon } from "@hugeicons/react"
6
+ import { useState } from "react"
7
+ import { defineDocs } from "../../docs/types"
8
+ import { TextFieldIcons } from "./examples/text-field-icons"
9
+ import { TextFieldShowcase } from "./examples/text-field-showcase"
10
+ import { TextFieldUsage } from "./examples/text-field-usage"
11
+ import { TextFieldValidation } from "./examples/text-field-validation"
12
+ import { TextField, type TextFieldProps } from "./text-field"
13
+
14
+ // The icons are nodes, so the playground offers a switch for each and
15
+ // the snippet writes the icon itself.
16
+ type DemoProps = Pick<
17
+ TextFieldProps,
18
+ "label" | "placeholder" | "disabled"
19
+ > & {
20
+ icon?: boolean
21
+ trailingIcon?: boolean
22
+ }
23
+
24
+ function TextFieldDemo({
25
+ icon,
26
+ trailingIcon,
27
+ ...props
28
+ }: DemoProps) {
29
+ const [value, setValue] = useState("")
30
+ return (
31
+ <div className="w-full max-w-xs">
32
+ <TextField
33
+ {...props}
34
+ icon={
35
+ icon && (
36
+ <HugeiconsIcon
37
+ icon={Mail01Icon}
38
+ size={18}
39
+ />
40
+ )
41
+ }
42
+ trailingIcon={
43
+ trailingIcon && (
44
+ <HugeiconsIcon
45
+ icon={Tick02Icon}
46
+ size={18}
47
+ />
48
+ )
49
+ }
50
+ value={value}
51
+ onChange={(event) =>
52
+ setValue(event.target.value)
53
+ }
54
+ />
55
+ </div>
56
+ )
57
+ }
58
+
59
+ export const textFieldDocs = defineDocs<DemoProps>()({
60
+ slug: "text-field",
61
+ name: "TextField",
62
+ importPath: "@passui/react/text-field",
63
+ controls: {
64
+ label: {
65
+ kind: "text",
66
+ default: "Email",
67
+ required: true,
68
+ },
69
+ placeholder: {
70
+ kind: "text",
71
+ default: "you@example.com",
72
+ },
73
+ disabled: { kind: "boolean", default: false },
74
+ icon: { kind: "boolean", default: false },
75
+ trailingIcon: { kind: "boolean", default: false },
76
+ },
77
+ render: (values) => <TextFieldDemo {...values} />,
78
+ childrenSnippet: ({ icon, trailingIcon }) => ({
79
+ imports: [
80
+ 'import { useState } from "react"',
81
+ ...(icon || trailingIcon
82
+ ? [
83
+ `import { ${[icon && "Mail01Icon", trailingIcon && "Tick02Icon"].filter(Boolean).join(", ")} } from "@hugeicons/core-free-icons"`,
84
+ 'import { HugeiconsIcon } from "@hugeicons/react"',
85
+ ]
86
+ : []),
87
+ ],
88
+ setup: ['const [value, setValue] = useState("")'],
89
+ children: "",
90
+ omit: ["icon", "trailingIcon"],
91
+ expressionProps: {
92
+ ...(icon
93
+ ? {
94
+ icon: "<HugeiconsIcon icon={Mail01Icon} size={18} />",
95
+ }
96
+ : {}),
97
+ ...(trailingIcon
98
+ ? {
99
+ trailingIcon:
100
+ "<HugeiconsIcon icon={Tick02Icon} size={18} />",
101
+ }
102
+ : {}),
103
+ value: "value",
104
+ onChange:
105
+ "(event) => setValue(event.target.value)",
106
+ },
107
+ }),
108
+ usage: TextFieldUsage,
109
+ showcase: TextFieldShowcase,
110
+ examples: [
111
+ {
112
+ id: "icons",
113
+ component: TextFieldIcons,
114
+ playground: { icon: true, trailingIcon: true },
115
+ },
116
+ { id: "validation", component: TextFieldValidation },
117
+ ],
118
+ })
@@ -0,0 +1,135 @@
1
+ import {
2
+ type ComponentProps,
3
+ type ReactNode,
4
+ useId,
5
+ useState,
6
+ } from "react"
7
+ import { Button } from "../button/button"
8
+ import { cn } from "../../lib/class-names"
9
+
10
+ export const textControlClasses = cn(
11
+ "w-full min-w-0 rounded-xl border border-separator bg-elevated",
12
+ "px-3 py-2 text-body text-label placeholder:text-label-tertiary",
13
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-text",
14
+ "disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger"
15
+ )
16
+
17
+ const fieldIconClasses =
18
+ "flex shrink-0 items-center text-label-secondary"
19
+
20
+ export type TextFieldProps = ComponentProps<"input"> & {
21
+ label: string
22
+ description?: ReactNode
23
+ errors?: string[]
24
+ /** A decorative icon before the text, such as a mail glyph. */
25
+ icon?: ReactNode
26
+ /** A decorative icon after the text, such as a unit or a check. */
27
+ trailingIcon?: ReactNode
28
+ /** An interactive control after the text, such as a clear button. Size it to the field: an `xs` or `icon-xs` button, never the default. */
29
+ action?: ReactNode
30
+ revealLabels?: { show: string; hide: string }
31
+ }
32
+
33
+ export function TextField({
34
+ label,
35
+ description,
36
+ errors = [],
37
+ icon,
38
+ trailingIcon,
39
+ action,
40
+ revealLabels,
41
+ type = "text",
42
+ id: suppliedId,
43
+ className,
44
+ ...props
45
+ }: TextFieldProps) {
46
+ const generatedId = useId()
47
+ const id = suppliedId ?? generatedId
48
+ const [revealed, setRevealed] = useState(false)
49
+ const messages = Array.from(new Set(errors))
50
+ const reveal = type === "password" && revealLabels
51
+ return (
52
+ <div className="flex flex-col gap-2">
53
+ <label
54
+ htmlFor={id}
55
+ className="text-subheadline font-semibold"
56
+ >
57
+ {label}
58
+ </label>
59
+ <div className="flex items-center gap-2 rounded-xl border border-separator bg-elevated px-3 focus-within:ring-1 focus-within:ring-accent/40 has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-accent-text/70">
60
+ {icon && (
61
+ <span
62
+ aria-hidden
63
+ className={fieldIconClasses}
64
+ >
65
+ {icon}
66
+ </span>
67
+ )}
68
+ <input
69
+ {...props}
70
+ id={id}
71
+ type={reveal && revealed ? "text" : type}
72
+ aria-invalid={messages.length > 0}
73
+ aria-describedby={
74
+ messages.length || description
75
+ ? `${id}-description`
76
+ : undefined
77
+ }
78
+ className={cn(
79
+ "h-11 w-full min-w-0 bg-transparent text-body text-label outline-none",
80
+ "placeholder:text-label-tertiary disabled:opacity-40",
81
+ className
82
+ )}
83
+ />
84
+ {trailingIcon && (
85
+ <span
86
+ aria-hidden
87
+ className={fieldIconClasses}
88
+ >
89
+ {trailingIcon}
90
+ </span>
91
+ )}
92
+ {reveal && (
93
+ <Button
94
+ variant="ghost"
95
+ size="xs"
96
+ // sized to sit inside the 44px field; on touch a
97
+ // wider invisible area keeps a 44px target
98
+ className="-mr-1.5 shrink-0 whitespace-nowrap [@media(pointer:coarse)]:before:absolute [@media(pointer:coarse)]:before:-inset-1.5 [@media(pointer:coarse)]:before:content-['']"
99
+ aria-controls={id}
100
+ aria-label={
101
+ revealed
102
+ ? revealLabels.hide
103
+ : revealLabels.show
104
+ }
105
+ aria-pressed={revealed}
106
+ onClick={() => setRevealed(!revealed)}
107
+ >
108
+ {revealed
109
+ ? revealLabels.hide
110
+ : revealLabels.show}
111
+ </Button>
112
+ )}
113
+ {action}
114
+ </div>
115
+ {(messages.length > 0 || description) && (
116
+ <div
117
+ id={`${id}-description`}
118
+ role={
119
+ messages.length ? "alert" : undefined
120
+ }
121
+ className={cn(
122
+ "text-footnote",
123
+ messages.length
124
+ ? "text-danger"
125
+ : "text-label-secondary"
126
+ )}
127
+ >
128
+ {messages.length
129
+ ? messages.join(". ")
130
+ : description}
131
+ </div>
132
+ )}
133
+ </div>
134
+ )
135
+ }
@@ -0,0 +1,32 @@
1
+ import { Button } from "@passui/react/button"
2
+ import { toast } from "@passui/react/toast"
3
+ import { useState } from "react"
4
+
5
+ export function ToastAction() {
6
+ const [archived, setArchived] = useState(0)
7
+
8
+ return (
9
+ <div className="flex flex-col items-center gap-3">
10
+ <Button
11
+ variant="secondary"
12
+ onClick={() => {
13
+ setArchived((count) => count + 1)
14
+ toast("Note archived", {
15
+ action: {
16
+ label: "Undo",
17
+ onClick: () =>
18
+ setArchived(
19
+ (count) => count - 1
20
+ ),
21
+ },
22
+ })
23
+ }}
24
+ >
25
+ Archive note
26
+ </Button>
27
+ <p className="text-footnote text-label-secondary">
28
+ Archived: {archived}
29
+ </p>
30
+ </div>
31
+ )
32
+ }
@@ -0,0 +1,45 @@
1
+ import { Button } from "@passui/react/button"
2
+ import { toast } from "@passui/react/toast"
3
+
4
+ function upload(fails: boolean) {
5
+ return new Promise<string>((resolve, reject) =>
6
+ setTimeout(
7
+ () =>
8
+ fails
9
+ ? reject(new Error("offline"))
10
+ : resolve("photo.jpg"),
11
+ 1600
12
+ )
13
+ )
14
+ }
15
+
16
+ export function ToastPromise() {
17
+ return (
18
+ <div className="flex flex-wrap justify-center gap-2">
19
+ <Button
20
+ variant="secondary"
21
+ onClick={() =>
22
+ toast.promise(upload(false), {
23
+ loading: "Uploading",
24
+ success: (name) => `${name} uploaded`,
25
+ error: "Upload failed",
26
+ })
27
+ }
28
+ >
29
+ Upload
30
+ </Button>
31
+ <Button
32
+ variant="secondary"
33
+ onClick={() =>
34
+ toast.promise(upload(true), {
35
+ loading: "Uploading",
36
+ success: "Uploaded",
37
+ error: "Upload failed. Check your connection.",
38
+ })
39
+ }
40
+ >
41
+ Upload while offline
42
+ </Button>
43
+ </div>
44
+ )
45
+ }
@@ -0,0 +1,49 @@
1
+ import { LabeledContent, Section } from "@passui/react"
2
+ import { toast } from "@passui/react/toast"
3
+ import { Toggle } from "@passui/react/toggle"
4
+ import { useState } from "react"
5
+
6
+ export function ToastShowcase() {
7
+ const [backups, setBackups] = useState(true)
8
+ const [alerts, setAlerts] = useState(false)
9
+
10
+ return (
11
+ <div className="w-full max-w-sm">
12
+ <Section
13
+ title="Account"
14
+ footer="Each change saves at once and confirms with a toast."
15
+ >
16
+ <LabeledContent label="Daily backups">
17
+ <Toggle
18
+ label="Daily backups"
19
+ labelHidden
20
+ checked={backups}
21
+ onChange={(checked) => {
22
+ setBackups(checked)
23
+ toast.success(
24
+ checked
25
+ ? "Backups turned on"
26
+ : "Backups turned off"
27
+ )
28
+ }}
29
+ />
30
+ </LabeledContent>
31
+ <LabeledContent label="Sign-in alerts">
32
+ <Toggle
33
+ label="Sign-in alerts"
34
+ labelHidden
35
+ checked={alerts}
36
+ onChange={(checked) => {
37
+ setAlerts(checked)
38
+ toast(
39
+ checked
40
+ ? "We'll email you about new sign-ins"
41
+ : "Sign-in alerts turned off"
42
+ )
43
+ }}
44
+ />
45
+ </LabeledContent>
46
+ </Section>
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,13 @@
1
+ import { Button } from "@passui/react/button"
2
+ import { toast } from "@passui/react/toast"
3
+
4
+ export function ToastUsage() {
5
+ // render one <Toaster /> near the root of your app
6
+ return (
7
+ <Button
8
+ onClick={() => toast.success("Changes saved")}
9
+ >
10
+ Save
11
+ </Button>
12
+ )
13
+ }