@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
@@ -1,21 +1,27 @@
1
1
  <script lang="ts">
2
- import { Combobox } from "bits-ui";
3
- import { tick } from "svelte";
2
+ import type { Snippet } from "svelte";
3
+ import {
4
+ Combobox,
5
+ ComboboxInput,
6
+ ComboboxTrigger,
7
+ ComboboxPortal,
8
+ ComboboxViewport,
9
+ ComboboxContent,
10
+ ComboboxItem,
11
+ } from "@admin/components/ui/combobox";
4
12
  import ChevronDownIcon from "~icons/lucide/chevron-down";
5
13
  import ArrowRightIcon from "~icons/lucide/arrow-right";
6
- import ImageIcon from "~icons/lucide/image";
7
14
  import PencilIcon from "~icons/lucide/pencil";
8
15
  import PlusIcon from "~icons/lucide/plus";
16
+ import SearchIcon from "~icons/lucide/search";
9
17
  import XIcon from "~icons/lucide/x";
10
- import { fieldControlARIA, TooltipContent, TooltipRoot, TooltipTrigger } from "@riducms/ui";
11
-
12
- import { Skeleton } from "@admin/components/ui/skeleton";
18
+ import { fieldControlARIA } from "@riducms/ui";
13
19
  import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
14
-
15
20
  import {
16
21
  RelationshipQuickPickerController,
17
22
  type RelationshipQuickPickerTarget,
18
23
  } from "@admin/fields/relationship/relationship-quick-picker-controller.svelte";
24
+ import "@admin/components/ui/combobox/combobox.scss";
19
25
 
20
26
  let {
21
27
  id,
@@ -24,7 +30,6 @@
24
30
  selectedTarget,
25
31
  selectedID,
26
32
  selectedLabel,
27
- selectedInitials,
28
33
  placeholder,
29
34
  readOnly = false,
30
35
  blocked = false,
@@ -32,10 +37,15 @@
32
37
  onPick,
33
38
  onRemove,
34
39
  onBrowse,
40
+ onCreate,
35
41
  onEdit,
36
42
  upload = false,
37
43
  invalid = false,
38
44
  hasDescription = false,
45
+ multiple = false,
46
+ values = [],
47
+ onValuesChange,
48
+ selections,
39
49
  }: {
40
50
  id: string;
41
51
  label: string;
@@ -43,7 +53,6 @@
43
53
  selectedTarget?: string;
44
54
  selectedID?: string;
45
55
  selectedLabel?: string;
46
- selectedInitials?: string;
47
56
  placeholder?: string;
48
57
  readOnly?: boolean;
49
58
  blocked?: boolean;
@@ -51,12 +60,16 @@
51
60
  onPick: (target: string, id: string) => void;
52
61
  onRemove: () => void;
53
62
  onBrowse: (target: string) => void;
63
+ onCreate?: (target: string) => void;
54
64
  onEdit?: () => void;
55
65
  upload?: boolean;
56
66
  invalid?: boolean;
57
67
  hasDescription?: boolean;
68
+ multiple?: boolean;
69
+ values?: string[];
70
+ onValuesChange?: (values: string[]) => void;
71
+ selections?: Snippet;
58
72
  } = $props();
59
-
60
73
  const runtime = getAdminRuntime();
61
74
  const controller = new RelationshipQuickPickerController({
62
75
  runtime,
@@ -67,7 +80,10 @@
67
80
  return selectedTarget;
68
81
  },
69
82
  get selectedID() {
70
- return selectedID;
83
+ return multiple ? undefined : selectedID;
84
+ },
85
+ get excludedValues() {
86
+ return multiple ? values : [];
71
87
  },
72
88
  get locale() {
73
89
  return locale;
@@ -77,236 +93,183 @@
77
93
  onBrowse: (target) => onBrowse(target),
78
94
  });
79
95
  const { comboboxItems, groups, open, query, selectedValue } = $derived(controller);
80
- const {
81
- browseAll,
82
- changeOpen: setOpen,
83
- documentLabel,
84
- initials,
85
- pick,
86
- remove: removeSelection,
87
- setQuery,
88
- toggleOpen,
89
- } = controller;
96
+ const { browseAll, changeOpen, documentLabel, pick, remove, setQuery } = controller;
97
+ let anchor = $state<HTMLDivElement | null>(null);
98
+
90
99
  const selectedCollection = $derived(
91
100
  targets.find((target) => target.collection.slug === selectedTarget)?.collection ??
92
101
  targets[0]?.collection
93
102
  );
94
- const searchLabel = $derived(
95
- targets.length === 1 ? (selectedCollection?.labels.plural ?? label) : label
103
+ const editingBlocked = $derived(readOnly || blocked);
104
+ const canCreate = $derived(
105
+ !editingBlocked &&
106
+ onCreate !== undefined &&
107
+ selectedCollection !== undefined &&
108
+ runtime.collectionOperations[selectedCollection.slug]?.create === true
96
109
  );
97
110
  const controlARIA = $derived(fieldControlARIA(id, hasDescription, invalid));
98
- const editingBlocked = $derived(readOnly || blocked);
99
- let triggerElement = $state<HTMLButtonElement | null>(null);
100
- let searchInput = $state<HTMLInputElement | null>(null);
111
+ const inputValue = $derived(open || multiple ? query : (selectedLabel ?? ""));
112
+ const inputPlaceholder = $derived(
113
+ placeholder ??
114
+ runtime.i18n.t(upload ? "fields:choose" : "fields:select", {
115
+ label: (
116
+ selectedCollection?.labels.singular ??
117
+ runtime.i18n.t(upload ? "fields:asset" : "fields:document")
118
+ ).toLocaleLowerCase(runtime.i18n.language),
119
+ })
120
+ );
121
+
122
+ const selection = $derived(
123
+ multiple
124
+ ? {
125
+ type: "multiple" as const,
126
+ value: values,
127
+ onValueChange: (next: string[]) => {
128
+ if (!editingBlocked) onValuesChange?.(next);
129
+ setQuery("");
130
+ },
131
+ }
132
+ : { type: "single" as const, value: selectedValue, onValueChange: pick }
133
+ );
101
134
 
102
135
  $effect(() => {
103
- if (editingBlocked) setOpen(false);
136
+ if (editingBlocked) changeOpen(false);
104
137
  });
105
-
106
- function changePickerOpen(next: boolean) {
107
- if (editingBlocked && next) return;
108
- setOpen(next);
109
- }
110
-
111
- async function togglePicker() {
112
- if (editingBlocked) return;
113
- if (!toggleOpen()) return;
114
- await tick();
115
- searchInput?.focus();
116
- }
117
-
118
- function beginSearch(event: Event) {
119
- if (editingBlocked) return;
120
- setQuery((event.currentTarget as HTMLInputElement).value);
121
- }
122
-
123
- function choose(value: string) {
124
- if (editingBlocked) return;
125
- pick(value);
126
- }
127
-
128
- function remove(event: MouseEvent) {
129
- event.stopPropagation();
130
- if (editingBlocked) return;
131
- removeSelection();
132
- }
133
-
134
- function browse(event: MouseEvent, target?: string) {
135
- event.stopPropagation();
136
- if (blocked) return;
137
- browseAll(target);
138
- }
139
-
140
- function edit(event: MouseEvent) {
141
- event.stopPropagation();
142
- if (blocked) return;
143
- onEdit?.();
144
- }
145
138
  </script>
146
139
 
147
- <Combobox.Root
148
- type="single"
149
- value={selectedValue}
150
- inputValue={query}
140
+ <Combobox
141
+ {...selection}
142
+ {inputValue}
151
143
  items={comboboxItems}
152
- {open}
153
- onOpenChange={changePickerOpen}
154
- onValueChange={choose}
144
+ bind:open={() => open, changeOpen}
145
+ disabled={editingBlocked}
155
146
  allowDeselect={false}
156
147
  loop
157
148
  >
158
149
  <div
159
- class="flex h-10.5 min-w-0 rounded-[3px] border border-control-border bg-control transition-[background-color,border-color] duration-150 hover:bg-background focus-within:border-primary/65 aria-invalid:!border-destructive/65"
150
+ bind:this={anchor}
151
+ class="ridu-combobox"
160
152
  aria-invalid={invalid}
153
+ data-disabled={editingBlocked}
161
154
  >
162
- <button
163
- bind:this={triggerElement}
164
- {id}
165
- type="button"
166
- class="flex min-w-0 flex-1 items-center gap-2 rounded-s-[3px] px-2.75 text-start outline-none disabled:cursor-not-allowed disabled:opacity-45"
167
- disabled={editingBlocked}
168
- aria-label={label}
169
- {...controlARIA}
170
- aria-haspopup="listbox"
171
- aria-expanded={open}
172
- onclick={togglePicker}
173
- >
174
- {#if selectedID !== undefined}
175
- <span
176
- class="font-mono grid size-[19px] shrink-0 place-items-center rounded-full bg-sidebar-accent text-[8px] font-semibold tracking-[0.04em] text-sidebar-accent-foreground"
177
- aria-hidden="true"
178
- >
179
- {selectedInitials}
180
- </span>
181
- <span class="min-w-0 flex-1 truncate text-[13px] text-foreground-strong">
182
- {selectedLabel}
183
- </span>
184
- {:else}
185
- {#if upload}<ImageIcon class="size-3.5 shrink-0 text-foreground-sub" />{/if}
186
- <span class="min-w-0 flex-1 truncate text-[13px] text-foreground-placeholder">
187
- {placeholder ??
188
- runtime.i18n.t(upload ? "fields:choose" : "fields:select", {
189
- label: (
190
- selectedCollection?.labels.singular ??
191
- runtime.i18n.t(upload ? "fields:asset" : "fields:document")
192
- ).toLocaleLowerCase(runtime.i18n.language),
193
- })}
194
- </span>
195
- {/if}
196
- <ChevronDownIcon class="size-3.5 shrink-0 text-foreground-sub" aria-hidden="true" />
197
- </button>
198
-
199
- {#if selectedID !== undefined && onEdit !== undefined}
200
- <TooltipRoot>
201
- <TooltipTrigger>
202
- {#snippet child({ props })}
155
+ <div class="ridu-combobox-value">
156
+ {@render selections?.()}
157
+ <ComboboxInput
158
+ {id}
159
+ class="ridu-combobox-input"
160
+ autocomplete="off"
161
+ autocorrect="off"
162
+ autocapitalize="none"
163
+ spellcheck={false}
164
+ aria-label={label}
165
+ {...controlARIA}
166
+ placeholder={selectedID || values.length > 0 ? "" : inputPlaceholder}
167
+ clearOnDeselect
168
+ onpointerdown={() => changeOpen(true)}
169
+ oninput={(event) => {
170
+ setQuery(event.currentTarget.value);
171
+ changeOpen(true);
172
+ }}
173
+ >
174
+ {#snippet child({ props })}
175
+ <!-- Keep the visible search controlled: Bits writes the last picked label internally. -->
176
+ <input {...props} value={inputValue} />
177
+ {/snippet}
178
+ </ComboboxInput>
179
+ {#if !multiple && selectedID && !query}
180
+ <div class="ridu-combobox-selection">
181
+ <span>{selectedLabel}</span>
182
+ {#if onEdit}
203
183
  <button
204
- {...props}
205
184
  type="button"
206
- class="grid w-9 shrink-0 place-items-center border-s border-control-border text-foreground-sub outline-none transition-colors hover:bg-background hover:text-foreground focus-visible:text-primary disabled:cursor-not-allowed disabled:opacity-45"
185
+ class="ridu-combobox-icon"
207
186
  disabled={blocked}
208
- onpointerdown={(event) => event.preventDefault()}
209
- onclick={edit}
210
187
  aria-label={runtime.i18n.t("fields:edit", { label: selectedLabel ?? "" })}
211
- >
212
- <PencilIcon class="size-3.25" />
213
- </button>
214
- {/snippet}
215
- </TooltipTrigger>
216
- <TooltipContent>{runtime.i18n.t("fields:editRelationship")}</TooltipContent>
217
- </TooltipRoot>
218
- {/if}
219
-
220
- {#if selectedID !== undefined && !readOnly}
221
- <TooltipRoot>
222
- <TooltipTrigger>
223
- {#snippet child({ props })}
224
- <button
225
- {...props}
226
- type="button"
227
- disabled={editingBlocked}
228
- class="grid w-9 shrink-0 place-items-center border-s border-control-border text-foreground-sub outline-none transition-colors hover:bg-destructive/7 hover:text-destructive focus-visible:text-destructive"
229
188
  onpointerdown={(event) => event.preventDefault()}
230
- onclick={remove}
231
- aria-label={runtime.i18n.t("fields:remove", { label: selectedLabel ?? "" })}
189
+ onclick={onEdit}
232
190
  >
233
- <XIcon class="size-3.5" />
191
+ <PencilIcon />
234
192
  </button>
235
- {/snippet}
236
- </TooltipTrigger>
237
- <TooltipContent>{runtime.i18n.t("fields:removeRelationship")}</TooltipContent>
238
- </TooltipRoot>
193
+ {/if}
194
+ </div>
195
+ {/if}
196
+ </div>
197
+ {#if (selectedID || values.length > 0) && !readOnly}
198
+ <button
199
+ type="button"
200
+ class="ridu-combobox-icon"
201
+ disabled={editingBlocked}
202
+ aria-label={multiple
203
+ ? runtime.i18n.t("general:clearSelection")
204
+ : runtime.i18n.t("fields:remove", { label: selectedLabel ?? "" })}
205
+ onclick={() => (multiple ? onValuesChange?.([]) : remove())}
206
+ >
207
+ <XIcon />
208
+ </button>
239
209
  {/if}
240
-
241
- <TooltipRoot>
242
- <TooltipTrigger>
243
- {#snippet child({ props })}
244
- <button
245
- {...props}
246
- type="button"
247
- disabled={blocked}
248
- class="grid w-9 shrink-0 place-items-center rounded-e-[3px] border-s border-control-border text-foreground-sub outline-none transition-colors hover:bg-background hover:text-foreground focus-visible:text-primary"
249
- onpointerdown={(event) => event.preventDefault()}
250
- onclick={(event) => browse(event, selectedCollection?.slug)}
251
- aria-label={runtime.i18n.t("fields:browse", {
252
- label: (
253
- selectedCollection?.labels.plural ?? runtime.i18n.t("fields:relationships")
254
- ).toLocaleLowerCase(runtime.i18n.language),
255
- })}
256
- >
257
- <PlusIcon class="size-3.5" />
258
- </button>
259
- {/snippet}
260
- </TooltipTrigger>
261
- <TooltipContent>{runtime.i18n.t("fields:addRelationship")}</TooltipContent>
262
- </TooltipRoot>
263
- </div>
264
-
265
- <Combobox.Portal>
266
- <Combobox.Content
267
- customAnchor={triggerElement}
268
- align="start"
269
- sideOffset={4}
270
- class="ridu-popover-enter z-50 flex flex-col overflow-hidden rounded-[4px] border border-control-border bg-popover p-1.5 text-popover-foreground shadow-lg outline-none"
271
- style="width: var(--bits-combobox-anchor-width);"
210
+ <ComboboxTrigger
211
+ type="button"
212
+ class="ridu-combobox-icon ridu-combobox-trigger"
213
+ aria-label={inputPlaceholder}
272
214
  >
273
- <Combobox.Input
274
- bind:ref={searchInput}
275
- disabled={editingBlocked}
276
- aria-label={runtime.i18n.t("fields:searchLabel", { label: searchLabel })}
277
- placeholder={runtime.i18n.t("fields:search", {
278
- label: searchLabel.toLocaleLowerCase(runtime.i18n.language),
215
+ <ChevronDownIcon />
216
+ </ComboboxTrigger>
217
+ {#if canCreate && selectedCollection}
218
+ <button
219
+ type="button"
220
+ class="ridu-combobox-icon ridu-combobox-add"
221
+ aria-label={runtime.i18n.t("fields:add", { label: selectedCollection.labels.singular })}
222
+ onclick={() => {
223
+ changeOpen(false);
224
+ onCreate?.(selectedCollection!.slug);
225
+ }}
226
+ >
227
+ <PlusIcon />
228
+ </button>
229
+ {:else if upload || readOnly}
230
+ <button
231
+ type="button"
232
+ class="ridu-combobox-icon ridu-combobox-add"
233
+ disabled={blocked}
234
+ aria-label={runtime.i18n.t("fields:browse", {
235
+ label: (readOnly
236
+ ? (selectedCollection?.labels.plural ?? label)
237
+ : label
238
+ ).toLocaleLowerCase(runtime.i18n.language),
279
239
  })}
280
- class="mb-1.25 h-[29px] w-full rounded-[3px] border border-control-border bg-control px-[9px] text-[12.5px] text-foreground caret-primary outline-none placeholder:text-foreground-faint focus-visible:border-primary/55"
281
- oninput={beginSearch}
282
- />
240
+ onclick={() => browseAll(selectedCollection?.slug)}
241
+ >
242
+ {#if readOnly}
243
+ <SearchIcon />
244
+ {:else}
245
+ <PlusIcon />
246
+ {/if}
247
+ </button>
248
+ {/if}
249
+ </div>
283
250
 
284
- <Combobox.Viewport class="grid max-h-64 min-h-8 overflow-y-auto">
251
+ <ComboboxPortal>
252
+ <ComboboxContent customAnchor={anchor} align="start" sideOffset={0} class="ridu-combobox-popup">
253
+ <ComboboxViewport>
285
254
  {#each groups as group (group.collection.id)}
286
255
  <div role="group" aria-label={group.collection.labels.plural}>
287
256
  {#if groups.length > 1}
288
- <p
289
- class="px-2 pt-2 pb-1 text-[10px] font-medium tracking-[0.08em] text-foreground-faint uppercase"
290
- >
257
+ <p class="ridu-combobox-group">
291
258
  {group.collection.labels.plural}
292
259
  </p>
293
260
  {/if}
294
261
  {#if group.status === "initial" || group.status === "loading"}
295
- <div
296
- class="grid gap-1 p-0.5"
297
- aria-label={runtime.i18n.t("fields:loading", {
298
- label: group.collection.labels.plural,
299
- })}
262
+ <p
263
+ class="ridu-combobox-message"
264
+ role="status"
265
+ data-ridu-loading-surface="relationship-options"
300
266
  >
301
- <Skeleton class="h-[31px]" />
302
- <Skeleton class="h-[31px]" />
303
- </div>
304
- {:else if group.error !== undefined}
305
- <p class="px-3 py-2 text-[12px] leading-4 text-destructive" role="alert">
306
- {group.error}
267
+ {runtime.i18n.t("fields:loading", { label: group.collection.labels.plural })}
307
268
  </p>
308
- {:else if group.docs.length === 0}
309
- <p class="px-3 py-2 text-[12px] text-foreground-faint">
269
+ {:else if group.error}
270
+ <p class="ridu-combobox-message" role="alert">{group.error}</p>
271
+ {:else if !group.docs.length && query.trim()}
272
+ <p class="ridu-combobox-message">
310
273
  {runtime.i18n.t("fields:noMatch", {
311
274
  label: group.collection.labels.plural.toLocaleLowerCase(runtime.i18n.language),
312
275
  })}
@@ -314,42 +277,30 @@
314
277
  {:else}
315
278
  {#each group.docs as document (document.id)}
316
279
  {const optionLabel = documentLabel(group.collection, document)}
317
- <Combobox.Item
280
+ <ComboboxItem
318
281
  value={JSON.stringify([group.collection.slug, document.id])}
319
282
  label={optionLabel}
320
283
  disabled={editingBlocked}
321
- class="flex w-full cursor-default items-center gap-[9px] rounded-[3px] px-2 py-1.5 text-start text-[12.5px] text-foreground-muted outline-none select-none data-[highlighted]:bg-control data-[selected]:text-foreground-strong"
284
+ class="ridu-combobox-option"
322
285
  >
323
- {#snippet children({ selected })}
324
- <span
325
- class="font-mono grid size-[19px] shrink-0 place-items-center rounded-full bg-sidebar-accent text-[8px] font-semibold text-sidebar-accent-foreground"
326
- aria-hidden="true"
327
- >
328
- {initials(optionLabel)}
329
- </span>
330
- <span class="min-w-0 flex-1 truncate">{optionLabel}</span>
331
- <span class="font-mono text-[9.5px] text-primary" aria-hidden="true">
332
- {selected ? "✓" : ""}
333
- </span>
334
- {/snippet}
335
- </Combobox.Item>
286
+ {optionLabel}
287
+ </ComboboxItem>
336
288
  {/each}
337
289
  {/if}
338
290
  <button
339
291
  type="button"
340
- disabled={editingBlocked}
341
- class="mt-0.5 flex w-full items-center gap-1 px-2 py-1.5 text-start text-[11.5px] text-primary outline-none hover:text-primary-hover focus-visible:text-primary-hover"
292
+ class="ridu-combobox-browse"
293
+ disabled={blocked}
342
294
  onpointerdown={(event) => event.preventDefault()}
343
- onclick={(event) => browse(event, group.collection.slug)}
295
+ onclick={() => browseAll(group.collection.slug)}
344
296
  >
345
297
  {runtime.i18n.t("fields:browseAll", {
346
298
  label: group.collection.labels.plural.toLocaleLowerCase(runtime.i18n.language),
347
- })}
348
- <ArrowRightIcon class="size-3 rtl:rotate-180" aria-hidden="true" />
299
+ })}<ArrowRightIcon />
349
300
  </button>
350
301
  </div>
351
302
  {/each}
352
- </Combobox.Viewport>
353
- </Combobox.Content>
354
- </Combobox.Portal>
355
- </Combobox.Root>
303
+ </ComboboxViewport>
304
+ </ComboboxContent>
305
+ </ComboboxPortal>
306
+ </Combobox>
@@ -0,0 +1,120 @@
1
+ <script lang="ts">
2
+ import { useSortable } from "@dnd-kit-svelte/svelte/sortable";
3
+ import { getAdminI18n } from "@riducms/plugin";
4
+ import type { AdminDocument } from "@admin/core/api/admin-client";
5
+ import FileIcon from "~icons/lucide/file";
6
+ import PencilIcon from "~icons/lucide/pencil";
7
+ import XIcon from "~icons/lucide/x";
8
+ import GripVerticalIcon from "~icons/lucide/grip-vertical";
9
+ import "@admin/fields/relationship/relationship.scss";
10
+
11
+ let {
12
+ id,
13
+ index,
14
+ label,
15
+ document,
16
+ upload = false,
17
+ sortable = false,
18
+ readOnly = false,
19
+ blocked = false,
20
+ onEdit,
21
+ onRemove,
22
+ }: {
23
+ id: string;
24
+ index: number;
25
+ label: string;
26
+ document?: AdminDocument;
27
+ upload?: boolean;
28
+ sortable?: boolean;
29
+ readOnly?: boolean;
30
+ blocked?: boolean;
31
+ onEdit: () => void;
32
+ onRemove: () => void;
33
+ } = $props();
34
+
35
+ const i18n = getAdminI18n();
36
+ const sortableItem = useSortable({
37
+ id: () => id,
38
+ index: () => index,
39
+ disabled: () => !sortable || readOnly || blocked,
40
+ });
41
+ const metadata = $derived(
42
+ [
43
+ typeof document?.filesize === "number"
44
+ ? i18n.formatNumber(document.filesize, { style: "unit", unit: "byte", unitDisplay: "long" })
45
+ : undefined,
46
+ typeof document?.width === "number" && typeof document?.height === "number"
47
+ ? `${document.width} × ${document.height}`
48
+ : undefined,
49
+ typeof document?.mimeType === "string" ? document.mimeType : undefined,
50
+ ]
51
+ .filter(Boolean)
52
+ .join(" — ")
53
+ );
54
+ </script>
55
+
56
+ <!-- dnd-kit's popover reset belongs on the drag container, outside the styled surface. -->
57
+ <div
58
+ class="ridu-relationship-selection"
59
+ data-dragging={sortableItem.isDragging.current}
60
+ {@attach sortable && sortableItem.ref}
61
+ >
62
+ <div
63
+ class={[
64
+ upload ? "ridu-upload-reference" : "ridu-relationship-chip",
65
+ { "ridu-upload-reference--compact": upload && sortable },
66
+ ]}
67
+ >
68
+ {#if sortable}
69
+ <button
70
+ type="button"
71
+ class="ridu-relationship-grip"
72
+ hidden={readOnly}
73
+ disabled={blocked || readOnly}
74
+ aria-label={i18n.t("fields:drag", { label })}
75
+ {@attach sortableItem.handleRef}
76
+ >
77
+ <GripVerticalIcon />
78
+ </button>
79
+ {/if}
80
+ {#if upload}
81
+ <div class="ridu-upload-reference__thumbnail">
82
+ {#if typeof document?.mimeType === "string" && document.mimeType.startsWith("image/") && typeof document.url === "string"}
83
+ <img src={document.url} alt="" />
84
+ {:else}
85
+ <FileIcon />
86
+ {/if}
87
+ </div>
88
+ {/if}
89
+ <div class="ridu-relationship-label">
90
+ <button type="button" class="ridu-relationship-title" disabled={blocked} onclick={onEdit}>
91
+ {label}
92
+ </button>
93
+ {#if upload && !sortable && metadata}
94
+ <span class="ridu-upload-reference__metadata">
95
+ {metadata}
96
+ </span>
97
+ {/if}
98
+ </div>
99
+ <button
100
+ type="button"
101
+ class="ridu-relationship-action"
102
+ disabled={blocked}
103
+ aria-label={i18n.t("fields:edit", { label })}
104
+ onclick={onEdit}
105
+ >
106
+ <PencilIcon />
107
+ </button>
108
+ {#if !readOnly}
109
+ <button
110
+ type="button"
111
+ class="ridu-relationship-action"
112
+ disabled={blocked}
113
+ aria-label={i18n.t("fields:remove", { label })}
114
+ onclick={onRemove}
115
+ >
116
+ <XIcon />
117
+ </button>
118
+ {/if}
119
+ </div>
120
+ </div>