@byline/admin 2.5.2 → 2.6.1

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 +61 -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 +59 -0
  72. package/dist/fields/relation/relation-picker.js +237 -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 +138 -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 +343 -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,211 @@
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 { useState } from 'react'
10
+
11
+ import type {
12
+ CollectionAdminConfig,
13
+ CollectionDefinition,
14
+ RelationField as FieldType,
15
+ RelatedDocumentValue,
16
+ } from '@byline/core'
17
+ import { getCollectionAdminConfig, getCollectionDefinition } from '@byline/core'
18
+ import { useTranslation } from '@byline/i18n/react'
19
+ import { Button, CloseIcon, EditIcon, ErrorText, IconButton, Label } from '@byline/ui/react'
20
+ import cx from 'classnames'
21
+
22
+ import { useFieldError, useFieldValue } from '../../forms/form-context'
23
+ import styles from './relation-field.module.css'
24
+ import { RelationPicker } from './relation-picker'
25
+ import { RelationSummary } from './relation-summary'
26
+
27
+ // The raw form value for a relation field is `RelatedDocumentValue`, but
28
+ // when the edit loader runs server-side populate the value arrives as a
29
+ // `PopulatedRelationValue` (same base shape, plus `_resolved` / `document`
30
+ // discriminator keys). We accept both here and let `RelationSummary`
31
+ // narrow internally.
32
+ type IncomingRelationValue = RelatedDocumentValue & {
33
+ _resolved?: boolean
34
+ _cycle?: boolean
35
+ document?: Record<string, any>
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // RelationField — widget for `type: 'relation'` fields
40
+ // ---------------------------------------------------------------------------
41
+
42
+ interface RelationFieldProps {
43
+ field: FieldType
44
+ value?: RelatedDocumentValue | null
45
+ defaultValue?: RelatedDocumentValue | null
46
+ onChange?: (value: RelatedDocumentValue | null) => void
47
+ id?: string
48
+ path?: string
49
+ }
50
+
51
+ export const RelationField = ({
52
+ field,
53
+ value,
54
+ defaultValue,
55
+ onChange,
56
+ id,
57
+ path,
58
+ }: RelationFieldProps) => {
59
+ const fieldPath = path ?? field.name
60
+ const htmlId = id ?? fieldPath
61
+ const fieldError = useFieldError(fieldPath)
62
+ const fieldValue = useFieldValue<IncomingRelationValue | null | undefined>(fieldPath)
63
+
64
+ const incomingValue: IncomingRelationValue | null =
65
+ fieldValue !== undefined
66
+ ? ((fieldValue as IncomingRelationValue | null) ?? null)
67
+ : ((value as IncomingRelationValue | null) ??
68
+ (defaultValue as IncomingRelationValue | null) ??
69
+ null)
70
+
71
+ // Resolve the target collection definition + admin config. The admin
72
+ // config drives the picker-column rendering inside RelationSummary so
73
+ // the selected tile matches the picker row exactly. Missing target →
74
+ // render an inline error and disable the picker.
75
+ const targetDef: CollectionDefinition | null = getCollectionDefinition(field.targetCollection)
76
+ const targetAdminConfig: CollectionAdminConfig | null = getCollectionAdminConfig(
77
+ field.targetCollection
78
+ )
79
+
80
+ const { t } = useTranslation('byline-admin')
81
+ const [pickerOpen, setPickerOpen] = useState(false)
82
+ // Cached target document from the most recent picker selection. Lets the
83
+ // tile render real display data (name, thumbnail) immediately after a
84
+ // pick without a round trip. Cleared via the `targetDocumentId`
85
+ // comparison in the render path.
86
+ const [pickedRecord, setPickedRecord] = useState<{
87
+ id: string
88
+ record: Record<string, any>
89
+ } | null>(null)
90
+
91
+ const handleSelect = (selection: {
92
+ targetDocumentId: string
93
+ targetCollectionId: string
94
+ record?: Record<string, any>
95
+ }) => {
96
+ setPickerOpen(false)
97
+ if (selection.record) {
98
+ setPickedRecord({ id: selection.targetDocumentId, record: selection.record })
99
+ } else {
100
+ setPickedRecord(null)
101
+ }
102
+ onChange?.({
103
+ targetDocumentId: selection.targetDocumentId,
104
+ targetCollectionId: selection.targetCollectionId,
105
+ })
106
+ }
107
+
108
+ const handleRemove = () => {
109
+ setPickedRecord(null)
110
+ onChange?.(null)
111
+ }
112
+
113
+ // Only carry the cached picker record through to the summary when it
114
+ // still matches the current value — guards against a stale cache after
115
+ // an external value change (e.g. patch rollback).
116
+ const cachedRecord =
117
+ pickedRecord && incomingValue && pickedRecord.id === incomingValue.targetDocumentId
118
+ ? pickedRecord.record
119
+ : null
120
+
121
+ const isUnknown = targetDef == null
122
+
123
+ return (
124
+ <div className={`byline-field-relation ${field.name}`}>
125
+ <div className={cx('byline-field-relation-header', styles.header)}>
126
+ <Label
127
+ id={`${htmlId}-label`}
128
+ htmlFor={htmlId}
129
+ label={field.label ?? field.name}
130
+ required={!field.optional}
131
+ />
132
+ </div>
133
+ {field.helpText && (
134
+ <div className={cx('byline-field-relation-help', styles.help)}>{field.helpText}</div>
135
+ )}
136
+
137
+ {isUnknown ? (
138
+ <div className={cx('byline-field-relation-error-tile', styles['error-tile'])}>
139
+ <span>
140
+ {t('fields.relation.unknownError', {
141
+ name: field.name,
142
+ target: field.targetCollection,
143
+ })}
144
+ </span>
145
+ <span className={cx('byline-field-relation-error-text', styles['error-text'])}>
146
+ {t('fields.relation.unknownHint')}
147
+ </span>
148
+ </div>
149
+ ) : incomingValue ? (
150
+ <div className={cx('byline-field-relation-tile', styles.tile)}>
151
+ <RelationSummary
152
+ targetDefinition={targetDef}
153
+ targetAdminConfig={targetAdminConfig}
154
+ displayField={field.displayField}
155
+ value={incomingValue}
156
+ cachedRecord={cachedRecord}
157
+ />
158
+ <div className={cx('byline-field-relation-actions', styles.actions)}>
159
+ <IconButton
160
+ id={htmlId}
161
+ type="button"
162
+ intent="noeffect"
163
+ size="xs"
164
+ aria-label={t('fields.relation.changeAriaLabel', {
165
+ label: targetDef.labels.singular,
166
+ })}
167
+ onClick={() => setPickerOpen(true)}
168
+ >
169
+ <EditIcon width="15px" height="15px" />
170
+ </IconButton>
171
+ <IconButton
172
+ type="button"
173
+ intent="noeffect"
174
+ size="xs"
175
+ aria-label={t('fields.relation.removeAriaLabel', {
176
+ label: targetDef.labels.singular,
177
+ })}
178
+ onClick={handleRemove}
179
+ >
180
+ <CloseIcon width="15px" height="15px" />
181
+ </IconButton>
182
+ </div>
183
+ </div>
184
+ ) : (
185
+ <Button
186
+ id={htmlId}
187
+ size="xs"
188
+ variant="outlined"
189
+ intent="noeffect"
190
+ type="button"
191
+ onClick={() => setPickerOpen(true)}
192
+ >
193
+ {t('fields.relation.selectButton', { label: targetDef.labels.singular })}
194
+ </Button>
195
+ )}
196
+
197
+ {fieldError && <ErrorText id={`${field.name}-error`} text={fieldError} />}
198
+
199
+ {!isUnknown && (
200
+ <RelationPicker
201
+ targetCollectionPath={field.targetCollection}
202
+ targetDefinition={targetDef}
203
+ displayField={field.displayField}
204
+ isOpen={pickerOpen}
205
+ onSelect={handleSelect}
206
+ onDismiss={() => setPickerOpen(false)}
207
+ />
208
+ )}
209
+ </div>
210
+ )
211
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * RelationPicker — modal listing for selecting a target document.
3
+ *
4
+ * Override handles:
5
+ * .byline-field-relation-picker — picker chrome wrapper
6
+ * .byline-field-relation-picker-header — modal header (title)
7
+ * .byline-field-relation-picker-title — title text
8
+ * .byline-field-relation-picker-body — vertical stack of search + list + pager
9
+ * .byline-field-relation-picker-list — scrollable list container
10
+ * .byline-field-relation-picker-loading — centered spinner row
11
+ * .byline-field-relation-picker-error — error message row
12
+ * .byline-field-relation-picker-empty — "no results" message row
13
+ * .byline-field-relation-picker-rows — <ul> of rows
14
+ * .byline-field-relation-picker-row-button — clickable row button
15
+ * .byline-field-relation-picker-row-selected — added when row is selected
16
+ * .byline-field-relation-picker-row-cells — picker-columns layout inside a row
17
+ * .byline-field-relation-picker-row-stack — fallback label/path stack
18
+ * .byline-field-relation-picker-row-label — fallback row label text
19
+ * .byline-field-relation-picker-row-path — fallback row secondary path
20
+ * .byline-field-relation-picker-pager — pager row at the bottom
21
+ * .byline-field-relation-picker-action — Cancel / Select buttons
22
+ */
23
+
24
+ .header,
25
+ :global(.byline-field-relation-picker-header) {
26
+ padding-top: 1rem;
27
+ margin-bottom: var(--spacing-8);
28
+ }
29
+
30
+ .title,
31
+ :global(.byline-field-relation-picker-title) {
32
+ margin: 0 0 var(--spacing-8) 0;
33
+ font-size: var(--font-size-xl);
34
+ }
35
+
36
+ .body,
37
+ :global(.byline-field-relation-picker-body) {
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: 0.75rem;
41
+ }
42
+
43
+ .list,
44
+ :global(.byline-field-relation-picker-list) {
45
+ min-height: 320px;
46
+ max-height: 420px;
47
+ overflow-y: auto;
48
+ border: var(--border-width-thin) var(--border-style-solid) var(--gray-700);
49
+ border-radius: var(--border-radius-md);
50
+ }
51
+
52
+ .loading,
53
+ :global(.byline-field-relation-picker-loading) {
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ padding: 2.5rem 0;
58
+ }
59
+
60
+ .error,
61
+ :global(.byline-field-relation-picker-error) {
62
+ padding: 2.5rem 1rem;
63
+ color: var(--red-500);
64
+ font-size: var(--font-size-sm);
65
+ text-align: center;
66
+ }
67
+
68
+ .empty,
69
+ :global(.byline-field-relation-picker-empty) {
70
+ padding: 2.5rem 1rem;
71
+ color: var(--gray-400);
72
+ font-size: var(--font-size-sm);
73
+ text-align: center;
74
+ }
75
+
76
+ .rows,
77
+ :global(.byline-field-relation-picker-rows) {
78
+ margin: 0;
79
+ padding: 0;
80
+ list-style: none;
81
+ }
82
+
83
+ .rows > li + li,
84
+ :global(.byline-field-relation-picker-rows) > li + li {
85
+ border-top: var(--border-width-thin) var(--border-style-solid) var(--gray-700);
86
+ }
87
+
88
+ .row-button,
89
+ :global(.byline-field-relation-picker-row-button) {
90
+ width: 100%;
91
+ padding: var(--spacing-8) 0.75rem;
92
+ border: none;
93
+ background: none;
94
+ text-align: left;
95
+ color: inherit;
96
+ cursor: pointer;
97
+ transition: background-color 150ms ease;
98
+ }
99
+
100
+ .row-button:hover,
101
+ :global(.byline-field-relation-picker-row-button):hover {
102
+ background-color: var(--gray-25);
103
+ }
104
+
105
+ .row-selected,
106
+ :global(.byline-field-relation-picker-row-selected) {
107
+ background-color: oklch(from var(--primary-200) l c h / 0.3);
108
+ border-left: 2px solid var(--primary-200);
109
+ }
110
+
111
+ .row-cells,
112
+ :global(.byline-field-relation-picker-row-cells) {
113
+ display: flex;
114
+ align-items: center;
115
+ gap: 0.75rem;
116
+ }
117
+
118
+ .row-stack,
119
+ :global(.byline-field-relation-picker-row-stack) {
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 0.125rem;
123
+ }
124
+
125
+ .row-label,
126
+ :global(.byline-field-relation-picker-row-label) {
127
+ color: var(--gray-100);
128
+ font-size: var(--font-size-sm);
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ white-space: nowrap;
132
+ }
133
+
134
+ .row-path,
135
+ :global(.byline-field-relation-picker-row-path) {
136
+ color: var(--gray-500);
137
+ font-size: var(--font-size-xs);
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ white-space: nowrap;
141
+ }
142
+
143
+ .pager,
144
+ :global(.byline-field-relation-picker-pager) {
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: space-between;
148
+ color: var(--gray-400);
149
+ font-size: var(--font-size-xs);
150
+ }
151
+
152
+ .action,
153
+ :global(.byline-field-relation-picker-action) {
154
+ min-width: 70px;
155
+ }
156
+
157
+ :is([data-theme="dark"], :global(.dark)) {
158
+ .row-button:hover,
159
+ :global(.byline-field-relation-picker-row-button):hover {
160
+ background-color: var(--gray-900);
161
+ }
162
+
163
+ .row-selected,
164
+ :global(.byline-field-relation-picker-row-selected) {
165
+ background-color: oklch(from var(--primary-900) l c h / 0.3);
166
+ border-left: 2px solid var(--primary-400);
167
+ }
168
+ }