@riducms/admin 0.2.2 → 0.3.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 (352) hide show
  1. package/THIRD_PARTY_NOTICES.md +60 -37
  2. package/package.json +27 -15
  3. package/src/app/admin-core-view-route.svelte +38 -53
  4. package/src/app/admin-extension-route.svelte +19 -0
  5. package/src/app/admin-layout.scss +175 -0
  6. package/src/app/admin-layout.svelte +144 -410
  7. package/src/app/admin-navigation-progress.svelte +11 -77
  8. package/src/app/admin-page-title.svelte +17 -85
  9. package/src/app/admin-page-title.ts +136 -0
  10. package/src/app/admin-route-error.svelte +24 -0
  11. package/src/app/admin-router-provider.svelte +34 -0
  12. package/src/app/admin-router-root.svelte +41 -0
  13. package/src/app/admin-routes.svelte +56 -19
  14. package/src/app/loading-boundary.svelte +3 -1
  15. package/src/app-meta.ts +13 -0
  16. package/src/app.css +138 -496
  17. package/src/app.svelte +60 -26
  18. package/src/components/brand/ridu-logo.svelte +19 -5
  19. package/src/components/data/list-table.scss +63 -0
  20. package/src/components/icons/LICENSE.md +24 -0
  21. package/src/components/icons/chevron.svelte +8 -0
  22. package/src/components/icons/plus.svelte +12 -0
  23. package/src/components/icons/search.svelte +12 -0
  24. package/src/components/icons/x.svelte +8 -0
  25. package/src/components/json-tree/json-tree-node.svelte +61 -67
  26. package/src/components/json-tree/json-viewer.scss +153 -0
  27. package/src/components/json-tree/json-viewer.svelte +57 -96
  28. package/src/components/ui/banner/banner.scss +28 -0
  29. package/src/components/ui/banner/banner.svelte +9 -8
  30. package/src/components/ui/banner/index.ts +1 -5
  31. package/src/components/ui/code-editor/code-editor.scss +20 -0
  32. package/src/components/ui/code-editor/code-editor.svelte +71 -0
  33. package/src/components/ui/code-editor/code-editor.ts +151 -0
  34. package/src/components/ui/code-editor/code-languages.ts +29 -0
  35. package/src/components/ui/collapsible-panel/collapsible-panel.scss +38 -0
  36. package/src/components/ui/collapsible-panel/collapsible-panel.svelte +13 -0
  37. package/src/components/ui/collapsible-panel/index.ts +1 -0
  38. package/src/components/ui/combobox/combobox.scss +163 -0
  39. package/src/components/ui/combobox/index.ts +22 -0
  40. package/src/components/ui/date-value-control/date-value-calendar.svelte +128 -16
  41. package/src/components/ui/date-value-control/date-value-control-loader.scss +26 -0
  42. package/src/components/ui/date-value-control/date-value-control-loader.svelte +35 -13
  43. package/src/components/ui/date-value-control/date-value-control.scss +223 -0
  44. package/src/components/ui/date-value-control/date-value-control.svelte +62 -44
  45. package/src/components/ui/drawer/drawer-depth.ts +8 -0
  46. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +2 -5
  47. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +2 -5
  48. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -2
  49. package/src/components/ui/dropdown-menu/dropdown-menu.scss +62 -0
  50. package/src/components/ui/kbd/kbd.scss +17 -0
  51. package/src/components/ui/kbd/kbd.svelte +3 -10
  52. package/src/components/ui/navigation-drawer/navigation-drawer.scss +40 -0
  53. package/src/components/ui/navigation-drawer/navigation-drawer.svelte +38 -0
  54. package/src/components/ui/popover/popover-content.svelte +3 -5
  55. package/src/components/ui/popover/popover.scss +18 -0
  56. package/src/components/ui/radio-group/index.ts +0 -1
  57. package/src/components/ui/radio-group/radio-card-item.svelte +7 -12
  58. package/src/components/ui/radio-group/radio-group.svelte +2 -2
  59. package/src/components/ui/radio-group/radio.scss +45 -0
  60. package/src/components/ui/select/select-content.svelte +5 -6
  61. package/src/components/ui/select/select-item.svelte +8 -8
  62. package/src/components/ui/select/select-scroll-down-button.svelte +3 -5
  63. package/src/components/ui/select/select-scroll-up-button.svelte +3 -5
  64. package/src/components/ui/select/select-trigger.svelte +6 -7
  65. package/src/components/ui/select/select.scss +126 -0
  66. package/src/components/ui/sheet/sheet-content.svelte +6 -34
  67. package/src/components/ui/sheet/sheet-description.svelte +2 -2
  68. package/src/components/ui/sheet/sheet-overlay.svelte +2 -21
  69. package/src/components/ui/sheet/sheet-title.svelte +2 -2
  70. package/src/components/ui/sheet/sheet.scss +221 -0
  71. package/src/components/ui/skeleton/skeleton.scss +20 -0
  72. package/src/components/ui/skeleton/skeleton.svelte +4 -16
  73. package/src/components/ui/sonner/sonner.scss +121 -0
  74. package/src/components/ui/sonner/sonner.svelte +11 -10
  75. package/src/components/ui/status-indicator/status-indicator.scss +48 -0
  76. package/src/components/ui/status-indicator/status-indicator.svelte +14 -14
  77. package/src/components/ui/tabs/tabs-content.svelte +2 -2
  78. package/src/components/ui/tabs/tabs-list.svelte +7 -7
  79. package/src/components/ui/tabs/tabs-trigger.svelte +2 -8
  80. package/src/components/ui/tabs/tabs.scss +86 -0
  81. package/src/components/ui/tabs/tabs.svelte +2 -2
  82. package/src/components/ui/timezone-picker/timezone-picker.svelte +152 -0
  83. package/src/core/api/admin-client.ts +2 -2
  84. package/src/core/bootstrap/admin-bootstrap.ts +396 -0
  85. package/src/core/bootstrap/admin-prepared-state.ts +213 -0
  86. package/src/core/bootstrap/admin-route-modules.ts +66 -0
  87. package/src/core/clipboard/copy-feedback.svelte.ts +43 -0
  88. package/src/core/forms/embedded-fields.ts +4 -8
  89. package/src/core/forms/field-editor-authoring.ts +8 -0
  90. package/src/core/forms/field-issue-focus.ts +4 -4
  91. package/src/core/forms/field-occurrence.ts +4 -7
  92. package/src/core/forms/form-controller.svelte.ts +14 -21
  93. package/src/core/forms/form-draft-recovery.ts +17 -4
  94. package/src/core/forms/form-issue-correlation.ts +13 -13
  95. package/src/core/forms/form-path.ts +17 -0
  96. package/src/core/forms/form-schema.ts +12 -43
  97. package/src/core/forms/form-validation.ts +7 -46
  98. package/src/core/forms/plugin-field-authoring.ts +6 -2
  99. package/src/core/forms/plugin-field-write.ts +2 -6
  100. package/src/core/i18n/admin-i18n.svelte.ts +18 -0
  101. package/src/core/i18n/format-admin-date-time.ts +19 -0
  102. package/src/core/i18n/humanize-identifier.ts +5 -0
  103. package/src/core/i18n/time-zone-label.ts +20 -0
  104. package/src/core/loaders/admin-view-host.svelte +72 -0
  105. package/src/core/loaders/admin-view-loader.svelte.ts +90 -0
  106. package/src/core/notifications/validation-toast.svelte +2 -2
  107. package/src/core/plugins/field-registry.ts +7 -8
  108. package/src/core/routing/admin-paths.ts +53 -2
  109. package/src/core/routing/admin-scroll.svelte.ts +74 -0
  110. package/src/core/routing/admin-view-selection.ts +78 -0
  111. package/src/core/runtime/admin-runtime.svelte.ts +121 -124
  112. package/src/core/schema/field-default.ts +33 -0
  113. package/src/core/schema/field-localization.ts +15 -0
  114. package/src/core/schema/read-document-path.ts +11 -0
  115. package/src/features/account/account-menu.scss +37 -0
  116. package/src/features/account/account-menu.svelte +115 -0
  117. package/src/features/{auth → account}/account-navigation.svelte +16 -19
  118. package/src/features/{auth → account}/account-route.svelte +206 -49
  119. package/src/features/account/account-security-controller.svelte.ts +157 -0
  120. package/src/features/{auth → account}/account-security-route.svelte +140 -42
  121. package/src/features/api-reference/api-reference-code.svelte +64 -0
  122. package/src/features/api-reference/api-reference-content.svelte +258 -0
  123. package/src/features/api-reference/api-reference-drawer.scss +74 -0
  124. package/src/features/api-reference/api-reference-examples.ts +381 -0
  125. package/src/features/api-reference/api-reference-schema.ts +332 -0
  126. package/src/features/api-reference/api-reference.scss +190 -0
  127. package/src/features/api-reference/api-reference.svelte +74 -0
  128. package/src/features/auth/auth-brand.svelte +5 -3
  129. package/src/features/auth/auth-field.svelte +92 -0
  130. package/src/features/auth/auth-frame.svelte +5 -8
  131. package/src/features/auth/auth.scss +122 -0
  132. package/src/features/auth/create-first-user-controller.svelte.ts +20 -51
  133. package/src/features/auth/create-first-user-route.svelte +83 -122
  134. package/src/features/auth/forgot-password-route.svelte +31 -23
  135. package/src/features/auth/login-route.svelte +68 -116
  136. package/src/features/auth/logout-button.scss +34 -0
  137. package/src/features/auth/logout-button.svelte +52 -0
  138. package/src/features/auth/request-verification-route.svelte +27 -22
  139. package/src/features/auth/reset-password-route.svelte +108 -41
  140. package/src/features/auth/verify-email-route.svelte +49 -15
  141. package/src/features/bulk-edit/bulk-edit-fields.ts +23 -0
  142. package/src/features/bulk-edit/bulk-edit.scss +152 -0
  143. package/src/features/bulk-edit/field-picker.svelte +176 -0
  144. package/src/features/collections/bulk/bulk-actions.svelte +148 -0
  145. package/src/features/collections/bulk/bulk-confirmation.svelte +153 -0
  146. package/src/features/collections/bulk/bulk-editor-loader.svelte +95 -0
  147. package/src/features/collections/bulk/bulk-editor.svelte +186 -0
  148. package/src/features/collections/collection-list-cell-values.ts +144 -0
  149. package/src/features/collections/collection-list-controller.svelte.ts +268 -379
  150. package/src/features/collections/collection-list-preferences-controller.svelte.ts +54 -40
  151. package/src/features/collections/collection-list-query.ts +223 -0
  152. package/src/features/collections/collection-list-results.svelte +205 -329
  153. package/src/features/collections/collection-list-route.svelte +143 -884
  154. package/src/features/collections/collection-list.scss +261 -0
  155. package/src/features/collections/collection-list.svelte.ts +362 -0
  156. package/src/features/collections/controls/column-picker.svelte +110 -0
  157. package/src/features/collections/controls/column-pill.svelte +62 -0
  158. package/src/features/collections/controls/filter-builder.svelte +193 -0
  159. package/src/features/collections/controls/filter-row.svelte +142 -0
  160. package/src/features/collections/controls/list-controls.scss +196 -0
  161. package/src/features/collections/controls/list-options.svelte +203 -0
  162. package/src/features/collections/controls/list-pagination.scss +78 -0
  163. package/src/features/collections/controls/list-pagination.svelte +115 -0
  164. package/src/features/collections/controls/list-toolbar.svelte +132 -0
  165. package/src/features/collections/controls/sort-heading.scss +52 -0
  166. package/src/features/collections/controls/sort-heading.svelte +78 -0
  167. package/src/features/collections/list-columns.ts +43 -0
  168. package/src/features/collections/list-workspace.ts +155 -54
  169. package/src/features/dashboard/dashboard-route.svelte +82 -86
  170. package/src/features/dashboard/dashboard.scss +131 -0
  171. package/src/features/development/development-tools.svelte +20 -36
  172. package/src/features/documents/document-action-bar.svelte +438 -0
  173. package/src/features/documents/document-api-view-loader.svelte +17 -6
  174. package/src/features/documents/document-api-view.scss +120 -0
  175. package/src/features/documents/document-api-view.svelte +169 -454
  176. package/src/features/documents/document-controller.svelte.ts +526 -613
  177. package/src/features/documents/document-credentials.scss +37 -0
  178. package/src/features/documents/document-credentials.svelte +72 -0
  179. package/src/features/documents/{document-route-dirty.ts → document-credentials.ts} +25 -0
  180. package/src/features/documents/document-field-sections.scss +39 -0
  181. package/src/features/documents/document-field-sections.svelte +4 -13
  182. package/src/features/documents/document-loading.scss +24 -0
  183. package/src/features/documents/document-lock-controller.svelte.ts +187 -0
  184. package/src/features/documents/document-operation-dialogs.svelte +110 -0
  185. package/src/features/documents/document-route-loader.svelte +17 -5
  186. package/src/features/documents/document-route.svelte +305 -811
  187. package/src/features/documents/document-schedule.scss +127 -0
  188. package/src/features/documents/document-schedule.svelte +323 -0
  189. package/src/features/documents/document-title.ts +25 -0
  190. package/src/features/documents/document.scss +274 -0
  191. package/src/features/documents/live-preview-panel.scss +117 -0
  192. package/src/features/documents/live-preview-panel.svelte +167 -168
  193. package/src/features/documents/live-preview.ts +2 -10
  194. package/src/features/documents/upload-document-preview-loader.svelte +26 -23
  195. package/src/features/localization/content-locale-switcher.scss +43 -0
  196. package/src/features/localization/content-locale-switcher.svelte +24 -17
  197. package/src/features/navigation/admin-breadcrumbs.scss +56 -0
  198. package/src/features/navigation/admin-breadcrumbs.svelte +102 -37
  199. package/src/features/navigation/admin-command-menu.svelte +38 -15
  200. package/src/features/navigation/admin-navigation.scss +133 -0
  201. package/src/features/navigation/admin-navigation.svelte +169 -0
  202. package/src/features/navigation/breadcrumb-context.svelte.ts +15 -0
  203. package/src/features/navigation/navigation-preference.svelte.ts +70 -0
  204. package/src/features/reference-browser/reference-browser-editor.svelte +180 -0
  205. package/src/features/reference-browser/reference-browser-list.scss +113 -0
  206. package/src/features/reference-browser/reference-browser-list.svelte +370 -0
  207. package/src/features/reference-browser/reference-browser-loader.svelte +26 -8
  208. package/src/features/reference-browser/reference-browser-workflow.svelte.ts +335 -236
  209. package/src/features/reference-browser/reference-browser.scss +217 -0
  210. package/src/features/reference-browser/reference-browser.svelte +154 -797
  211. package/src/features/reference-browser/reference-collection-picker.svelte +116 -0
  212. package/src/features/reference-browser/reference-list-query.ts +35 -0
  213. package/src/features/uploads/bulk-upload-controller.svelte.ts +604 -0
  214. package/src/features/uploads/bulk-upload-edit-all.svelte +92 -0
  215. package/src/features/uploads/bulk-upload-editor.svelte +191 -0
  216. package/src/features/uploads/bulk-upload-file-picker.svelte +70 -0
  217. package/src/features/uploads/bulk-upload-remote.svelte +100 -0
  218. package/src/features/uploads/bulk-upload-route-loader.svelte +54 -0
  219. package/src/features/uploads/bulk-upload-route.svelte +172 -592
  220. package/src/features/uploads/bulk-upload-sidebar.svelte +125 -0
  221. package/src/features/uploads/bulk-upload.scss +526 -0
  222. package/src/features/uploads/bulk-upload.ts +105 -14
  223. package/src/features/uploads/crop-surface.ts +150 -0
  224. package/src/features/uploads/image-editor.svelte +231 -0
  225. package/src/features/uploads/rendition-preview.svelte +64 -0
  226. package/src/features/uploads/upload-control.svelte +340 -0
  227. package/src/features/uploads/upload-document-contracts.ts +30 -0
  228. package/src/features/uploads/upload-draft.svelte.ts +294 -0
  229. package/src/features/uploads/upload-metadata-fields.scss +32 -0
  230. package/src/features/uploads/upload-metadata-fields.svelte +8 -7
  231. package/src/features/uploads/upload-thumbnail.svelte +47 -0
  232. package/src/features/uploads/upload.scss +361 -0
  233. package/src/features/versions/version-comparison.svelte +361 -0
  234. package/src/features/versions/version-diff-fields.svelte +136 -0
  235. package/src/features/versions/version-diff.ts +296 -41
  236. package/src/features/versions/version-history-controller.svelte.ts +311 -0
  237. package/src/features/versions/version-history-route-loader.svelte +32 -0
  238. package/src/features/versions/version-history-route.svelte +157 -522
  239. package/src/features/versions/version-history.scss +444 -0
  240. package/src/features/versions/version-history.ts +45 -0
  241. package/src/features/versions/version-table.svelte +91 -0
  242. package/src/fields/embedded-schema-draft-editor.scss +48 -0
  243. package/src/fields/embedded-schema-draft-editor.svelte +24 -16
  244. package/src/fields/field-collapsible.svelte +14 -8
  245. package/src/fields/field-layout.scss +171 -0
  246. package/src/fields/field-layout.svelte +3 -5
  247. package/src/fields/field-renderer.svelte +17 -29
  248. package/src/fields/field-shell.svelte +1 -1
  249. package/src/fields/field-tab-layout.svelte +25 -19
  250. package/src/fields/join/join-field-controller.svelte.ts +513 -0
  251. package/src/fields/join/join-field-values.ts +111 -0
  252. package/src/fields/join/join-field.scss +189 -0
  253. package/src/fields/join/join-field.svelte +164 -332
  254. package/src/fields/live-validation-feedback.svelte +4 -3
  255. package/src/fields/local-field-editor.svelte +2 -2
  256. package/src/fields/nested/block-name-input.svelte +11 -12
  257. package/src/fields/nested/block-picker.scss +128 -0
  258. package/src/fields/nested/block-picker.svelte +81 -0
  259. package/src/fields/nested/block-placeholder.svg +2 -0
  260. package/src/fields/nested/nested-field.scss +331 -0
  261. package/src/fields/nested/nested-field.svelte +265 -245
  262. package/src/fields/nested/sortable-row.svelte +9 -7
  263. package/src/fields/plugin-field.svelte +4 -6
  264. package/src/fields/point/point-field.svelte +51 -38
  265. package/src/fields/primitive-list/primitive-list-field.svelte +63 -19
  266. package/src/fields/primitive-list/primitive-list.scss +109 -0
  267. package/src/fields/radio/radio-field.svelte +2 -1
  268. package/src/fields/relationship/relationship-field-controller.svelte.ts +81 -41
  269. package/src/fields/relationship/relationship-field.svelte +144 -175
  270. package/src/fields/relationship/relationship-lookup-controller.svelte.ts +58 -30
  271. package/src/fields/relationship/relationship-query.ts +2 -10
  272. package/src/fields/relationship/relationship-quick-picker-controller.svelte.ts +36 -62
  273. package/src/fields/relationship/relationship-quick-picker.svelte +178 -227
  274. package/src/fields/relationship/relationship-selection.svelte +120 -0
  275. package/src/fields/relationship/relationship-value.ts +46 -7
  276. package/src/fields/relationship/relationship.scss +182 -0
  277. package/src/fields/scalar/code-field.svelte +101 -0
  278. package/src/fields/scalar/date-control-value.ts +13 -0
  279. package/src/fields/scalar/scalar-field.svelte +25 -67
  280. package/src/fields/select/select-field.svelte +132 -94
  281. package/src/fields/text/slug-field.scss +28 -0
  282. package/src/fields/text/slug-field.svelte +19 -15
  283. package/src/fields/text/text-field.svelte +1 -2
  284. package/src/fields/ui/ui-field.svelte +7 -4
  285. package/src/fields/virtual/virtual-field.svelte +2 -5
  286. package/src/index.ts +1 -2
  287. package/src/routing.ts +2 -0
  288. package/src/app/deferred-redirect.svelte +0 -23
  289. package/src/components/code-block/code-block.svelte +0 -107
  290. package/src/components/ui/bulk-action-bar/bulk-action-bar.svelte +0 -50
  291. package/src/components/ui/bulk-action-bar/index.ts +0 -1
  292. package/src/components/ui/button/index.ts +0 -8
  293. package/src/components/ui/calendar/calendar-caption.svelte +0 -72
  294. package/src/components/ui/calendar/calendar-cell.svelte +0 -20
  295. package/src/components/ui/calendar/calendar-day.svelte +0 -27
  296. package/src/components/ui/calendar/calendar-grid-row.svelte +0 -13
  297. package/src/components/ui/calendar/calendar-grid.svelte +0 -17
  298. package/src/components/ui/calendar/calendar-head-cell.svelte +0 -20
  299. package/src/components/ui/calendar/calendar-header.svelte +0 -20
  300. package/src/components/ui/calendar/calendar-heading.svelte +0 -17
  301. package/src/components/ui/calendar/calendar-month-select.svelte +0 -45
  302. package/src/components/ui/calendar/calendar-month.svelte +0 -16
  303. package/src/components/ui/calendar/calendar-months.svelte +0 -20
  304. package/src/components/ui/calendar/calendar-nav.svelte +0 -20
  305. package/src/components/ui/calendar/calendar-next-button.svelte +0 -30
  306. package/src/components/ui/calendar/calendar-prev-button.svelte +0 -30
  307. package/src/components/ui/calendar/calendar-year-select.svelte +0 -44
  308. package/src/components/ui/calendar/calendar.svelte +0 -118
  309. package/src/components/ui/calendar/index.ts +0 -1
  310. package/src/components/ui/checkbox/checkbox.svelte +0 -35
  311. package/src/components/ui/checkbox/index.ts +0 -1
  312. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  313. package/src/components/ui/dialog/dialog-content.svelte +0 -52
  314. package/src/components/ui/dialog/dialog-description.svelte +0 -20
  315. package/src/components/ui/dialog/dialog-footer.svelte +0 -20
  316. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  317. package/src/components/ui/dialog/dialog-overlay.svelte +0 -20
  318. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  319. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  320. package/src/components/ui/dialog/dialog.svelte +0 -7
  321. package/src/components/ui/dialog/index.ts +0 -8
  322. package/src/components/ui/empty-state/empty-state.svelte +0 -34
  323. package/src/components/ui/empty-state/index.ts +0 -1
  324. package/src/components/ui/input/index.ts +0 -1
  325. package/src/components/ui/pagination/index.ts +0 -1
  326. package/src/components/ui/pagination/pagination.svelte +0 -88
  327. package/src/components/ui/radio-group/radio-group-item.svelte +0 -17
  328. package/src/components/ui/slider/index.ts +0 -3
  329. package/src/components/ui/slider/slider-content.svelte +0 -59
  330. package/src/components/ui/slider/slider.svelte +0 -35
  331. package/src/components/ui/table/index.ts +0 -6
  332. package/src/components/ui/table/table-body.svelte +0 -20
  333. package/src/components/ui/table/table-cell.svelte +0 -23
  334. package/src/components/ui/table/table-head.svelte +0 -23
  335. package/src/components/ui/table/table-header.svelte +0 -20
  336. package/src/components/ui/table/table-row.svelte +0 -23
  337. package/src/components/ui/table/table.svelte +0 -22
  338. package/src/components/ui/textarea/index.ts +0 -1
  339. package/src/components/ui/tooltip/index.ts +0 -4
  340. package/src/components/ui/tooltip/tooltip-content.svelte +0 -50
  341. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  342. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  343. package/src/components/ui/tooltip/tooltip.svelte +0 -7
  344. package/src/features/auth/account-menu.svelte +0 -159
  345. package/src/features/auth/account-security-controller.svelte.ts +0 -119
  346. package/src/features/collections/collection-list-bulk-actions.svelte +0 -143
  347. package/src/features/collections/collection-list-bulk-editor.svelte +0 -157
  348. package/src/features/collections/collection-list-filter-popover.svelte +0 -238
  349. package/src/features/collections/collection-list-workspace-toolbar.svelte +0 -409
  350. package/src/features/collections/collection-table-skeleton.svelte +0 -44
  351. package/src/features/documents/document-heading.ts +0 -20
  352. package/src/features/documents/upload-document-preview.svelte +0 -525
@@ -0,0 +1,332 @@
1
+ import {
2
+ resolveBlockTypes,
3
+ type SchemaCollection,
4
+ type SchemaField,
5
+ type SchemaManifest,
6
+ } from "@riducms/protocol";
7
+ import type { AdminI18n } from "@riducms/plugin";
8
+ import { schemaFieldDefault, schemaFieldRequiredOnCreate } from "@admin/core/schema/field-default";
9
+ import { isUploadMetadataField } from "@admin/features/uploads/upload-document-contracts";
10
+
11
+ export type ReferenceField = {
12
+ path: string;
13
+ type: string;
14
+ outputType: string;
15
+ requiredOnCreate: boolean;
16
+ writable: boolean;
17
+ localized: boolean;
18
+ description: string;
19
+ constraints: string[];
20
+ jsonSchema?: unknown;
21
+ };
22
+
23
+ export type CollectionReferenceSchema = {
24
+ fields: ReferenceField[];
25
+ create: Record<string, unknown>;
26
+ update: Record<string, unknown>;
27
+ omitted: string[];
28
+ filter?: { path: string; value: string | number | boolean };
29
+ };
30
+
31
+ /** Project the canonical manifest into documentation, never form or validation state. */
32
+ export function collectionReferenceSchema(
33
+ collection: SchemaCollection,
34
+ manifest: SchemaManifest,
35
+ i18n: AdminI18n
36
+ ): CollectionReferenceSchema {
37
+ const fields: ReferenceField[] = [];
38
+ const omitted: string[] = [];
39
+ const active = new Set<SchemaField>();
40
+ const pluginFieldTypes = new Map(
41
+ manifest.plugins.flatMap((plugin) => plugin.fieldTypes ?? []).map((type) => [type.key, type])
42
+ );
43
+
44
+ function writable(field: SchemaField, root: boolean) {
45
+ return (
46
+ !["ui", "join", "virtual"].includes(field.type) &&
47
+ !(root && collection.capabilities.upload && isUploadMetadataField(field.name))
48
+ );
49
+ }
50
+
51
+ function visit(
52
+ items: SchemaField[],
53
+ prefix = "",
54
+ depth = 0,
55
+ inheritedLocale = false,
56
+ inheritedWrite = true,
57
+ ancestorRequiredOnCreate = true
58
+ ) {
59
+ for (const field of items) {
60
+ if (field.type === "ui") continue;
61
+ const path = prefix + field.name;
62
+ const localized = inheritedLocale || field.localized === true;
63
+ const input = inheritedWrite && writable(field, prefix === "");
64
+ const requiredOnCreate = ancestorRequiredOnCreate && schemaFieldRequiredOnCreate(field);
65
+ const plugin =
66
+ field.plugin === undefined ? undefined : pluginFieldTypes.get(field.plugin.key);
67
+ const constraints: string[] = [];
68
+ const text = field.text ?? field.textarea ?? field.code;
69
+ const rows = field.list ?? field.nested ?? field.blocks;
70
+ const blocks = resolveBlockTypes(field.blocks);
71
+
72
+ if (text?.minLength !== undefined)
73
+ constraints.push(i18n.t("apiReference:minLength", { value: text.minLength }));
74
+ if (text?.maxLength !== undefined)
75
+ constraints.push(i18n.t("apiReference:maxLength", { value: text.maxLength }));
76
+ if (field.number?.min !== undefined)
77
+ constraints.push(i18n.t("apiReference:minimum", { value: field.number.min }));
78
+ if (field.number?.max !== undefined)
79
+ constraints.push(i18n.t("apiReference:maximum", { value: field.number.max }));
80
+ if (rows?.minRows !== undefined)
81
+ constraints.push(i18n.t("apiReference:minItems", { value: rows.minRows }));
82
+ if (rows?.maxRows !== undefined)
83
+ constraints.push(i18n.t("apiReference:maxItems", { value: rows.maxRows }));
84
+ if (field.select)
85
+ constraints.push(
86
+ i18n.t("apiReference:allowedValues", {
87
+ values: field.select.options.map((option) => JSON.stringify(option.value)).join(", "),
88
+ })
89
+ );
90
+ if (field.unique) constraints.push(i18n.t("apiReference:unique"));
91
+ if (field.dynamicDefault) constraints.push(i18n.t("apiReference:dynamicDefault"));
92
+ else if (field.default !== undefined)
93
+ constraints.push(i18n.t("apiReference:defaultValue", { value: field.default }));
94
+ if (field.queryRestricted) constraints.push(i18n.t("apiReference:queryRestricted"));
95
+ if (field.date)
96
+ constraints.push(
97
+ field.date.format === "time"
98
+ ? "HH:mm[:ss]"
99
+ : field.date.format === "date"
100
+ ? "YYYY-MM-DD"
101
+ : "RFC 3339"
102
+ );
103
+
104
+ const targets =
105
+ field.relationship?.targets?.map((target) => target.collectionSlug) ??
106
+ [field.relationship?.collectionSlug ?? field.upload?.collectionSlug].filter(
107
+ (slug): slug is string => slug !== undefined
108
+ );
109
+ if (targets.length)
110
+ constraints.push(i18n.t("apiReference:references", { collections: targets.join(", ") }));
111
+ if (field.relationship?.polymorphic) constraints.push(i18n.t("apiReference:polymorphic"));
112
+ if (field.type === "array") constraints.push(i18n.t("apiReference:rowIdentity"));
113
+ if (field.type === "blocks")
114
+ constraints.push(
115
+ i18n.t("apiReference:blockDiscriminator", {
116
+ values: blocks.map((block) => block.slug).join(", "),
117
+ })
118
+ );
119
+
120
+ fields.push({
121
+ path,
122
+ type: fieldType(field, plugin?.typescriptInput),
123
+ outputType: fieldType(field, plugin?.typescriptOutput, true),
124
+ requiredOnCreate,
125
+ writable: input,
126
+ localized,
127
+ description: i18n.text(field.admin.description ?? "", field.admin.descriptionTranslations),
128
+ constraints,
129
+ ...(plugin?.jsonSchema === undefined ? {} : { jsonSchema: plugin.jsonSchema }),
130
+ });
131
+
132
+ // Recursive named block families stay finite; their discriminator row documents the edge.
133
+ if (depth >= 4 || active.has(field)) {
134
+ if (field.nested || field.blocks) constraints.push(i18n.t("apiReference:furtherNesting"));
135
+ continue;
136
+ }
137
+ active.add(field);
138
+ if (field.nested)
139
+ visit(
140
+ field.nested.fields,
141
+ `${path}${field.type === "array" ? "[]" : ""}.`,
142
+ depth + 1,
143
+ localized,
144
+ input,
145
+ requiredOnCreate
146
+ );
147
+ for (const block of blocks)
148
+ visit(
149
+ block.fields,
150
+ `${path}[${block.slug}].`,
151
+ depth + 1,
152
+ localized,
153
+ input,
154
+ requiredOnCreate
155
+ );
156
+ active.delete(field);
157
+ }
158
+ }
159
+
160
+ function sample(items: SchemaField[], prefix = "", depth = 0): Record<string, unknown> {
161
+ const result: Record<string, unknown> = {};
162
+ for (const field of items) {
163
+ if (!writable(field, prefix === "")) continue;
164
+ const path = prefix + field.name;
165
+ if (field.dynamicDefault) continue;
166
+
167
+ const textRules = field.text ?? field.textarea ?? field.code;
168
+ const largeExample =
169
+ (field.nested?.minRows ?? field.blocks?.minRows ?? field.list?.minRows ?? 0) > 3 ||
170
+ (textRules?.minLength ?? 0) > 256;
171
+ const unsupportedEmail =
172
+ field.type === "email" &&
173
+ ((textRules?.minLength ?? 0) > 76 || (textRules?.maxLength ?? Infinity) < 6);
174
+ if (
175
+ depth >= 4 ||
176
+ active.has(field) ||
177
+ field.type === "plugin" ||
178
+ largeExample ||
179
+ unsupportedEmail
180
+ ) {
181
+ omitted.push(path);
182
+ continue;
183
+ }
184
+ active.add(field);
185
+ const blocks = resolveBlockTypes(field.blocks);
186
+ let value: unknown = scalarExample(field);
187
+ if (field.type === "group") value = sample(field.nested?.fields ?? [], `${path}.`, depth + 1);
188
+ if (field.type === "array")
189
+ value =
190
+ field.required || (field.nested?.minRows ?? 0) > 0
191
+ ? Array.from({ length: Math.min(3, Math.max(1, field.nested?.minRows ?? 0)) }, () =>
192
+ sample(field.nested?.fields ?? [], `${path}[].`, depth + 1)
193
+ )
194
+ : [];
195
+ if (field.type === "blocks") {
196
+ const block = blocks[0];
197
+ value =
198
+ block && (field.required || (field.blocks?.minRows ?? 0) > 0)
199
+ ? Array.from({ length: Math.max(1, field.blocks?.minRows ?? 0) }, () => ({
200
+ blockType: block.slug,
201
+ ...sample(block.fields, `${path}[${block.slug}].`, depth + 1),
202
+ }))
203
+ : [];
204
+ }
205
+ active.delete(field);
206
+ if (value !== undefined) result[field.name] = value;
207
+ }
208
+ return result;
209
+ }
210
+
211
+ visit(collection.fields);
212
+ const create = sample(collection.fields);
213
+ const update = { ...create };
214
+ const filterField = collection.fields.find(
215
+ (field) =>
216
+ !field.queryRestricted &&
217
+ writable(field, true) &&
218
+ ["text", "email", "number", "checkbox", "select", "radio"].includes(field.type) &&
219
+ !field.select?.hasMany
220
+ );
221
+ const value = filterField ? create[filterField.name] : undefined;
222
+ const filter =
223
+ filterField &&
224
+ (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
225
+ ? { path: filterField.name, value }
226
+ : undefined;
227
+
228
+ return { fields, create, update, omitted: [...new Set(omitted)], filter };
229
+ }
230
+
231
+ function fieldType(field: SchemaField, pluginType?: string, output = false): string {
232
+ if (field.list || field.select?.hasMany)
233
+ return field.type === "number-list" ? "number[]" : "string[]";
234
+ if (field.relationship || field.upload) {
235
+ const value = field.relationship?.polymorphic
236
+ ? output
237
+ ? "{ relationTo, id: string | object }"
238
+ : "{ relationTo, id }"
239
+ : output
240
+ ? "string (ID) | object"
241
+ : "string (ID)";
242
+ return field.relationship?.hasMany || field.upload?.hasMany ? `(${value})[]` : value;
243
+ }
244
+ switch (field.type) {
245
+ case "number":
246
+ return "number";
247
+ case "checkbox":
248
+ return "boolean";
249
+ case "point":
250
+ return "[longitude, latitude]";
251
+ case "group":
252
+ return "object";
253
+ case "array":
254
+ case "blocks":
255
+ return "object[]";
256
+ case "json":
257
+ return "JSON";
258
+ case "plugin":
259
+ return pluginType ?? "JSON";
260
+ case "join":
261
+ return "object[]";
262
+ case "virtual":
263
+ return field.virtual?.valueType ?? "JSON";
264
+ default:
265
+ return "string";
266
+ }
267
+ }
268
+
269
+ function scalarExample(field: SchemaField): unknown {
270
+ const defaultValue = schemaFieldDefault(field);
271
+ if (defaultValue.present) return defaultValue.value;
272
+ const numeric = Math.min(
273
+ field.number?.max ?? Infinity,
274
+ Math.max(field.number?.min ?? -Infinity, 1)
275
+ );
276
+ const textRules = field.text ?? field.textarea ?? field.code;
277
+ const text = "Example "
278
+ .repeat(Math.ceil(Math.max(7, textRules?.minLength ?? 0) / 8))
279
+ .trim()
280
+ .padEnd(textRules?.minLength ?? 0, "x")
281
+ .slice(0, textRules?.maxLength ?? 256);
282
+ const itemCount = Math.min(
283
+ field.list?.maxRows ?? Infinity,
284
+ Math.max(1, field.list?.minRows ?? 0)
285
+ );
286
+ switch (field.type) {
287
+ case "number":
288
+ return numeric;
289
+ case "checkbox":
290
+ return true;
291
+ case "number-list":
292
+ return Array.from({ length: itemCount }, () => numeric);
293
+ case "text-list":
294
+ return Array.from({ length: itemCount }, () => text);
295
+ case "email": {
296
+ const length = Math.min(
297
+ textRules?.maxLength ?? Infinity,
298
+ Math.max(18, textRules?.minLength ?? 0)
299
+ );
300
+ const domain = length >= 13 ? "@example.com" : "@a.co";
301
+ return "person".padEnd(length - domain.length, "x").slice(0, length - domain.length) + domain;
302
+ }
303
+ case "date":
304
+ return field.date?.format === "time"
305
+ ? "12:00"
306
+ : field.date?.format === "date-time"
307
+ ? "2026-01-01T12:00:00Z"
308
+ : "2026-01-01";
309
+ case "select":
310
+ case "radio":
311
+ return field.select?.hasMany
312
+ ? field.select.options.slice(0, 1).map((option) => option.value)
313
+ : field.select?.options[0]?.value;
314
+ case "point":
315
+ return [-0.12, 51.5];
316
+ case "relationship": {
317
+ const value = field.relationship?.polymorphic
318
+ ? {
319
+ relationTo: field.relationship.targets?.[0]?.collectionSlug ?? "COLLECTION",
320
+ id: "RELATED_ID",
321
+ }
322
+ : "RELATED_ID";
323
+ return field.relationship?.hasMany ? [value] : value;
324
+ }
325
+ case "upload":
326
+ return field.upload?.hasMany ? ["UPLOAD_ID"] : "UPLOAD_ID";
327
+ case "json":
328
+ return {};
329
+ default:
330
+ return text;
331
+ }
332
+ }
@@ -0,0 +1,190 @@
1
+ @use "@riducms/ui/styles.scss" as ui;
2
+
3
+ @layer ridu.components {
4
+ .ridu-api-reference {
5
+ display: grid;
6
+ grid-template-columns: 180px minmax(0, 1fr);
7
+ min-height: 0;
8
+ flex: 1;
9
+
10
+ &__operations {
11
+ display: flex;
12
+ flex-direction: column;
13
+ gap: 4px;
14
+ padding: 24px 12px;
15
+ border-inline-end: 1px solid var(--border);
16
+ overflow: auto;
17
+
18
+ button {
19
+ padding: 10px 14px;
20
+ border: 0;
21
+ border-radius: var(--radius-sm);
22
+ background: transparent;
23
+ color: var(--muted-foreground);
24
+ text-align: start;
25
+ white-space: nowrap;
26
+ cursor: pointer;
27
+ &:hover,
28
+ &[aria-current] {
29
+ background: var(--muted);
30
+ color: var(--foreground);
31
+ }
32
+ }
33
+ }
34
+ button:focus-visible,
35
+ pre:focus-visible,
36
+ &__table-scroll:focus-visible,
37
+ summary:focus-visible {
38
+ @include ui.focus-outline;
39
+ }
40
+ &__body {
41
+ min-width: 0;
42
+ overflow: auto;
43
+ padding: 30px;
44
+ }
45
+ &__section {
46
+ margin-bottom: 32px;
47
+ h2,
48
+ h3 {
49
+ margin: 0 0 16px;
50
+ font-weight: 500;
51
+ }
52
+ h2 {
53
+ font-size: 22px;
54
+ span {
55
+ color: var(--muted-foreground);
56
+ font-size: 18px;
57
+ }
58
+ }
59
+ h3 {
60
+ font-size: 18px;
61
+ }
62
+ p {
63
+ margin: 12px 0;
64
+ color: var(--muted-foreground);
65
+ }
66
+ }
67
+ &__endpoint {
68
+ display: flex;
69
+ gap: 12px;
70
+ padding: 14px 16px;
71
+ border: 1px solid var(--border);
72
+ border-radius: var(--radius-sm);
73
+ background: var(--card);
74
+ code {
75
+ overflow-wrap: anywhere;
76
+ }
77
+ }
78
+ code,
79
+ pre {
80
+ font-family: var(--font-mono);
81
+ font-size: 12px;
82
+ }
83
+ &__code-toolbar {
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: space-between;
87
+ gap: 8px;
88
+
89
+ .ridu-tabs__list {
90
+ min-width: 0;
91
+ overflow-x: auto;
92
+ }
93
+ }
94
+
95
+ &__notice {
96
+ padding-inline-start: 14px;
97
+ border-inline-start: 2px solid var(--border);
98
+ }
99
+ &__parameters {
100
+ margin: 0;
101
+ > div {
102
+ display: grid;
103
+ grid-template-columns: 130px minmax(0, 1fr);
104
+ gap: 12px;
105
+ padding-block: 12px;
106
+ border-top: 1px solid var(--border);
107
+ }
108
+ dd {
109
+ margin: 0;
110
+ color: var(--muted-foreground);
111
+ }
112
+ }
113
+ &__table-scroll {
114
+ overflow: auto;
115
+ }
116
+ table {
117
+ width: 100%;
118
+ border-collapse: collapse;
119
+ text-align: start;
120
+ }
121
+ th,
122
+ td {
123
+ padding: 14px 12px;
124
+ text-align: start;
125
+ vertical-align: top;
126
+ border-bottom: 1px solid var(--border);
127
+ overflow-wrap: anywhere;
128
+ }
129
+ th {
130
+ color: var(--muted-foreground);
131
+ font-weight: 400;
132
+ }
133
+ td {
134
+ min-width: 100px;
135
+ p {
136
+ margin: 4px 0;
137
+ }
138
+ }
139
+ &__badges {
140
+ display: flex;
141
+ flex-wrap: wrap;
142
+ gap: 4px;
143
+ span {
144
+ padding: 2px 6px;
145
+ border-radius: var(--radius-sm);
146
+ background: var(--muted);
147
+ font-size: 11px;
148
+ }
149
+ }
150
+ details {
151
+ margin-top: 8px;
152
+ summary {
153
+ cursor: pointer;
154
+ }
155
+ pre {
156
+ max-width: 300px;
157
+ overflow: auto;
158
+ }
159
+ }
160
+ .ridu-json-viewer {
161
+ border: 1px solid var(--border);
162
+ border-radius: var(--radius-sm);
163
+ max-height: 430px;
164
+ overflow: auto;
165
+ }
166
+ }
167
+
168
+ @media (max-width: 700px) {
169
+ .ridu-api-reference {
170
+ grid-template-columns: minmax(0, 1fr);
171
+ grid-template-rows: auto minmax(0, 1fr);
172
+ &__operations {
173
+ flex-direction: row;
174
+ padding: 10px;
175
+ border-inline-end: 0;
176
+ border-bottom: 1px solid var(--border);
177
+ }
178
+ &__body {
179
+ padding: 20px;
180
+ }
181
+ &__parameters > div {
182
+ grid-template-columns: minmax(0, 1fr);
183
+ gap: 4px;
184
+ }
185
+ }
186
+ .ridu-api-reference-drawer__header {
187
+ padding: 20px;
188
+ }
189
+ }
190
+ }
@@ -0,0 +1,74 @@
1
+ <script lang="ts">
2
+ import { Dialog } from "bits-ui";
3
+ import { Button, buttonVariants } from "@riducms/ui";
4
+ import type { SchemaCollection } from "@riducms/protocol";
5
+ import XIcon from "~icons/lucide/x";
6
+
7
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
8
+ import "@admin/features/api-reference/api-reference-drawer.scss";
9
+
10
+ let { collection, documentID }: { collection: SchemaCollection; documentID?: string } = $props();
11
+
12
+ const runtime = getAdminRuntime();
13
+ const i18n = runtime.i18n;
14
+ let open = $state(false);
15
+ let content =
16
+ $state.raw<
17
+ Promise<typeof import("@admin/features/api-reference/api-reference-content.svelte")>
18
+ >();
19
+
20
+ function load() {
21
+ content ??= import("@admin/features/api-reference/api-reference-content.svelte");
22
+ }
23
+
24
+ function changeOpen(value: boolean) {
25
+ open = value;
26
+ if (value) load();
27
+ }
28
+ </script>
29
+
30
+ <Dialog.Root {open} onOpenChange={changeOpen}>
31
+ <Dialog.Trigger class={buttonVariants({ variant: "outline", size: "sm" })}>
32
+ {i18n.t("apiReference:title")}
33
+ </Dialog.Trigger>
34
+
35
+ <Dialog.Portal>
36
+ <Dialog.Overlay class="ridu-api-reference-overlay" />
37
+ <Dialog.Content class="ridu-api-reference-drawer" dir={i18n.direction}>
38
+ <header class="ridu-api-reference-drawer__header">
39
+ <div>
40
+ <Dialog.Title class="ridu-api-reference-drawer__title">
41
+ {i18n.t("apiReference:title")}
42
+ <code>{collection.slug}</code>
43
+ </Dialog.Title>
44
+ <Dialog.Description class="ridu-api-reference-drawer__description">
45
+ {i18n.t("apiReference:description")}
46
+ </Dialog.Description>
47
+ </div>
48
+ <Dialog.Close
49
+ class={buttonVariants({ variant: "ghost", size: "icon" })}
50
+ aria-label={i18n.t("general:close")}
51
+ >
52
+ <XIcon />
53
+ </Dialog.Close>
54
+ </header>
55
+
56
+ {#await content}
57
+ <p class="ridu-api-reference-drawer__message" role="status">
58
+ {i18n.t("apiReference:loading")}
59
+ </p>
60
+ {:then module}
61
+ {#if module && runtime.manifest}
62
+ <module.default {collection} {documentID} manifest={runtime.manifest} />
63
+ {/if}
64
+ {:catch}
65
+ <div class="ridu-api-reference-drawer__message" role="alert">
66
+ <p>{i18n.t("apiReference:loadFailed")}</p>
67
+ <Button variant="outline" onclick={() => window.location.reload()}>
68
+ {i18n.t("general:reloadPage")}
69
+ </Button>
70
+ </div>
71
+ {/await}
72
+ </Dialog.Content>
73
+ </Dialog.Portal>
74
+ </Dialog.Root>
@@ -1,17 +1,19 @@
1
1
  <script lang="ts">
2
2
  import RiduLogo from "@admin/components/brand/ridu-logo.svelte";
3
+ import { adminApplicationName } from "@admin/app-meta";
3
4
  import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
4
5
 
5
6
  const runtime = getAdminRuntime();
7
+
6
8
  const applicationName = $derived(
7
- runtime.manifest?.application.name ?? runtime.i18n.t("general:riduApplication")
9
+ adminApplicationName(runtime.manifest, runtime.i18n, runtime.i18n.t("general:riduApplication"))
8
10
  );
9
11
  </script>
10
12
 
11
13
  <div
12
- class="inline-flex text-foreground"
14
+ class="ridu-auth__logo"
13
15
  role="img"
14
16
  aria-label={runtime.i18n.t("auth:adminBrand", { application: applicationName })}
15
17
  >
16
- <RiduLogo class="h-7" />
18
+ <RiduLogo class="ridu-auth__logo-mark" />
17
19
  </div>
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from "svelte/elements";
3
+ import { FieldFrame, Input, PasswordInput, fieldControlARIA } from "@riducms/ui";
4
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
5
+
6
+ let {
7
+ id,
8
+ label,
9
+ type = "email",
10
+ value = $bindable(""),
11
+ required = true,
12
+ description,
13
+ errors = [],
14
+ showLabel,
15
+ hideLabel,
16
+ oninput,
17
+ ...inputProps
18
+ }: Pick<HTMLInputAttributes, "autocomplete" | "disabled" | "minlength" | "oninput"> & {
19
+ id: string;
20
+ label: string;
21
+ type?: "email" | "password";
22
+ value?: string;
23
+ required?: boolean;
24
+ description?: string;
25
+ errors?: readonly string[];
26
+ showLabel?: string;
27
+ hideLabel?: string;
28
+ } = $props();
29
+
30
+ const { i18n } = getAdminRuntime();
31
+
32
+ let validationMessage = $state<string>();
33
+ let validationExposed = false;
34
+
35
+ const messages = $derived(validationMessage === undefined ? errors : [validationMessage]);
36
+
37
+ function invalid(event: Event & { currentTarget: HTMLInputElement }) {
38
+ // Keep native constraint validation, but render its result through Ridu's field feedback.
39
+ event.preventDefault();
40
+ const input = event.currentTarget;
41
+ validationExposed = true;
42
+ validationMessage = constraintMessage(input);
43
+ input.form?.querySelector<HTMLElement>("input:invalid")?.focus();
44
+ }
45
+
46
+ function constraintMessage(input: HTMLInputElement) {
47
+ if (input.validity.valid) return undefined;
48
+
49
+ if (input.validity.tooShort) {
50
+ return i18n.t("auth:passwordTooShort", { minimum: i18n.formatNumber(input.minLength) });
51
+ }
52
+
53
+ return input.validity.valueMissing
54
+ ? i18n.t("errors:required")
55
+ : input.validity.typeMismatch
56
+ ? i18n.t("auth:invalidEmail")
57
+ : input.validationMessage;
58
+ }
59
+
60
+ function input(event: Event & { currentTarget: HTMLInputElement }) {
61
+ if (validationExposed) validationMessage = constraintMessage(event.currentTarget);
62
+
63
+ oninput?.(event);
64
+ }
65
+ </script>
66
+
67
+ <FieldFrame controlID={id} {label} required={required === true} {description} errors={messages}>
68
+ {#if type === "password"}
69
+ <PasswordInput
70
+ {...inputProps}
71
+ {...fieldControlARIA(id, description !== undefined, messages.length > 0)}
72
+ {id}
73
+ {required}
74
+ bind:value
75
+ showLabel={showLabel ?? i18n.t("auth:showPassword")}
76
+ hideLabel={hideLabel ?? i18n.t("auth:hidePassword")}
77
+ oninvalid={invalid}
78
+ oninput={input}
79
+ />
80
+ {:else}
81
+ <Input
82
+ {...inputProps}
83
+ {...fieldControlARIA(id, description !== undefined, messages.length > 0)}
84
+ {id}
85
+ {type}
86
+ {required}
87
+ bind:value
88
+ oninvalid={invalid}
89
+ oninput={input}
90
+ />
91
+ {/if}
92
+ </FieldFrame>