@krizaka/ui 2.10.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/README.md +4 -2
  2. package/dist/audio-player.js +1 -1
  3. package/dist/{chunk-EUPTXKH7.js → chunk-BRKMQ6JV.js} +2 -1
  4. package/dist/chunk-FDCMBWUU.js +168 -0
  5. package/dist/chunk-FDCMBWUU.js.map +1 -0
  6. package/dist/chunk-HCNOOPZE.js +210 -0
  7. package/dist/chunk-HCNOOPZE.js.map +1 -0
  8. package/dist/{chunk-X6TW3X6Y.js → chunk-LCZXS6AK.js} +2 -2
  9. package/dist/chunk-OGJI5X4W.js +157 -0
  10. package/dist/chunk-OGJI5X4W.js.map +1 -0
  11. package/dist/combobox.js +2 -2
  12. package/dist/data-table.d.ts +284 -24
  13. package/dist/data-table.js +914 -121
  14. package/dist/data-table.js.map +1 -1
  15. package/dist/date-picker.js +1 -1
  16. package/dist/filter-bar.d.ts +12 -12
  17. package/dist/filter-bar.js +9 -145
  18. package/dist/filter-bar.js.map +1 -1
  19. package/dist/form/server.d.ts +96 -0
  20. package/dist/form/server.js +82 -0
  21. package/dist/form/server.js.map +1 -0
  22. package/dist/form.d.ts +503 -0
  23. package/dist/form.js +760 -0
  24. package/dist/form.js.map +1 -0
  25. package/dist/image-cropper.js +1 -1
  26. package/dist/media-capture.js +1 -1
  27. package/dist/native/form.d.ts +376 -0
  28. package/dist/native/form.js +650 -0
  29. package/dist/native/form.js.map +1 -0
  30. package/dist/native/list.d.ts +95 -0
  31. package/dist/native/list.js +135 -0
  32. package/dist/native/list.js.map +1 -0
  33. package/dist/native.d.ts +113 -2
  34. package/dist/native.js +618 -388
  35. package/dist/native.js.map +1 -1
  36. package/dist/search-field.js +6 -160
  37. package/dist/search-field.js.map +1 -1
  38. package/dist/tag-input.js +2 -2
  39. package/dist/video-player.js +1 -1
  40. package/dist/video-trimmer.js +1 -1
  41. package/dist/virtual.d.ts +140 -0
  42. package/dist/virtual.js +19 -0
  43. package/dist/virtual.js.map +1 -0
  44. package/package.json +111 -8
  45. package/registry/achievement.json +1 -1
  46. package/registry/action-bar.json +1 -1
  47. package/registry/alert.json +1 -1
  48. package/registry/app-shell.json +1 -1
  49. package/registry/argument-card.json +1 -1
  50. package/registry/audio-player.json +1 -1
  51. package/registry/auth-layout.json +1 -1
  52. package/registry/avatar.json +1 -1
  53. package/registry/badge.json +1 -1
  54. package/registry/button.json +1 -1
  55. package/registry/card-grid.json +1 -1
  56. package/registry/card.json +1 -1
  57. package/registry/catalogue-page.json +1 -1
  58. package/registry/changelog-page.json +1 -1
  59. package/registry/checkbox.json +89 -4
  60. package/registry/chip-link.json +1 -1
  61. package/registry/chip.json +1 -1
  62. package/registry/choice-grid.json +1 -1
  63. package/registry/code-block.json +1 -1
  64. package/registry/combobox.json +1 -1
  65. package/registry/command.json +1 -1
  66. package/registry/component-page.json +1 -1
  67. package/registry/confirm-button.json +1 -1
  68. package/registry/contact-page.json +1 -1
  69. package/registry/continue-prompt.json +1 -1
  70. package/registry/countdown.json +1 -1
  71. package/registry/cta-panel.json +1 -1
  72. package/registry/dashboard-layout.json +1 -1
  73. package/registry/data-table.json +643 -49
  74. package/registry/date-picker.json +142 -4
  75. package/registry/decision-list.json +1 -1
  76. package/registry/detail-layout.json +1 -1
  77. package/registry/dialog.json +1 -1
  78. package/registry/docs-article-page.json +1 -1
  79. package/registry/docs-hub-page.json +1 -1
  80. package/registry/docs-layout.json +1 -1
  81. package/registry/dropdown-menu.json +1 -1
  82. package/registry/empty-state.json +1 -1
  83. package/registry/error-page.json +1 -1
  84. package/registry/examples/checkbox/native/terms.d.ts +4 -0
  85. package/registry/examples/checkbox/native/terms.dark.png +0 -0
  86. package/registry/examples/checkbox/native/terms.js +26 -0
  87. package/registry/examples/checkbox/native/terms.light.png +0 -0
  88. package/registry/examples/checkbox/native/terms.tsx +28 -0
  89. package/registry/examples/data-table/details.d.ts +4 -0
  90. package/registry/examples/data-table/details.js +33 -0
  91. package/registry/examples/data-table/details.tsx +31 -0
  92. package/registry/examples/data-table/filters.d.ts +4 -0
  93. package/registry/examples/data-table/filters.js +40 -0
  94. package/registry/examples/data-table/filters.tsx +40 -0
  95. package/registry/examples/data-table/grouped.d.ts +4 -0
  96. package/registry/examples/data-table/grouped.js +24 -0
  97. package/registry/examples/data-table/grouped.tsx +23 -0
  98. package/registry/examples/data-table/paginated.d.ts +4 -0
  99. package/registry/examples/data-table/paginated.js +23 -0
  100. package/registry/examples/data-table/paginated.tsx +22 -0
  101. package/registry/examples/data-table/pinned.d.ts +4 -0
  102. package/registry/examples/data-table/pinned.js +26 -0
  103. package/registry/examples/data-table/pinned.tsx +25 -0
  104. package/registry/examples/data-table/server.d.ts +4 -0
  105. package/registry/examples/data-table/server.js +40 -0
  106. package/registry/examples/data-table/server.tsx +40 -0
  107. package/registry/examples/data-table/virtualized.js +2 -2
  108. package/registry/examples/data-table/virtualized.tsx +3 -3
  109. package/registry/examples/date-picker/native/birth-date.d.ts +4 -0
  110. package/registry/examples/date-picker/native/birth-date.dark.png +0 -0
  111. package/registry/examples/date-picker/native/birth-date.js +30 -0
  112. package/registry/examples/date-picker/native/birth-date.light.png +0 -0
  113. package/registry/examples/date-picker/native/birth-date.tsx +27 -0
  114. package/registry/examples/field/native/default.d.ts +4 -0
  115. package/registry/examples/field/native/default.dark.png +0 -0
  116. package/registry/examples/field/native/default.js +29 -0
  117. package/registry/examples/field/native/default.light.png +0 -0
  118. package/registry/examples/field/native/default.tsx +23 -0
  119. package/registry/examples/form/amount.d.ts +4 -0
  120. package/registry/examples/form/amount.js +30 -0
  121. package/registry/examples/form/amount.tsx +36 -0
  122. package/registry/examples/form/audience.d.ts +4 -0
  123. package/registry/examples/form/audience.js +29 -0
  124. package/registry/examples/form/audience.tsx +31 -0
  125. package/registry/examples/form/native/amount.d.ts +4 -0
  126. package/registry/examples/form/native/amount.dark.png +0 -0
  127. package/registry/examples/form/native/amount.js +26 -0
  128. package/registry/examples/form/native/amount.light.png +0 -0
  129. package/registry/examples/form/native/amount.tsx +30 -0
  130. package/registry/examples/form/native/audience.d.ts +4 -0
  131. package/registry/examples/form/native/audience.dark.png +0 -0
  132. package/registry/examples/form/native/audience.js +24 -0
  133. package/registry/examples/form/native/audience.light.png +0 -0
  134. package/registry/examples/form/native/audience.tsx +24 -0
  135. package/registry/examples/form/native/profile.d.ts +4 -0
  136. package/registry/examples/form/native/profile.dark.png +0 -0
  137. package/registry/examples/form/native/profile.js +33 -0
  138. package/registry/examples/form/native/profile.light.png +0 -0
  139. package/registry/examples/form/native/profile.tsx +36 -0
  140. package/registry/examples/form/native/settings.d.ts +4 -0
  141. package/registry/examples/form/native/settings.dark.png +0 -0
  142. package/registry/examples/form/native/settings.js +25 -0
  143. package/registry/examples/form/native/settings.light.png +0 -0
  144. package/registry/examples/form/native/settings.tsx +23 -0
  145. package/registry/examples/form/native/sign-up.d.ts +4 -0
  146. package/registry/examples/form/native/sign-up.dark.png +0 -0
  147. package/registry/examples/form/native/sign-up.js +38 -0
  148. package/registry/examples/form/native/sign-up.light.png +0 -0
  149. package/registry/examples/form/native/sign-up.tsx +34 -0
  150. package/registry/examples/form/native/upload.d.ts +4 -0
  151. package/registry/examples/form/native/upload.dark.png +0 -0
  152. package/registry/examples/form/native/upload.js +23 -0
  153. package/registry/examples/form/native/upload.light.png +0 -0
  154. package/registry/examples/form/native/upload.tsx +22 -0
  155. package/registry/examples/form/profile.d.ts +4 -0
  156. package/registry/examples/form/profile.js +35 -0
  157. package/registry/examples/form/profile.tsx +40 -0
  158. package/registry/examples/form/report.d.ts +4 -0
  159. package/registry/examples/form/report.js +30 -0
  160. package/registry/examples/form/report.tsx +30 -0
  161. package/registry/examples/form/settings.d.ts +4 -0
  162. package/registry/examples/form/settings.js +30 -0
  163. package/registry/examples/form/settings.tsx +31 -0
  164. package/registry/examples/form/sign-up.d.ts +4 -0
  165. package/registry/examples/form/sign-up.js +38 -0
  166. package/registry/examples/form/sign-up.tsx +37 -0
  167. package/registry/examples/form/upload.d.ts +4 -0
  168. package/registry/examples/form/upload.js +32 -0
  169. package/registry/examples/form/upload.tsx +32 -0
  170. package/registry/examples/list/native/default.d.ts +4 -0
  171. package/registry/examples/list/native/default.dark.png +0 -0
  172. package/registry/examples/list/native/default.js +33 -0
  173. package/registry/examples/list/native/default.light.png +0 -0
  174. package/registry/examples/list/native/default.tsx +31 -0
  175. package/registry/examples/list/native/infinite.d.ts +4 -0
  176. package/registry/examples/list/native/infinite.dark.png +0 -0
  177. package/registry/examples/list/native/infinite.js +30 -0
  178. package/registry/examples/list/native/infinite.light.png +0 -0
  179. package/registry/examples/list/native/infinite.tsx +35 -0
  180. package/registry/examples/list/native/states.d.ts +4 -0
  181. package/registry/examples/list/native/states.dark.png +0 -0
  182. package/registry/examples/list/native/states.js +26 -0
  183. package/registry/examples/list/native/states.light.png +0 -0
  184. package/registry/examples/list/native/states.tsx +21 -0
  185. package/registry/examples/virtual/feed.d.ts +4 -0
  186. package/registry/examples/virtual/feed.js +26 -0
  187. package/registry/examples/virtual/feed.tsx +24 -0
  188. package/registry/examples/virtual/gallery.d.ts +4 -0
  189. package/registry/examples/virtual/gallery.js +31 -0
  190. package/registry/examples/virtual/gallery.tsx +28 -0
  191. package/registry/examples/virtual/messages.d.ts +4 -0
  192. package/registry/examples/virtual/messages.js +28 -0
  193. package/registry/examples/virtual/messages.tsx +28 -0
  194. package/registry/field.json +104 -4
  195. package/registry/file-upload.json +1 -1
  196. package/registry/filter-bar.json +1 -1
  197. package/registry/form.json +2940 -0
  198. package/registry/gallery-page.json +1 -1
  199. package/registry/glow-card.json +1 -1
  200. package/registry/goal-gauge.json +1 -1
  201. package/registry/how-it-works-page.json +1 -1
  202. package/registry/image-cropper.json +1 -1
  203. package/registry/index.json +86 -11
  204. package/registry/iso.json +1 -1
  205. package/registry/kbd.json +1 -1
  206. package/registry/landing-page.json +1 -1
  207. package/registry/landscape.json +1 -1
  208. package/registry/legal-page.json +1 -1
  209. package/registry/link-card.json +1 -1
  210. package/registry/list.json +249 -0
  211. package/registry/loader.json +1 -1
  212. package/registry/longform-page.json +1 -1
  213. package/registry/marketing-layout.json +1 -1
  214. package/registry/marquee.json +1 -1
  215. package/registry/mascot-quote.json +1 -1
  216. package/registry/mascot-state.json +2 -2
  217. package/registry/mascots.json +2 -2
  218. package/registry/media-capture.json +1 -1
  219. package/registry/meter.json +1 -1
  220. package/registry/nav-bar.json +1 -1
  221. package/registry/number-ticker.json +1 -1
  222. package/registry/page-header.json +1 -1
  223. package/registry/page-hero.json +1 -1
  224. package/registry/page.json +1 -1
  225. package/registry/pagination.json +1 -1
  226. package/registry/pointer-light.json +1 -1
  227. package/registry/popover.json +1 -1
  228. package/registry/pricing-page.json +1 -1
  229. package/registry/product-page.json +1 -1
  230. package/registry/profile-card.json +1 -1
  231. package/registry/progress.json +1 -1
  232. package/registry/promise-card.json +1 -1
  233. package/registry/radio-group.json +1 -1
  234. package/registry/scroll-stage.json +1 -1
  235. package/registry/search-field.json +1 -1
  236. package/registry/section-backdrop.json +1 -1
  237. package/registry/separator.json +1 -1
  238. package/registry/settings-layout.json +1 -1
  239. package/registry/skeleton.json +1 -1
  240. package/registry/slider.json +1 -1
  241. package/registry/slot.json +1 -1
  242. package/registry/spinner.json +1 -1
  243. package/registry/split-view.json +1 -1
  244. package/registry/stage-banner.json +1 -1
  245. package/registry/stat.json +1 -1
  246. package/registry/status-page.json +1 -1
  247. package/registry/stepper.json +1 -1
  248. package/registry/switch.json +1 -1
  249. package/registry/tabs.json +1 -1
  250. package/registry/tag-input.json +1 -1
  251. package/registry/terminal-replay.json +1 -1
  252. package/registry/theme.json +1 -1
  253. package/registry/toast.json +1 -1
  254. package/registry/toc.json +1 -1
  255. package/registry/tooltip.json +1 -1
  256. package/registry/video-player.json +1 -1
  257. package/registry/video-trimmer.json +1 -1
  258. package/registry/virtual.json +569 -0
  259. package/skills/data-table/SKILL.md +56 -0
  260. package/skills/form/SKILL.md +44 -0
  261. package/skills/native-list/SKILL.md +39 -0
  262. package/skills/virtual/SKILL.md +35 -0
  263. /package/dist/{chunk-EUPTXKH7.js.map → chunk-BRKMQ6JV.js.map} +0 -0
  264. /package/dist/{chunk-X6TW3X6Y.js.map → chunk-LCZXS6AK.js.map} +0 -0
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/native/sign-up.tsx
4
+ import { KrizakaApiError } from "@krizaka/query";
5
+ import { createKrizakaForm, latestBirthDate } from "@krizaka/ui/native/form";
6
+ import { z } from "zod";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var signUp = createKrizakaForm({
9
+ schema: z.object({
10
+ email: z.email(),
11
+ username: z.string().min(3).max(30).regex(/^[a-z0-9_]+$/, "signUp.username.format"),
12
+ password: z.string().min(8),
13
+ dateOfBirth: z.iso.date().refine((d) => d <= latestBirthDate(18), "form.errors.UNDERAGE"),
14
+ acceptTerms: z.boolean().refine(Boolean, "signUp.terms.required")
15
+ }),
16
+ defaultValues: { email: "", username: "", password: "", dateOfBirth: "", acceptTerms: false }
17
+ });
18
+ var WORDS = { "signUp.username.format": "Lowercase letters, digits and _ only.", "signUp.username.taken": "This username is taken.", "signUp.terms.required": "Accept the terms to continue.", "form.errors.UNDERAGE": "You must be 18 or older.", "form.errors.BIRTH_DATE_INVALID": "Enter your real date of birth.", "form.errors.USERNAME_RESERVED": "This username is reserved.", "apiErrors.ACCOUNT_EXISTS": "This e-mail already has an account. Sign in instead." };
19
+ var taken = (u) => new Promise((done) => setTimeout(() => done(["eric", "elena"].includes(u)), 300));
20
+ var register = async (v) => {
21
+ if (v.username === "admin") throw new KrizakaApiError({ status: 400, code: "USERNAME_RESERVED" });
22
+ if (v.email.endsWith("@orochia.com")) throw new KrizakaApiError({ status: 409, code: "ACCOUNT_EXISTS" });
23
+ };
24
+ function NativeFormSignUp() {
25
+ const form = signUp.useForm({ i18n: { t: (key) => WORDS[key], locale: "en-GB" }, codeFields: { UNDERAGE: "dateOfBirth", BIRTH_DATE_INVALID: "dateOfBirth", USERNAME_RESERVED: "username" }, onSubmit: register });
26
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Create your account", style: { width: 312 }, children: [
27
+ /* @__PURE__ */ jsx(form.TextField, { name: "email", label: "E-mail", keyboardType: "email-address", autoComplete: "email", autoCapitalize: "none", textContentType: "emailAddress" }),
28
+ /* @__PURE__ */ jsx(form.TextField, { name: "username", label: "Username", autoCapitalize: "none", autoComplete: "username", transform: (v) => v.toLowerCase().replace(/\s+/g, "_"), availableHint: "Free", validateAsync: async (u) => await taken(u) ? { key: "signUp.username.taken", suggestion: { label: `Use @${u}_${u.length}`, value: `${u}_${u.length}` } } : null }),
29
+ /* @__PURE__ */ jsx(form.TextField, { name: "password", label: "Password", secureTextEntry: true, autoComplete: "new-password", textContentType: "newPassword", hint: "At least 8 characters." }),
30
+ /* @__PURE__ */ jsx(form.DateField, { name: "dateOfBirth", label: "Date of birth", autoComplete: "bday", hint: "Private, never shown." }),
31
+ /* @__PURE__ */ jsx(form.Checkbox, { name: "acceptTerms", label: "I accept the terms and the privacy policy" }),
32
+ /* @__PURE__ */ jsx(form.FormError, {}),
33
+ /* @__PURE__ */ jsx(form.Submit, { label: "Create my account", brand: "orochia" })
34
+ ] });
35
+ }
36
+ export {
37
+ NativeFormSignUp as default
38
+ };
@@ -0,0 +1,34 @@
1
+ import { KrizakaApiError } from "@krizaka/query";
2
+ import { createKrizakaForm, latestBirthDate } from "@krizaka/ui/native/form";
3
+ import { z } from "zod";
4
+
5
+ // The same schema as the web form (a product keeps it in packages/core): the same rules, the same keys.
6
+ const signUp = createKrizakaForm({
7
+ schema: z.object({
8
+ email: z.email(),
9
+ username: z.string().min(3).max(30).regex(/^[a-z0-9_]+$/, "signUp.username.format"),
10
+ password: z.string().min(8),
11
+ dateOfBirth: z.iso.date().refine((d) => d <= latestBirthDate(18), "form.errors.UNDERAGE"),
12
+ acceptTerms: z.boolean().refine(Boolean, "signUp.terms.required"),
13
+ }),
14
+ defaultValues: { email: "", username: "", password: "", dateOfBirth: "", acceptTerms: false },
15
+ });
16
+ const WORDS: Record<string, string> = { "signUp.username.format": "Lowercase letters, digits and _ only.", "signUp.username.taken": "This username is taken.", "signUp.terms.required": "Accept the terms to continue.", "form.errors.UNDERAGE": "You must be 18 or older.", "form.errors.BIRTH_DATE_INVALID": "Enter your real date of birth.", "form.errors.USERNAME_RESERVED": "This username is reserved.", "apiErrors.ACCOUNT_EXISTS": "This e-mail already has an account. Sign in instead." };
17
+ const taken = (u: string) => new Promise<boolean>((done) => setTimeout(() => done(["eric", "elena"].includes(u)), 300));
18
+ const register = async (v: { email: string; username: string }) => { if (v.username === "admin") throw new KrizakaApiError({ status: 400, code: "USERNAME_RESERVED" }); if (v.email.endsWith("@orochia.com")) throw new KrizakaApiError({ status: 409, code: "ACCOUNT_EXISTS" }); };
19
+
20
+ // Sign-up in the app: the return key walks the fields, the username is checked as typed, the 18+ birth date is day · month · year.
21
+ export default function NativeFormSignUp() {
22
+ const form = signUp.useForm({ i18n: { t: (key) => WORDS[key], locale: "en-GB" }, codeFields: { UNDERAGE: "dateOfBirth", BIRTH_DATE_INVALID: "dateOfBirth", USERNAME_RESERVED: "username" }, onSubmit: register });
23
+ return (
24
+ <form.Form aria-label="Create your account" style={{ width: 312 }}>
25
+ <form.TextField name="email" label="E-mail" keyboardType="email-address" autoComplete="email" autoCapitalize="none" textContentType="emailAddress" />
26
+ <form.TextField name="username" label="Username" autoCapitalize="none" autoComplete="username" transform={(v) => v.toLowerCase().replace(/\s+/g, "_")} availableHint="Free" validateAsync={async (u) => ((await taken(u)) ? { key: "signUp.username.taken", suggestion: { label: `Use @${u}_${u.length}`, value: `${u}_${u.length}` } } : null)} />
27
+ <form.TextField name="password" label="Password" secureTextEntry autoComplete="new-password" textContentType="newPassword" hint="At least 8 characters." />
28
+ <form.DateField name="dateOfBirth" label="Date of birth" autoComplete="bday" hint="Private, never shown." />
29
+ <form.Checkbox name="acceptTerms" label="I accept the terms and the privacy policy" />
30
+ <form.FormError />
31
+ <form.Submit label="Create my account" brand="orochia" />
32
+ </form.Form>
33
+ );
34
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,23 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/native/upload.tsx
4
+ import { createKrizakaForm } from "@krizaka/ui/native/form";
5
+ import { z } from "zod";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var upload = createKrizakaForm({
8
+ schema: z.object({ title: z.string().min(3).max(100), description: z.string().max(2e3), visibility: z.enum(["public", "followers", "private"]) }),
9
+ defaultValues: { title: "Sunrise over Rysy", description: "", visibility: "public" }
10
+ });
11
+ var VISIBILITY = [{ value: "public", label: "Everyone" }, { value: "followers", label: "Followers" }, { value: "private", label: "Only me" }];
12
+ function NativeFormUpload() {
13
+ const form = upload.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 600)) });
14
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Publish a video", style: { width: 312 }, children: [
15
+ /* @__PURE__ */ jsx(form.TextField, { name: "title", label: "Title", maxLength: 100 }),
16
+ /* @__PURE__ */ jsx(form.TextArea, { name: "description", label: "Description", placeholder: "What happens in it?" }),
17
+ /* @__PURE__ */ jsx(form.RadioGroup, { name: "visibility", label: "Who can watch", options: VISIBILITY }),
18
+ /* @__PURE__ */ jsx(form.Submit, { label: "Publish" })
19
+ ] });
20
+ }
21
+ export {
22
+ NativeFormUpload as default
23
+ };
@@ -0,0 +1,22 @@
1
+ import { createKrizakaForm } from "@krizaka/ui/native/form";
2
+ import { z } from "zod";
3
+
4
+ // What describes a video picked from the library (the picker is the app's: expo-image-picker): title, description, audience.
5
+ const upload = createKrizakaForm({
6
+ schema: z.object({ title: z.string().min(3).max(100), description: z.string().max(2000), visibility: z.enum(["public", "followers", "private"]) }),
7
+ defaultValues: { title: "Sunrise over Rysy", description: "", visibility: "public" as "public" | "followers" | "private" },
8
+ });
9
+ const VISIBILITY = [{ value: "public", label: "Everyone" }, { value: "followers", label: "Followers" }, { value: "private", label: "Only me" }];
10
+
11
+ // The details of an upload, then the button that publishes them with the file.
12
+ export default function NativeFormUpload() {
13
+ const form = upload.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 600)) });
14
+ return (
15
+ <form.Form aria-label="Publish a video" style={{ width: 312 }}>
16
+ <form.TextField name="title" label="Title" maxLength={100} />
17
+ <form.TextArea name="description" label="Description" placeholder="What happens in it?" />
18
+ <form.RadioGroup name="visibility" label="Who can watch" options={VISIBILITY} />
19
+ <form.Submit label="Publish" />
20
+ </form.Form>
21
+ );
22
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,35 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/profile.tsx
4
+ import { KrizakaQueryProvider, useMutation } from "@krizaka/query";
5
+ import { Button } from "@krizaka/ui/button";
6
+ import { createKrizakaForm } from "@krizaka/ui/form";
7
+ import { z } from "zod";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var profile = createKrizakaForm({
10
+ schema: z.object({ displayName: z.string().min(2).max(40), bio: z.string().max(160), categories: z.array(z.string()).min(1).max(3), tags: z.array(z.string()).max(10), links: z.array(z.object({ url: z.url() })).max(3) }),
11
+ defaultValues: { displayName: "Elena Varga", bio: "Night skies over the Tatras.", categories: ["travel"], tags: ["astro", "mountains"], links: [{ url: "https://elena.example" }] }
12
+ });
13
+ var CATEGORIES = [{ value: "travel", label: "Travel" }, { value: "music", label: "Music" }, { value: "science", label: "Science" }, { value: "sport", label: "Sport" }];
14
+ function ProfileForm() {
15
+ const mutation = useMutation({ mutationFn: (values) => new Promise((done) => setTimeout(() => done(values), 400)) });
16
+ const form = profile.useForm({ onSubmit: (values) => mutation.mutateAsync(values) });
17
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Your profile", className: "flex w-96 flex-col gap-4", children: [
18
+ /* @__PURE__ */ jsx(form.TextField, { name: "displayName", label: "Display name", autoComplete: "name" }),
19
+ /* @__PURE__ */ jsx(form.TextArea, { name: "bio", label: "Bio", rows: 3, maxLength: 160, hint: "Up to 160 characters." }),
20
+ /* @__PURE__ */ jsx(form.Combobox, { name: "categories", label: "Categories", multiple: true, options: CATEGORIES, hint: "Up to three." }),
21
+ /* @__PURE__ */ jsx(form.TagInput, { name: "tags", label: "Tags", max: 10, normalize: (t) => t.toLowerCase().replace(/^#/, "") }),
22
+ /* @__PURE__ */ jsx(form.FieldArray, { name: "links", children: (links) => /* @__PURE__ */ jsxs("fieldset", { className: "flex flex-col gap-2", children: [
23
+ /* @__PURE__ */ jsx("legend", { className: "text-xs font-semibold text-fg-secondary", children: "Links" }),
24
+ links.items.map((_, i) => /* @__PURE__ */ jsx(form.TextField, { name: `links[${i}].url`, label: `Link ${i + 1}`, type: "url" }, i)),
25
+ links.items.length < 3 ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: "ghost", onClick: () => links.push({ url: "" }), children: "Add a link" }) : null
26
+ ] }) }),
27
+ /* @__PURE__ */ jsx(form.Submit, { children: "Save the profile" })
28
+ ] });
29
+ }
30
+ function FormProfile() {
31
+ return /* @__PURE__ */ jsx(KrizakaQueryProvider, { children: /* @__PURE__ */ jsx(ProfileForm, {}) });
32
+ }
33
+ export {
34
+ FormProfile as default
35
+ };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+
3
+ import { KrizakaQueryProvider, useMutation } from "@krizaka/query";
4
+ import { Button } from "@krizaka/ui/button";
5
+ import { createKrizakaForm } from "@krizaka/ui/form";
6
+ import { z } from "zod";
7
+
8
+ const profile = createKrizakaForm({
9
+ schema: z.object({ displayName: z.string().min(2).max(40), bio: z.string().max(160), categories: z.array(z.string()).min(1).max(3), tags: z.array(z.string()).max(10), links: z.array(z.object({ url: z.url() })).max(3) }),
10
+ defaultValues: { displayName: "Elena Varga", bio: "Night skies over the Tatras.", categories: ["travel"], tags: ["astro", "mountains"], links: [{ url: "https://elena.example" }] },
11
+ });
12
+ const CATEGORIES = [{ value: "travel", label: "Travel" }, { value: "music", label: "Music" }, { value: "science", label: "Science" }, { value: "sport", label: "Sport" }];
13
+
14
+ function ProfileForm() {
15
+ const mutation = useMutation({ mutationFn: (values: unknown) => new Promise((done) => setTimeout(() => done(values), 400)) });
16
+ const form = profile.useForm({ onSubmit: (values) => mutation.mutateAsync(values) });
17
+ return (
18
+ <form.Form aria-label="Your profile" className="flex w-96 flex-col gap-4">
19
+ <form.TextField name="displayName" label="Display name" autoComplete="name" />
20
+ <form.TextArea name="bio" label="Bio" rows={3} maxLength={160} hint="Up to 160 characters." />
21
+ <form.Combobox name="categories" label="Categories" multiple options={CATEGORIES} hint="Up to three." />
22
+ <form.TagInput name="tags" label="Tags" max={10} normalize={(t) => t.toLowerCase().replace(/^#/, "")} />
23
+ <form.FieldArray name="links">
24
+ {(links) => (
25
+ <fieldset className="flex flex-col gap-2">
26
+ <legend className="text-xs font-semibold text-fg-secondary">Links</legend>
27
+ {links.items.map((_, i) => <form.TextField key={i} name={`links[${i}].url`} label={`Link ${i + 1}`} type="url" />)}
28
+ {links.items.length < 3 ? <Button type="button" size="sm" variant="ghost" onClick={() => links.push({ url: "" })}>Add a link</Button> : null}
29
+ </fieldset>
30
+ )}
31
+ </form.FieldArray>
32
+ <form.Submit>Save the profile</form.Submit>
33
+ </form.Form>
34
+ );
35
+ }
36
+
37
+ // A profile saved by a mutation of @krizaka/query: categories, tags, a list of links that grows.
38
+ export default function FormProfile() {
39
+ return <KrizakaQueryProvider><ProfileForm /></KrizakaQueryProvider>;
40
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/report.tsx
4
+ import { createKrizakaForm } from "@krizaka/ui/form";
5
+ import { z } from "zod";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var report = createKrizakaForm({
8
+ schema: z.object({ reason: z.enum(["spam", "harassment", "minor", "copyright", "other"], { message: "report.reason.required" }), details: z.string().max(500), block: z.boolean() }),
9
+ defaultValues: { reason: "", details: "", block: false },
10
+ i18n: { t: (key) => key === "report.reason.required" ? "Choose what is wrong." : void 0 }
11
+ });
12
+ var REASONS = [
13
+ { value: "spam", label: "Spam or a scam" },
14
+ { value: "harassment", label: "Harassment" },
15
+ { value: "minor", label: "It may involve a minor" },
16
+ { value: "copyright", label: "It uses my work" },
17
+ { value: "other", label: "Something else" }
18
+ ];
19
+ function FormReport() {
20
+ const form = report.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 400)) });
21
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Report this video", className: "flex w-80 flex-col gap-4", children: [
22
+ /* @__PURE__ */ jsx(form.RadioGroup, { name: "reason", label: "What is wrong?", options: REASONS }),
23
+ /* @__PURE__ */ jsx(form.TextArea, { name: "details", label: "Details", hint: "Optional. What should our team look at?", rows: 3, maxLength: 500 }),
24
+ /* @__PURE__ */ jsx(form.Checkbox, { name: "block", label: "Also block this account" }),
25
+ /* @__PURE__ */ jsx(form.Submit, { variant: "danger", children: "Send the report" })
26
+ ] });
27
+ }
28
+ export {
29
+ FormReport as default
30
+ };
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import { createKrizakaForm } from "@krizaka/ui/form";
4
+ import { z } from "zod";
5
+
6
+ const report = createKrizakaForm({
7
+ schema: z.object({ reason: z.enum(["spam", "harassment", "minor", "copyright", "other"], { message: "report.reason.required" }), details: z.string().max(500), block: z.boolean() }),
8
+ defaultValues: { reason: "" as unknown as "spam", details: "", block: false },
9
+ i18n: { t: (key) => (key === "report.reason.required" ? "Choose what is wrong." : undefined) },
10
+ });
11
+ const REASONS = [
12
+ { value: "spam", label: "Spam or a scam" },
13
+ { value: "harassment", label: "Harassment" },
14
+ { value: "minor", label: "It may involve a minor" },
15
+ { value: "copyright", label: "It uses my work" },
16
+ { value: "other", label: "Something else" },
17
+ ];
18
+
19
+ // A report: one reason among a few, details, and blocking the account in the same step.
20
+ export default function FormReport() {
21
+ const form = report.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 400)) });
22
+ return (
23
+ <form.Form aria-label="Report this video" className="flex w-80 flex-col gap-4">
24
+ <form.RadioGroup name="reason" label="What is wrong?" options={REASONS} />
25
+ <form.TextArea name="details" label="Details" hint="Optional. What should our team look at?" rows={3} maxLength={500} />
26
+ <form.Checkbox name="block" label="Also block this account" />
27
+ <form.Submit variant="danger">Send the report</form.Submit>
28
+ </form.Form>
29
+ );
30
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/settings.tsx
4
+ import { createKrizakaForm } from "@krizaka/ui/form";
5
+ import { z } from "zod";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var settings = createKrizakaForm({
8
+ schema: z.object({ language: z.enum(["en", "fr", "es"]), emailDigest: z.boolean(), pushMessages: z.boolean(), autoplayVolume: z.number().min(0).max(100), quality: z.enum(["auto", "data-saver", "best"]) }),
9
+ defaultValues: { language: "en", emailDigest: true, pushMessages: false, autoplayVolume: 40, quality: "auto" }
10
+ });
11
+ var LANGUAGES = [{ value: "en", label: "English" }, { value: "fr", label: "Fran\xE7ais" }, { value: "es", label: "Espa\xF1ol" }];
12
+ var QUALITY = [{ value: "auto", label: "Automatic" }, { value: "data-saver", label: "Data saver" }, { value: "best", label: "Best" }];
13
+ async function saveSettings() {
14
+ await new Promise((done) => setTimeout(done, 400));
15
+ return { ok: true };
16
+ }
17
+ function FormSettings() {
18
+ const form = settings.useFormWithAction(saveSettings);
19
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Settings", className: "flex w-96 flex-col gap-5", children: [
20
+ /* @__PURE__ */ jsx(form.Select, { name: "language", label: "Language", options: LANGUAGES }),
21
+ /* @__PURE__ */ jsx(form.Switch, { name: "emailDigest", label: "Weekly e-mail", hint: "What happened on your channel." }),
22
+ /* @__PURE__ */ jsx(form.Switch, { name: "pushMessages", label: "Message notifications" }),
23
+ /* @__PURE__ */ jsx(form.Slider, { name: "autoplayVolume", label: "Autoplay volume", step: 10, formatValue: (v) => `${v} %` }),
24
+ /* @__PURE__ */ jsx(form.RadioGroup, { name: "quality", label: "Video quality", options: QUALITY, orientation: "horizontal" }),
25
+ /* @__PURE__ */ jsx(form.Submit, { children: "Save the settings" })
26
+ ] });
27
+ }
28
+ export {
29
+ FormSettings as default
30
+ };
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import { createKrizakaForm, type FormActionResult } from "@krizaka/ui/form";
4
+ import { z } from "zod";
5
+
6
+ const settings = createKrizakaForm({
7
+ schema: z.object({ language: z.enum(["en", "fr", "es"]), emailDigest: z.boolean(), pushMessages: z.boolean(), autoplayVolume: z.number().min(0).max(100), quality: z.enum(["auto", "data-saver", "best"]) }),
8
+ defaultValues: { language: "en" as "en" | "fr" | "es", emailDigest: true, pushMessages: false, autoplayVolume: 40, quality: "auto" as "auto" | "data-saver" | "best" },
9
+ });
10
+ const LANGUAGES = [{ value: "en", label: "English" }, { value: "fr", label: "Français" }, { value: "es", label: "Español" }];
11
+ const QUALITY = [{ value: "auto", label: "Automatic" }, { value: "data-saver", label: "Data saver" }, { value: "best", label: "Best" }];
12
+ // What a Server Action answers (`"use server"` in the app): done, or a refusal the form shows.
13
+ async function saveSettings(): Promise<FormActionResult> {
14
+ await new Promise((done) => setTimeout(done, 400));
15
+ return { ok: true };
16
+ }
17
+
18
+ // Settings sent by a Server Action (`useFormWithAction`): switches, a menu, a slider, a choice.
19
+ export default function FormSettings() {
20
+ const form = settings.useFormWithAction(saveSettings);
21
+ return (
22
+ <form.Form aria-label="Settings" className="flex w-96 flex-col gap-5">
23
+ <form.Select name="language" label="Language" options={LANGUAGES} />
24
+ <form.Switch name="emailDigest" label="Weekly e-mail" hint="What happened on your channel." />
25
+ <form.Switch name="pushMessages" label="Message notifications" />
26
+ <form.Slider name="autoplayVolume" label="Autoplay volume" step={10} formatValue={(v) => `${v} %`} />
27
+ <form.RadioGroup name="quality" label="Video quality" options={QUALITY} orientation="horizontal" />
28
+ <form.Submit>Save the settings</form.Submit>
29
+ </form.Form>
30
+ );
31
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/sign-up.tsx
4
+ import { KrizakaApiError } from "@krizaka/query";
5
+ import { createKrizakaForm, latestBirthDate } from "@krizaka/ui/form";
6
+ import { z } from "zod";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var signUp = createKrizakaForm({
9
+ schema: z.object({
10
+ email: z.email(),
11
+ username: z.string().min(3).max(30).regex(/^[a-z0-9_]+$/, "signUp.username.format"),
12
+ password: z.string().min(8),
13
+ dateOfBirth: z.iso.date().refine((d) => d <= latestBirthDate(18), "form.errors.UNDERAGE"),
14
+ acceptTerms: z.boolean().refine(Boolean, "signUp.terms.required")
15
+ }),
16
+ defaultValues: { email: "", username: "", password: "", dateOfBirth: "", acceptTerms: false }
17
+ });
18
+ var WORDS = { "signUp.username.format": "Lowercase letters, digits and _ only.", "signUp.username.taken": "This username is taken.", "signUp.terms.required": "Accept the terms to continue.", "form.errors.UNDERAGE": "You must be 18 or older.", "form.errors.BIRTH_DATE_INVALID": "Enter your real date of birth.", "form.errors.USERNAME_RESERVED": "This username is reserved.", "apiErrors.ACCOUNT_EXISTS": "This e-mail already has an account. Sign in instead." };
19
+ var taken = (u) => new Promise((done) => setTimeout(() => done(["eric", "elena"].includes(u)), 300));
20
+ var register = async (v) => {
21
+ if (v.username === "admin") throw new KrizakaApiError({ status: 400, code: "USERNAME_RESERVED" });
22
+ if (v.email.endsWith("@orochia.com")) throw new KrizakaApiError({ status: 409, code: "ACCOUNT_EXISTS" });
23
+ };
24
+ function FormSignUp() {
25
+ const form = signUp.useForm({ i18n: { t: (key) => WORDS[key] }, codeFields: { UNDERAGE: "dateOfBirth", BIRTH_DATE_INVALID: "dateOfBirth", USERNAME_RESERVED: "username" }, onSubmit: register });
26
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Create your account", className: "flex w-80 flex-col gap-4", children: [
27
+ /* @__PURE__ */ jsx(form.TextField, { name: "email", label: "E-mail", type: "email", autoComplete: "email", hint: "We send a link to confirm it." }),
28
+ /* @__PURE__ */ jsx(form.TextField, { name: "username", label: "Username", start: "@", autoComplete: "username", transform: (v) => v.toLowerCase().replace(/\s+/g, "_"), availableHint: "Free", validateAsync: async (u) => await taken(u) ? { key: "signUp.username.taken", suggestion: { label: `Use @${u}_${u.length}`, value: `${u}_${u.length}` } } : null }),
29
+ /* @__PURE__ */ jsx(form.TextField, { name: "password", label: "Password", type: "password", autoComplete: "new-password", hint: "At least 8 characters." }),
30
+ /* @__PURE__ */ jsx(form.DateField, { name: "dateOfBirth", label: "Date of birth", entry: "parts", autoComplete: "bday", hint: "Private, never shown." }),
31
+ /* @__PURE__ */ jsx(form.Checkbox, { name: "acceptTerms", label: "I accept the terms and the privacy policy" }),
32
+ /* @__PURE__ */ jsx(form.FormError, {}),
33
+ /* @__PURE__ */ jsx(form.Submit, { children: "Create my account" })
34
+ ] });
35
+ }
36
+ export {
37
+ FormSignUp as default
38
+ };
@@ -0,0 +1,37 @@
1
+ "use client";
2
+
3
+ import { KrizakaApiError } from "@krizaka/query";
4
+ import { createKrizakaForm, latestBirthDate } from "@krizaka/ui/form";
5
+ import { z } from "zod";
6
+
7
+ // The schema the sign-up API parses with: the same rules, the same keys. `signUp.*` are the app's own words.
8
+ const signUp = createKrizakaForm({
9
+ schema: z.object({
10
+ email: z.email(),
11
+ username: z.string().min(3).max(30).regex(/^[a-z0-9_]+$/, "signUp.username.format"),
12
+ password: z.string().min(8),
13
+ dateOfBirth: z.iso.date().refine((d) => d <= latestBirthDate(18), "form.errors.UNDERAGE"),
14
+ acceptTerms: z.boolean().refine(Boolean, "signUp.terms.required"),
15
+ }),
16
+ defaultValues: { email: "", username: "", password: "", dateOfBirth: "", acceptTerms: false },
17
+ });
18
+ const WORDS: Record<string, string> = { "signUp.username.format": "Lowercase letters, digits and _ only.", "signUp.username.taken": "This username is taken.", "signUp.terms.required": "Accept the terms to continue.", "form.errors.UNDERAGE": "You must be 18 or older.", "form.errors.BIRTH_DATE_INVALID": "Enter your real date of birth.", "form.errors.USERNAME_RESERVED": "This username is reserved.", "apiErrors.ACCOUNT_EXISTS": "This e-mail already has an account. Sign in instead." };
19
+ const taken = (u: string) => new Promise<boolean>((done) => setTimeout(() => done(["eric", "elena"].includes(u)), 300));
20
+ // The server's codes (Orochia's register API): the ones that belong to a field land under it, the others above the button.
21
+ const register = async (v: { email: string; username: string }) => { if (v.username === "admin") throw new KrizakaApiError({ status: 400, code: "USERNAME_RESERVED" }); if (v.email.endsWith("@orochia.com")) throw new KrizakaApiError({ status: 409, code: "ACCOUNT_EXISTS" }); };
22
+
23
+ // Orochia's sign-up: the username checked as it is typed (a free one offered), an 18+ birth date, the server's refusals in place.
24
+ export default function FormSignUp() {
25
+ const form = signUp.useForm({ i18n: { t: (key) => WORDS[key] }, codeFields: { UNDERAGE: "dateOfBirth", BIRTH_DATE_INVALID: "dateOfBirth", USERNAME_RESERVED: "username" }, onSubmit: register });
26
+ return (
27
+ <form.Form aria-label="Create your account" className="flex w-80 flex-col gap-4">
28
+ <form.TextField name="email" label="E-mail" type="email" autoComplete="email" hint="We send a link to confirm it." />
29
+ <form.TextField name="username" label="Username" start="@" autoComplete="username" transform={(v) => v.toLowerCase().replace(/\s+/g, "_")} availableHint="Free" validateAsync={async (u) => ((await taken(u)) ? { key: "signUp.username.taken", suggestion: { label: `Use @${u}_${u.length}`, value: `${u}_${u.length}` } } : null)} />
30
+ <form.TextField name="password" label="Password" type="password" autoComplete="new-password" hint="At least 8 characters." />
31
+ <form.DateField name="dateOfBirth" label="Date of birth" entry="parts" autoComplete="bday" hint="Private, never shown." />
32
+ <form.Checkbox name="acceptTerms" label="I accept the terms and the privacy policy" />
33
+ <form.FormError />
34
+ <form.Submit>Create my account</form.Submit>
35
+ </form.Form>
36
+ );
37
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ // registry/examples/form/upload.tsx
4
+ import { createKrizakaForm } from "@krizaka/ui/form";
5
+ import { z } from "zod";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var upload = createKrizakaForm({
8
+ schema: z.object({
9
+ video: z.array(z.instanceof(Blob)).length(1, "upload.video.required"),
10
+ title: z.string().min(3).max(100),
11
+ description: z.string().max(2e3),
12
+ tags: z.array(z.string()).max(10),
13
+ visibility: z.enum(["public", "followers", "private"])
14
+ }),
15
+ defaultValues: { video: [], title: "", description: "", tags: [], visibility: "public" },
16
+ i18n: { t: (key) => key === "upload.video.required" ? "Choose the video to publish." : void 0 }
17
+ });
18
+ var VISIBILITY = [{ value: "public", label: "Everyone" }, { value: "followers", label: "My followers" }, { value: "private", label: "Only me" }];
19
+ function FormUpload() {
20
+ const form = upload.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 600)) });
21
+ return /* @__PURE__ */ jsxs(form.Form, { "aria-label": "Publish a video", className: "flex w-96 flex-col gap-4", children: [
22
+ /* @__PURE__ */ jsx(form.FileUpload, { name: "video", label: "Video", title: "Drop your video here", browseLabel: "Choose a file", hint: "MP4 or MOV, up to 4 GB.", accept: "video/*", maxSize: 4 * 1024 ** 3, removeLabel: (file) => `Remove ${file}` }),
23
+ /* @__PURE__ */ jsx(form.TextField, { name: "title", label: "Title", maxLength: 100 }),
24
+ /* @__PURE__ */ jsx(form.TextArea, { name: "description", label: "Description", rows: 3 }),
25
+ /* @__PURE__ */ jsx(form.TagInput, { name: "tags", label: "Tags", max: 10, hint: "Up to 10, separated by commas." }),
26
+ /* @__PURE__ */ jsx(form.Select, { name: "visibility", label: "Who can watch", options: VISIBILITY }),
27
+ /* @__PURE__ */ jsx(form.Submit, { children: "Publish" })
28
+ ] });
29
+ }
30
+ export {
31
+ FormUpload as default
32
+ };
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { createKrizakaForm } from "@krizaka/ui/form";
4
+ import { z } from "zod";
5
+
6
+ const upload = createKrizakaForm({
7
+ schema: z.object({
8
+ video: z.array(z.instanceof(Blob)).length(1, "upload.video.required"),
9
+ title: z.string().min(3).max(100),
10
+ description: z.string().max(2000),
11
+ tags: z.array(z.string()).max(10),
12
+ visibility: z.enum(["public", "followers", "private"]),
13
+ }),
14
+ defaultValues: { video: [] as Blob[], title: "", description: "", tags: [] as string[], visibility: "public" as const },
15
+ i18n: { t: (key) => (key === "upload.video.required" ? "Choose the video to publish." : undefined) },
16
+ });
17
+ const VISIBILITY = [{ value: "public", label: "Everyone" }, { value: "followers", label: "My followers" }, { value: "private", label: "Only me" }];
18
+
19
+ // A video and what describes it: the file, a title, a description, tags and who sees it — sent once, together.
20
+ export default function FormUpload() {
21
+ const form = upload.useForm({ onSubmit: (values) => new Promise((done) => setTimeout(() => done(values), 600)) });
22
+ return (
23
+ <form.Form aria-label="Publish a video" className="flex w-96 flex-col gap-4">
24
+ <form.FileUpload name="video" label="Video" title="Drop your video here" browseLabel="Choose a file" hint="MP4 or MOV, up to 4 GB." accept="video/*" maxSize={4 * 1024 ** 3} removeLabel={(file) => `Remove ${file}`} />
25
+ <form.TextField name="title" label="Title" maxLength={100} />
26
+ <form.TextArea name="description" label="Description" rows={3} />
27
+ <form.TagInput name="tags" label="Tags" max={10} hint="Up to 10, separated by commas." />
28
+ <form.Select name="visibility" label="Who can watch" options={VISIBILITY} />
29
+ <form.Submit>Publish</form.Submit>
30
+ </form.Form>
31
+ );
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;
@@ -0,0 +1,33 @@
1
+ "use client";
2
+
3
+ // registry/examples/list/native/default.tsx
4
+ import { Avatar, Txt } from "@krizaka/ui/native";
5
+ import { List } from "@krizaka/ui/native/list";
6
+ import { View } from "react-native";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var notifications = [
9
+ { id: "n1", who: "Elena Varga", what: "tipped your video \u201CSunset over Montr\xE9al\u201D", when: "2 min" },
10
+ { id: "n2", who: "Noah Petit", what: "started following you", when: "1 h" },
11
+ { id: "n3", who: "Maya Chen", what: "commented: \u201CThe drone shot!\u201D", when: "3 h" }
12
+ ];
13
+ function NativeListDefault() {
14
+ return /* @__PURE__ */ jsx(
15
+ List,
16
+ {
17
+ label: "Notifications",
18
+ data: notifications,
19
+ keyExtractor: (n) => n.id,
20
+ separator: "inset",
21
+ renderItem: (n) => /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 12, paddingHorizontal: 16, paddingVertical: 12 }, children: [
22
+ /* @__PURE__ */ jsx(Avatar, { alt: n.who, size: "md" }),
23
+ /* @__PURE__ */ jsxs(View, { style: { flex: 1 }, children: [
24
+ /* @__PURE__ */ jsx(Txt, { children: `${n.who} ${n.what}` }),
25
+ /* @__PURE__ */ jsx(Txt, { variant: "caption", tone: "secondary", children: n.when })
26
+ ] })
27
+ ] })
28
+ }
29
+ );
30
+ }
31
+ export {
32
+ NativeListDefault as default
33
+ };
@@ -0,0 +1,31 @@
1
+ import { Avatar, Txt } from "@krizaka/ui/native";
2
+ import { List } from "@krizaka/ui/native/list";
3
+ import { View } from "react-native";
4
+
5
+ type Notification = { id: string; who: string; what: string; when: string };
6
+ const notifications: Notification[] = [
7
+ { id: "n1", who: "Elena Varga", what: "tipped your video “Sunset over Montréal”", when: "2 min" },
8
+ { id: "n2", who: "Noah Petit", what: "started following you", when: "1 h" },
9
+ { id: "n3", who: "Maya Chen", what: "commented: “The drone shot!”", when: "3 h" },
10
+ ];
11
+
12
+ // A FlashList v2 with the theme's separators, inset after the avatar.
13
+ export default function NativeListDefault() {
14
+ return (
15
+ <List
16
+ label="Notifications"
17
+ data={notifications}
18
+ keyExtractor={(n) => n.id}
19
+ separator="inset"
20
+ renderItem={(n) => (
21
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 12, paddingHorizontal: 16, paddingVertical: 12 }}>
22
+ <Avatar alt={n.who} size="md" />
23
+ <View style={{ flex: 1 }}>
24
+ <Txt>{`${n.who} ${n.what}`}</Txt>
25
+ <Txt variant="caption" tone="secondary">{n.when}</Txt>
26
+ </View>
27
+ </View>
28
+ )}
29
+ />
30
+ );
31
+ }
@@ -0,0 +1,4 @@
1
+ import type { ComponentType } from "react";
2
+ /** A named example of the @krizaka/ui registry; `defaultOpen` opens what it opens (a dialog, a menu, toasts) at load. */
3
+ declare const Example: ComponentType<{ defaultOpen?: boolean }>;
4
+ export default Example;