@byline/admin 2.5.1 → 2.6.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 (260) hide show
  1. package/dist/fields/array/array-field.d.ts +14 -0
  2. package/dist/fields/array/array-field.js +177 -0
  3. package/dist/fields/array/array-field.module.js +11 -0
  4. package/dist/fields/array/array-field_module.css +32 -0
  5. package/dist/fields/blocks/blocks-field.d.ts +13 -0
  6. package/dist/fields/blocks/blocks-field.js +245 -0
  7. package/dist/fields/blocks/blocks-field.module.js +26 -0
  8. package/dist/fields/blocks/blocks-field_module.css +107 -0
  9. package/dist/fields/checkbox/checkbox-field.d.ts +16 -0
  10. package/dist/fields/checkbox/checkbox-field.js +28 -0
  11. package/dist/fields/checkbox/checkbox-field.module.js +6 -0
  12. package/dist/fields/checkbox/checkbox-field_module.css +4 -0
  13. package/dist/fields/column-formatter.d.ts +20 -0
  14. package/dist/fields/column-formatter.js +15 -0
  15. package/dist/fields/date-time-formatter.d.ts +16 -0
  16. package/dist/fields/date-time-formatter.js +8 -0
  17. package/dist/fields/datetime/datetime-field.d.ts +16 -0
  18. package/dist/fields/datetime/datetime-field.js +37 -0
  19. package/dist/fields/datetime/datetime-field.module.js +5 -0
  20. package/dist/fields/datetime/datetime-field_module.css +4 -0
  21. package/dist/fields/draggable-context-menu.d.ts +6 -0
  22. package/dist/fields/draggable-context-menu.js +85 -0
  23. package/dist/fields/draggable-context-menu.module.js +15 -0
  24. package/dist/fields/draggable-context-menu_module.css +91 -0
  25. package/dist/fields/field-helpers.d.ts +26 -0
  26. package/dist/fields/field-helpers.js +50 -0
  27. package/dist/fields/field-renderer.d.ts +37 -0
  28. package/dist/fields/field-renderer.js +206 -0
  29. package/dist/fields/field-renderer.module.js +8 -0
  30. package/dist/fields/field-renderer_module.css +11 -0
  31. package/dist/fields/field-services-context.d.ts +16 -0
  32. package/dist/fields/field-services-context.js +13 -0
  33. package/dist/fields/field-services-types.d.ts +63 -0
  34. package/dist/fields/field-services-types.js +1 -0
  35. package/dist/fields/file/file-field.d.ts +19 -0
  36. package/dist/fields/file/file-field.js +225 -0
  37. package/dist/fields/file/file-field.module.js +18 -0
  38. package/dist/fields/file/file-field_module.css +131 -0
  39. package/dist/fields/file/file-upload-field.d.ts +21 -0
  40. package/dist/fields/file/file-upload-field.js +130 -0
  41. package/dist/fields/file/file-upload-field.module.js +15 -0
  42. package/dist/fields/file/file-upload-field_module.css +74 -0
  43. package/dist/fields/group/group-field.d.ts +15 -0
  44. package/dist/fields/group/group-field.js +59 -0
  45. package/dist/fields/group/group-field.module.js +9 -0
  46. package/dist/fields/group/group-field_module.css +27 -0
  47. package/dist/fields/image/image-field.d.ts +19 -0
  48. package/dist/fields/image/image-field.js +241 -0
  49. package/dist/fields/image/image-field.module.js +22 -0
  50. package/dist/fields/image/image-field_module.css +121 -0
  51. package/dist/fields/image/image-upload-field.d.ts +21 -0
  52. package/dist/fields/image/image-upload-field.js +190 -0
  53. package/dist/fields/image/image-upload-field.module.js +19 -0
  54. package/dist/fields/image/image-upload-field_module.css +92 -0
  55. package/dist/fields/local-date-time.d.ts +27 -0
  56. package/dist/fields/local-date-time.js +49 -0
  57. package/dist/fields/locale-badge.d.ts +18 -0
  58. package/dist/fields/locale-badge.js +10 -0
  59. package/dist/fields/locale-badge.module.js +5 -0
  60. package/dist/fields/locale-badge_module.css +27 -0
  61. package/dist/fields/numerical/numerical-field.d.ts +18 -0
  62. package/dist/fields/numerical/numerical-field.js +74 -0
  63. package/dist/fields/relation/relation-display.d.ts +40 -0
  64. package/dist/fields/relation/relation-display.js +58 -0
  65. package/dist/fields/relation/relation-display.module.js +9 -0
  66. package/dist/fields/relation/relation-display_module.css +21 -0
  67. package/dist/fields/relation/relation-field.d.ts +18 -0
  68. package/dist/fields/relation/relation-field.js +138 -0
  69. package/dist/fields/relation/relation-field.module.js +13 -0
  70. package/dist/fields/relation/relation-field_module.css +62 -0
  71. package/dist/fields/relation/relation-picker.d.ts +49 -0
  72. package/dist/fields/relation/relation-picker.js +236 -0
  73. package/dist/fields/relation/relation-picker.module.js +26 -0
  74. package/dist/fields/relation/relation-picker_module.css +124 -0
  75. package/dist/fields/relation/relation-summary.d.ts +31 -0
  76. package/dist/fields/relation/relation-summary.js +50 -0
  77. package/dist/fields/relation/relation-summary.module.js +11 -0
  78. package/dist/fields/relation/relation-summary_module.css +37 -0
  79. package/dist/fields/select/select-field.d.ts +16 -0
  80. package/dist/fields/select/select-field.js +50 -0
  81. package/dist/fields/select/select-field.module.js +5 -0
  82. package/dist/fields/select/select-field_module.css +4 -0
  83. package/dist/fields/sortable-item.d.ts +15 -0
  84. package/dist/fields/sortable-item.js +81 -0
  85. package/dist/fields/sortable-item.module.js +22 -0
  86. package/dist/fields/sortable-item_module.css +124 -0
  87. package/dist/fields/text/text-field.d.ts +20 -0
  88. package/dist/fields/text/text-field.js +104 -0
  89. package/dist/fields/text/text-field.module.js +6 -0
  90. package/dist/fields/text/text-field_module.css +5 -0
  91. package/dist/fields/text-area/text-area-field.d.ts +20 -0
  92. package/dist/fields/text-area/text-area-field.js +105 -0
  93. package/dist/fields/text-area/text-area-field.module.js +6 -0
  94. package/dist/fields/text-area/text-area-field_module.css +5 -0
  95. package/dist/fields/use-field-change-handler.d.ts +23 -0
  96. package/dist/fields/use-field-change-handler.js +52 -0
  97. package/dist/forms/document-actions.d.ts +48 -0
  98. package/dist/forms/document-actions.js +475 -0
  99. package/dist/forms/document-actions.module.js +34 -0
  100. package/dist/forms/document-actions_module.css +118 -0
  101. package/dist/forms/form-context.d.ts +89 -0
  102. package/dist/forms/form-context.js +466 -0
  103. package/dist/forms/form-renderer.d.ts +98 -0
  104. package/dist/forms/form-renderer.js +597 -0
  105. package/dist/forms/form-renderer.module.js +46 -0
  106. package/dist/forms/form-renderer_module.css +245 -0
  107. package/dist/forms/navigation-guard.d.ts +54 -0
  108. package/dist/forms/navigation-guard.js +22 -0
  109. package/dist/forms/path-widget.d.ts +36 -0
  110. package/dist/forms/path-widget.js +116 -0
  111. package/dist/forms/path-widget.module.js +8 -0
  112. package/dist/forms/path-widget_module.css +29 -0
  113. package/dist/forms/upload-executor.d.ts +57 -0
  114. package/dist/forms/upload-executor.js +94 -0
  115. package/dist/lib/translate-validation-error.d.ts +36 -0
  116. package/dist/lib/translate-validation-error.js +11 -0
  117. package/dist/modules/admin-account/commands.d.ts +2 -1
  118. package/dist/modules/admin-account/commands.js +13 -2
  119. package/dist/modules/admin-account/components/change-password.js +45 -36
  120. package/dist/modules/admin-account/components/container.js +185 -134
  121. package/dist/modules/admin-account/components/preferences.d.ts +8 -0
  122. package/dist/modules/admin-account/components/preferences.js +152 -0
  123. package/dist/modules/admin-account/components/preferences.module.js +11 -0
  124. package/dist/modules/admin-account/components/preferences_module.css +41 -0
  125. package/dist/modules/admin-account/components/update.js +50 -31
  126. package/dist/modules/admin-account/index.d.ts +3 -3
  127. package/dist/modules/admin-account/index.js +2 -2
  128. package/dist/modules/admin-account/schemas.d.ts +4 -0
  129. package/dist/modules/admin-account/schemas.js +4 -1
  130. package/dist/modules/admin-account/service.d.ts +1 -0
  131. package/dist/modules/admin-account/service.js +8 -0
  132. package/dist/modules/admin-permissions/components/inspector.js +31 -41
  133. package/dist/modules/admin-roles/components/create.js +43 -26
  134. package/dist/modules/admin-roles/components/permissions.js +26 -35
  135. package/dist/modules/admin-roles/components/update.js +26 -16
  136. package/dist/modules/admin-users/components/create.js +60 -40
  137. package/dist/modules/admin-users/components/roles.js +9 -15
  138. package/dist/modules/admin-users/components/set-password.js +30 -31
  139. package/dist/modules/admin-users/components/update.js +58 -39
  140. package/dist/modules/admin-users/dto.js +1 -0
  141. package/dist/modules/admin-users/repository.d.ts +17 -0
  142. package/dist/modules/admin-users/schemas.d.ts +4 -0
  143. package/dist/modules/admin-users/schemas.js +6 -2
  144. package/dist/modules/auth/components/sign-in-form.js +10 -8
  145. package/dist/presentation/group.d.ts +27 -0
  146. package/dist/presentation/group.js +14 -0
  147. package/dist/presentation/group.module.js +6 -0
  148. package/dist/presentation/group_module.css +19 -0
  149. package/dist/presentation/row.d.ts +25 -0
  150. package/dist/presentation/row.js +8 -0
  151. package/dist/presentation/row.module.js +5 -0
  152. package/dist/presentation/row_module.css +18 -0
  153. package/dist/presentation/tabs.d.ts +25 -0
  154. package/dist/presentation/tabs.js +39 -0
  155. package/dist/presentation/tabs.module.js +10 -0
  156. package/dist/presentation/tabs_module.css +68 -0
  157. package/dist/react.d.ts +66 -0
  158. package/dist/react.js +36 -0
  159. package/dist/services/admin-services-types.d.ts +16 -0
  160. package/dist/widgets/diff-viewer/diff-modal.d.ts +22 -0
  161. package/dist/widgets/diff-viewer/diff-modal.js +149 -0
  162. package/dist/widgets/diff-viewer/diff-modal.module.js +14 -0
  163. package/dist/widgets/diff-viewer/diff-modal_module.css +56 -0
  164. package/dist/widgets/status-badge/status-badge.d.ts +25 -0
  165. package/dist/widgets/status-badge/status-badge.js +37 -0
  166. package/dist/widgets/status-badge/status-badge.module.js +7 -0
  167. package/dist/widgets/status-badge/status-badge_module.css +20 -0
  168. package/package.json +14 -4
  169. package/src/fields/array/array-field.module.css +48 -0
  170. package/src/fields/array/array-field.tsx +267 -0
  171. package/src/fields/blocks/blocks-field.module.css +148 -0
  172. package/src/fields/blocks/blocks-field.tsx +323 -0
  173. package/src/fields/checkbox/checkbox-field.module.css +4 -0
  174. package/src/fields/checkbox/checkbox-field.tsx +54 -0
  175. package/src/fields/column-formatter.tsx +31 -0
  176. package/src/fields/date-time-formatter.tsx +22 -0
  177. package/src/fields/datetime/datetime-field.module.css +13 -0
  178. package/src/fields/datetime/datetime-field.tsx +54 -0
  179. package/src/fields/draggable-context-menu.module.css +127 -0
  180. package/src/fields/draggable-context-menu.tsx +87 -0
  181. package/src/fields/field-helpers.ts +69 -0
  182. package/src/fields/field-renderer.module.css +22 -0
  183. package/src/fields/field-renderer.tsx +288 -0
  184. package/src/fields/field-services-context.tsx +35 -0
  185. package/src/fields/field-services-types.ts +68 -0
  186. package/src/fields/file/file-field.module.css +153 -0
  187. package/src/fields/file/file-field.tsx +286 -0
  188. package/src/fields/file/file-upload-field.module.css +101 -0
  189. package/src/fields/file/file-upload-field.tsx +187 -0
  190. package/src/fields/group/group-field.module.css +43 -0
  191. package/src/fields/group/group-field.tsx +84 -0
  192. package/src/fields/image/image-field.module.css +155 -0
  193. package/src/fields/image/image-field.tsx +306 -0
  194. package/src/fields/image/image-upload-field.module.css +123 -0
  195. package/src/fields/image/image-upload-field.tsx +276 -0
  196. package/src/fields/local-date-time.tsx +88 -0
  197. package/src/fields/locale-badge.module.css +37 -0
  198. package/src/fields/locale-badge.tsx +32 -0
  199. package/src/fields/numerical/numerical-field.tsx +114 -0
  200. package/src/fields/relation/relation-display.module.css +36 -0
  201. package/src/fields/relation/relation-display.tsx +130 -0
  202. package/src/fields/relation/relation-field.module.css +83 -0
  203. package/src/fields/relation/relation-field.tsx +211 -0
  204. package/src/fields/relation/relation-picker.module.css +168 -0
  205. package/src/fields/relation/relation-picker.tsx +326 -0
  206. package/src/fields/relation/relation-summary.module.css +55 -0
  207. package/src/fields/relation/relation-summary.tsx +123 -0
  208. package/src/fields/select/select-field.module.css +13 -0
  209. package/src/fields/select/select-field.tsx +61 -0
  210. package/src/fields/sortable-item.module.css +167 -0
  211. package/src/fields/sortable-item.tsx +106 -0
  212. package/src/fields/text/text-field.module.css +13 -0
  213. package/src/fields/text/text-field.tsx +146 -0
  214. package/src/fields/text-area/text-area-field.module.css +13 -0
  215. package/src/fields/text-area/text-area-field.tsx +147 -0
  216. package/src/fields/use-field-change-handler.ts +112 -0
  217. package/src/forms/document-actions.module.css +160 -0
  218. package/src/forms/document-actions.tsx +482 -0
  219. package/src/forms/form-context.tsx +704 -0
  220. package/src/forms/form-renderer.module.css +321 -0
  221. package/src/forms/form-renderer.tsx +891 -0
  222. package/src/forms/navigation-guard.tsx +98 -0
  223. package/src/forms/path-widget.module.css +41 -0
  224. package/src/forms/path-widget.test.tsx +217 -0
  225. package/src/forms/path-widget.tsx +183 -0
  226. package/src/forms/upload-executor.ts +192 -0
  227. package/src/lib/translate-validation-error.ts +56 -0
  228. package/src/modules/admin-account/commands.ts +13 -0
  229. package/src/modules/admin-account/components/change-password.tsx +46 -31
  230. package/src/modules/admin-account/components/container.tsx +83 -38
  231. package/src/modules/admin-account/components/preferences.module.css +60 -0
  232. package/src/modules/admin-account/components/preferences.tsx +203 -0
  233. package/src/modules/admin-account/components/update.tsx +53 -27
  234. package/src/modules/admin-account/index.ts +3 -0
  235. package/src/modules/admin-account/schemas.ts +13 -0
  236. package/src/modules/admin-account/service.ts +12 -0
  237. package/src/modules/admin-permissions/components/inspector.tsx +22 -14
  238. package/src/modules/admin-roles/components/create.tsx +51 -23
  239. package/src/modules/admin-roles/components/permissions.tsx +25 -21
  240. package/src/modules/admin-roles/components/update.tsx +37 -19
  241. package/src/modules/admin-users/components/create.tsx +63 -34
  242. package/src/modules/admin-users/components/roles.tsx +9 -8
  243. package/src/modules/admin-users/components/set-password.tsx +34 -28
  244. package/src/modules/admin-users/components/update.tsx +58 -36
  245. package/src/modules/admin-users/dto.ts +1 -0
  246. package/src/modules/admin-users/repository.ts +17 -0
  247. package/src/modules/admin-users/schemas.ts +12 -0
  248. package/src/modules/auth/components/sign-in-form.tsx +14 -8
  249. package/src/presentation/group.module.css +41 -0
  250. package/src/presentation/group.tsx +40 -0
  251. package/src/presentation/row.module.css +32 -0
  252. package/src/presentation/row.tsx +33 -0
  253. package/src/presentation/tabs.module.css +107 -0
  254. package/src/presentation/tabs.tsx +84 -0
  255. package/src/react.ts +84 -0
  256. package/src/services/admin-services-types.ts +18 -0
  257. package/src/widgets/diff-viewer/diff-modal.module.css +79 -0
  258. package/src/widgets/diff-viewer/diff-modal.tsx +186 -0
  259. package/src/widgets/status-badge/status-badge.module.css +31 -0
  260. package/src/widgets/status-badge/status-badge.tsx +71 -0
@@ -0,0 +1,87 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * This Source Code is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ *
8
+ * Copyright (c) Infonomic Company Limited
9
+ */
10
+
11
+ import { useTranslation } from '@byline/i18n/react'
12
+ import {
13
+ DeleteIcon,
14
+ Dropdown as DropdownMenu,
15
+ EllipsisIcon,
16
+ IconButton,
17
+ PlusIcon,
18
+ } from '@byline/ui/react'
19
+ import cx from 'classnames'
20
+
21
+ import styles from './draggable-context-menu.module.css'
22
+
23
+ interface DraggableContextMenuProps {
24
+ onAddBelow?: () => void
25
+ onRemove?: () => void
26
+ }
27
+
28
+ export function DraggableContextMenu({
29
+ onAddBelow,
30
+ onRemove,
31
+ }: DraggableContextMenuProps): React.JSX.Element {
32
+ const { t } = useTranslation('byline-admin')
33
+ const itemClass = cx('byline-draggable-menu-item', styles.item)
34
+ const rowClass = cx('byline-draggable-menu-row', styles.row)
35
+ const iconSlotClass = cx('byline-draggable-menu-icon-slot', styles['icon-slot'])
36
+ const labelClass = cx('byline-draggable-menu-label', styles.label)
37
+
38
+ return (
39
+ <DropdownMenu.Root modal={false}>
40
+ <DropdownMenu.Trigger render={<IconButton variant="text" size="sm" />}>
41
+ <EllipsisIcon width="16px" height="16px" />
42
+ </DropdownMenu.Trigger>
43
+
44
+ <DropdownMenu.Portal>
45
+ <DropdownMenu.Content
46
+ align="end"
47
+ sideOffset={0}
48
+ className={cx('byline-draggable-menu', styles.menu)}
49
+ >
50
+ <DropdownMenu.Item className={itemClass} onClick={onAddBelow}>
51
+ <div className={rowClass}>
52
+ <span className={iconSlotClass}>
53
+ <PlusIcon width="18px" height="18px" />
54
+ </span>
55
+ <span className={labelClass}>{t('fields.draggableMenu.addBelow')}</span>
56
+ </div>
57
+ </DropdownMenu.Item>
58
+ <DropdownMenu.Separator
59
+ className={cx('byline-draggable-menu-separator', styles.separator)}
60
+ />
61
+ <DropdownMenu.Item className={itemClass} onClick={onRemove}>
62
+ <div className={rowClass}>
63
+ <div className={rowClass}>
64
+ <span className={iconSlotClass}>
65
+ <DeleteIcon
66
+ width="18px"
67
+ height="18px"
68
+ svgClassName={cx('byline-draggable-menu-delete-icon', styles['delete-icon'])}
69
+ />
70
+ </span>
71
+ <span
72
+ className={cx(
73
+ 'byline-draggable-menu-label byline-draggable-menu-label-danger',
74
+ styles.label,
75
+ styles['label-danger']
76
+ )}
77
+ >
78
+ {t('fields.draggableMenu.remove')}
79
+ </span>
80
+ </div>
81
+ </div>
82
+ </DropdownMenu.Item>
83
+ </DropdownMenu.Content>
84
+ </DropdownMenu.Portal>
85
+ </DropdownMenu.Root>
86
+ )
87
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type { Field } from '@byline/core'
10
+ import { resolveFieldDefaultValue } from '@byline/core'
11
+
12
+ export const placeholderStoredFileValue = {
13
+ fileId: crypto.randomUUID(),
14
+ filename: 'placeholder',
15
+ originalFilename: 'placeholder',
16
+ mimeType: 'application/octet-stream',
17
+ fileSize: 0,
18
+ storageProvider: 'placeholder',
19
+ storagePath: 'pending',
20
+ storageUrl: null,
21
+ fileHash: null,
22
+ imageWidth: null,
23
+ imageHeight: null,
24
+ imageFormat: null,
25
+ processingStatus: 'pending',
26
+ thumbnailGenerated: false,
27
+ }
28
+
29
+ export const placeholderForField = (f: Field): any => {
30
+ switch (f.type) {
31
+ case 'text':
32
+ case 'textArea':
33
+ return ''
34
+ case 'checkbox':
35
+ return false
36
+ case 'integer':
37
+ return 0
38
+ case 'counter':
39
+ // Allocator-assigned in the lifecycle layer; absent at form-init time.
40
+ return undefined
41
+ case 'richText':
42
+ case 'datetime':
43
+ return undefined
44
+ case 'select':
45
+ return ''
46
+ case 'file':
47
+ case 'image':
48
+ return placeholderStoredFileValue
49
+ default:
50
+ return null
51
+ }
52
+ }
53
+
54
+ export const defaultScalarForField = async (
55
+ f: Field,
56
+ getFieldValues: () => Record<string, any>
57
+ ): Promise<any> => {
58
+ const schemaDefault = await resolveFieldDefaultValue(f, {
59
+ data: getFieldValues(),
60
+ now: () => new Date(),
61
+ uuid: () => crypto.randomUUID(),
62
+ })
63
+
64
+ if (schemaDefault !== undefined) {
65
+ return schemaDefault
66
+ }
67
+
68
+ return placeholderForField(f)
69
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * FieldRenderer — main field-type switch wrapper.
3
+ *
4
+ * Override handles:
5
+ * .byline-field-localized-wrap — wrapper added when a non-self-badging
6
+ * field needs an absolutely-positioned
7
+ * locale badge in the corner
8
+ * .byline-field-localized-badge — the absolutely-positioned badge slot
9
+ */
10
+
11
+ .localized-wrap,
12
+ :global(.byline-field-localized-wrap) {
13
+ position: relative;
14
+ }
15
+
16
+ .localized-badge,
17
+ :global(.byline-field-localized-badge) {
18
+ position: absolute;
19
+ top: 0;
20
+ right: 0;
21
+ line-height: 1;
22
+ }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import type {
10
+ ArrayField as ArrayFieldType,
11
+ BlocksField as BlocksFieldType,
12
+ Field,
13
+ FieldComponentSlots,
14
+ GroupField as GroupFieldType,
15
+ RichTextEditorComponent,
16
+ } from '@byline/core'
17
+ import { getClientConfig } from '@byline/core'
18
+ import cx from 'classnames'
19
+
20
+ import { ArrayField } from './array/array-field'
21
+ import { BlocksField } from './blocks/blocks-field'
22
+ import { CheckboxField } from './checkbox/checkbox-field'
23
+ import { DateTimeField } from './datetime/datetime-field'
24
+ import styles from './field-renderer.module.css'
25
+ import { FileField } from './file/file-field'
26
+ import { GroupField } from './group/group-field'
27
+ import { ImageField } from './image/image-field'
28
+ import { LocaleBadge } from './locale-badge'
29
+ import { NumericalField } from './numerical/numerical-field'
30
+ import { RelationField } from './relation/relation-field'
31
+ import { SelectField } from './select/select-field'
32
+ import { TextField } from './text/text-field'
33
+ import { TextAreaField } from './text-area/text-area-field'
34
+ import { useFieldChangeHandler } from './use-field-change-handler'
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // FieldRenderer — the main field type switch. Delegates to the appropriate
38
+ // field widget based on `field.type`.
39
+ // ---------------------------------------------------------------------------
40
+
41
+ interface FieldRendererProps {
42
+ field: Field
43
+ defaultValue?: any
44
+ basePath?: string
45
+ disableSorting?: boolean
46
+ hideLabel?: boolean
47
+ /** Collection path (e.g. `'media'`) forwarded to upload-capable fields. */
48
+ collectionPath?: string
49
+ /**
50
+ * The active content locale (e.g. `'en'`, `'fr'`). When provided and
51
+ * `field.localized === true`, a small locale badge is shown so the editor
52
+ * knows they are working on a localised field in the current language.
53
+ */
54
+ contentLocale?: string
55
+ /**
56
+ * Optional UI component slot overrides from the admin config.
57
+ * Forwarded to value-field widgets that support custom slots.
58
+ */
59
+ components?: FieldComponentSlots
60
+ /**
61
+ * Per-field rich-text editor component override from the admin config.
62
+ * Takes precedence over the globally registered
63
+ * `ClientConfig.fields.richText.editor` for this single field.
64
+ * Ignored when `field.type !== 'richText'`.
65
+ */
66
+ editor?: RichTextEditorComponent
67
+ }
68
+
69
+ export const FieldRenderer = ({
70
+ field,
71
+ defaultValue,
72
+ basePath,
73
+ disableSorting,
74
+ hideLabel,
75
+ collectionPath,
76
+ contentLocale,
77
+ components,
78
+ editor,
79
+ }: FieldRendererProps) => {
80
+ const path = basePath ? `${basePath}.${field.name}` : field.name
81
+ const htmlId = path.replace(/[[\].]/g, '-')
82
+
83
+ const handleChange = useFieldChangeHandler(field, path)
84
+
85
+ // When a locale is active and the field is localised, inject a badge into
86
+ // the field label so the editor knows they are editing locale-specific content.
87
+ const isLocalised = (field as any).localized === true
88
+
89
+ const badge =
90
+ isLocalised && contentLocale && !hideLabel ? <LocaleBadge locale={contentLocale} /> : null
91
+
92
+ /**
93
+ * Render the underlying field widget. If the field is localised, we wrap it
94
+ * so we can append the locale badge after the label.
95
+ */
96
+ const renderField = () => {
97
+ switch (field.type) {
98
+ case 'text':
99
+ return (
100
+ <TextField
101
+ field={hideLabel ? { ...field, label: undefined } : field}
102
+ defaultValue={defaultValue}
103
+ onChange={handleChange}
104
+ path={path}
105
+ id={htmlId}
106
+ locale={isLocalised ? contentLocale : undefined}
107
+ components={components}
108
+ />
109
+ )
110
+ case 'textArea':
111
+ return (
112
+ <TextAreaField
113
+ field={hideLabel ? { ...field, label: undefined } : field}
114
+ defaultValue={defaultValue}
115
+ onChange={handleChange}
116
+ path={path}
117
+ id={htmlId}
118
+ locale={isLocalised ? contentLocale : undefined}
119
+ components={components}
120
+ />
121
+ )
122
+ case 'checkbox':
123
+ return (
124
+ <CheckboxField
125
+ field={hideLabel ? { ...field, label: undefined } : field}
126
+ defaultValue={defaultValue}
127
+ onChange={handleChange}
128
+ path={path}
129
+ id={htmlId}
130
+ />
131
+ )
132
+ case 'select':
133
+ return (
134
+ <SelectField
135
+ field={hideLabel ? { ...field, label: undefined } : field}
136
+ defaultValue={defaultValue}
137
+ onChange={handleChange}
138
+ path={path}
139
+ id={htmlId}
140
+ />
141
+ )
142
+ case 'richText': {
143
+ // Admin-side per-field override takes precedence over the globally
144
+ // registered editor (`ClientConfig.fields.richText.editor`). The
145
+ // override travels via `FieldAdminConfig.fields.<name>.editor` —
146
+ // see admin-types — so React component references stay out of the
147
+ // schema graph that's loaded by the server bootstrap.
148
+ const RichTextEditor = editor ?? getClientConfig().fields?.richText?.editor
149
+ if (!RichTextEditor) {
150
+ throw new Error(
151
+ 'No richText editor registered. Install @byline/richtext-lexical and set ' +
152
+ '`fields.richText.editor` in your admin config.'
153
+ )
154
+ }
155
+ return (
156
+ <RichTextEditor
157
+ field={hideLabel ? { ...field, label: undefined } : field}
158
+ defaultValue={defaultValue}
159
+ onChange={handleChange}
160
+ path={path}
161
+ instanceKey={htmlId}
162
+ locale={isLocalised ? contentLocale : undefined}
163
+ />
164
+ )
165
+ }
166
+ case 'datetime':
167
+ return (
168
+ <DateTimeField
169
+ field={hideLabel ? { ...field, label: undefined } : field}
170
+ defaultValue={defaultValue}
171
+ onChange={handleChange}
172
+ path={path}
173
+ id={htmlId}
174
+ />
175
+ )
176
+ case 'integer':
177
+ return (
178
+ <NumericalField
179
+ field={hideLabel ? { ...field, label: undefined } : field}
180
+ defaultValue={defaultValue}
181
+ onChange={handleChange}
182
+ path={path}
183
+ id={htmlId}
184
+ components={components}
185
+ />
186
+ )
187
+ case 'counter':
188
+ // Counter values are allocator-assigned; force readOnly at the
189
+ // renderer level so the widget is always non-editable regardless
190
+ // of whether the developer set `field.readOnly` explicitly.
191
+ return (
192
+ <NumericalField
193
+ field={
194
+ hideLabel
195
+ ? { ...field, label: undefined, readOnly: true }
196
+ : { ...field, readOnly: true }
197
+ }
198
+ defaultValue={defaultValue}
199
+ onChange={handleChange}
200
+ path={path}
201
+ id={htmlId}
202
+ components={components}
203
+ />
204
+ )
205
+ case 'file':
206
+ return (
207
+ <FileField
208
+ field={hideLabel ? { ...field, label: undefined } : field}
209
+ defaultValue={defaultValue}
210
+ onChange={handleChange}
211
+ path={path}
212
+ collectionPath={collectionPath}
213
+ />
214
+ )
215
+ case 'image':
216
+ return (
217
+ <ImageField
218
+ field={hideLabel ? { ...field, label: undefined } : field}
219
+ defaultValue={defaultValue}
220
+ onChange={handleChange}
221
+ path={path}
222
+ collectionPath={collectionPath}
223
+ />
224
+ )
225
+ case 'relation':
226
+ return (
227
+ <RelationField
228
+ field={hideLabel ? { ...field, label: undefined } : field}
229
+ defaultValue={defaultValue}
230
+ onChange={handleChange}
231
+ path={path}
232
+ id={htmlId}
233
+ />
234
+ )
235
+ case 'group':
236
+ // Render a group field as a fixed-order inline field group.
237
+ return (
238
+ <GroupField
239
+ field={
240
+ hideLabel
241
+ ? ({ ...field, label: undefined } as unknown as GroupFieldType)
242
+ : (field as unknown as GroupFieldType)
243
+ }
244
+ defaultValue={defaultValue}
245
+ path={path}
246
+ />
247
+ )
248
+ case 'blocks':
249
+ if (!field.blocks) return null
250
+ return (
251
+ <BlocksField
252
+ field={field as unknown as BlocksFieldType}
253
+ defaultValue={defaultValue}
254
+ path={path}
255
+ />
256
+ )
257
+ case 'array':
258
+ if (!field.fields) return null
259
+ return (
260
+ <ArrayField
261
+ field={field as unknown as ArrayFieldType}
262
+ defaultValue={defaultValue}
263
+ path={path}
264
+ disableSorting={disableSorting}
265
+ />
266
+ )
267
+ default:
268
+ return null
269
+ }
270
+ }
271
+
272
+ // text and textArea render the badge inside their own Label row;
273
+ // the outer wrapper is only needed for other field types.
274
+ const selfBadge = field.type === 'text' || field.type === 'textArea' || field.type === 'richText'
275
+
276
+ if (badge && !selfBadge) {
277
+ return (
278
+ <div className={cx('byline-field-localized-wrap', styles['localized-wrap'])}>
279
+ {renderField()}
280
+ <span className={cx('byline-field-localized-badge', styles['localized-badge'])}>
281
+ {badge}
282
+ </span>
283
+ </div>
284
+ )
285
+ }
286
+
287
+ return renderField()
288
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { createContext, type ReactNode, useContext } from 'react'
10
+
11
+ import type { BylineFieldServices } from './field-services-types'
12
+
13
+ const FieldServicesContext = createContext<BylineFieldServices | null>(null)
14
+
15
+ interface BylineFieldServicesProviderProps {
16
+ services: BylineFieldServices
17
+ children: ReactNode
18
+ }
19
+
20
+ export const BylineFieldServicesProvider = ({
21
+ services,
22
+ children,
23
+ }: BylineFieldServicesProviderProps) => (
24
+ <FieldServicesContext.Provider value={services}>{children}</FieldServicesContext.Provider>
25
+ )
26
+
27
+ export const useBylineFieldServices = (): BylineFieldServices => {
28
+ const ctx = useContext(FieldServicesContext)
29
+ if (!ctx) {
30
+ throw new Error(
31
+ '@byline/ui: BylineFieldServicesProvider missing. Wrap your admin tree with <BylineFieldServicesProvider services={…} />.'
32
+ )
33
+ }
34
+ return ctx
35
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ /**
10
+ * Framework-neutral function contracts that field/form components in
11
+ * `@byline/ui` need from the host application. The host wires concrete
12
+ * implementations via `BylineFieldServicesProvider` — typically thin
13
+ * adapters around TanStack Start server functions, Next.js server
14
+ * actions, or any other RPC-style transport.
15
+ */
16
+
17
+ import type { StoredFileValue } from '@byline/core'
18
+
19
+ export interface CollectionListParams {
20
+ page?: number
21
+ page_size?: number
22
+ order?: string
23
+ desc?: boolean
24
+ query?: string
25
+ locale?: string
26
+ status?: string
27
+ fields?: string[]
28
+ }
29
+
30
+ export interface CollectionListDoc {
31
+ id: string
32
+ path?: string
33
+ [field: string]: unknown
34
+ }
35
+
36
+ export interface CollectionListResponse {
37
+ docs: CollectionListDoc[]
38
+ meta: { totalPages?: number; [k: string]: unknown }
39
+ included: { collection: { id: string; [k: string]: unknown } }
40
+ }
41
+
42
+ export type GetCollectionDocumentsFn = (input: {
43
+ collection: string
44
+ params: CollectionListParams
45
+ }) => Promise<CollectionListResponse>
46
+
47
+ export interface UploadedFileResult {
48
+ documentId?: string
49
+ documentVersionId?: string
50
+ /**
51
+ * The persisted file value, including the `variants` array with
52
+ * `storagePath`, `storageUrl`, `width`, `height`, and `format` for each
53
+ * generated derivative. Single source of truth — the legacy top-level
54
+ * `variants: { name, url }[]` is gone.
55
+ */
56
+ storedFile: StoredFileValue
57
+ }
58
+
59
+ export type UploadFieldFn = (
60
+ collection: string,
61
+ formData: FormData,
62
+ createDocument?: boolean
63
+ ) => Promise<UploadedFileResult>
64
+
65
+ export interface BylineFieldServices {
66
+ getCollectionDocuments: GetCollectionDocumentsFn
67
+ uploadField: UploadFieldFn
68
+ }