@byline/ui 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 (218) hide show
  1. package/dist/components/shimmer/shimmer.d.ts +13 -1
  2. package/dist/components/shimmer/shimmer.js +29 -20
  3. package/dist/components/shimmer/shimmer_module.css +4 -4
  4. package/dist/dnd/draggable-sortable/demo/draggable-list-demo.js +1 -1
  5. package/dist/react.d.ts +18 -54
  6. package/dist/react.js +0 -35
  7. package/dist/styles/styles.css +3 -0
  8. package/dist/uikit.d.ts +1 -0
  9. package/dist/uikit.js +1 -0
  10. package/package.json +2 -8
  11. package/src/components/shimmer/shimmer.module.css +8 -4
  12. package/src/components/shimmer/shimmer.tsx +34 -9
  13. package/src/dnd/draggable-sortable/demo/draggable-list-demo.tsx +1 -1
  14. package/src/react.ts +20 -68
  15. package/src/styles/functional/surfaces.css +13 -1
  16. package/src/uikit.ts +1 -0
  17. package/dist/admin/group.d.ts +0 -27
  18. package/dist/admin/group.js +0 -14
  19. package/dist/admin/group.module.js +0 -6
  20. package/dist/admin/group_module.css +0 -19
  21. package/dist/admin/row.d.ts +0 -25
  22. package/dist/admin/row.js +0 -8
  23. package/dist/admin/row.module.js +0 -5
  24. package/dist/admin/row_module.css +0 -18
  25. package/dist/admin/tabs.d.ts +0 -25
  26. package/dist/admin/tabs.js +0 -35
  27. package/dist/admin/tabs.module.js +0 -10
  28. package/dist/admin/tabs_module.css +0 -68
  29. package/dist/fields/array/array-field.d.ts +0 -14
  30. package/dist/fields/array/array-field.js +0 -176
  31. package/dist/fields/array/array-field.module.js +0 -11
  32. package/dist/fields/array/array-field_module.css +0 -32
  33. package/dist/fields/blocks/blocks-field.d.ts +0 -13
  34. package/dist/fields/blocks/blocks-field.js +0 -244
  35. package/dist/fields/blocks/blocks-field.module.js +0 -26
  36. package/dist/fields/blocks/blocks-field_module.css +0 -107
  37. package/dist/fields/checkbox/checkbox-field.d.ts +0 -16
  38. package/dist/fields/checkbox/checkbox-field.js +0 -28
  39. package/dist/fields/checkbox/checkbox-field.module.js +0 -6
  40. package/dist/fields/checkbox/checkbox-field_module.css +0 -4
  41. package/dist/fields/column-formatter.d.ts +0 -20
  42. package/dist/fields/column-formatter.js +0 -15
  43. package/dist/fields/date-time-formatter.d.ts +0 -16
  44. package/dist/fields/date-time-formatter.js +0 -8
  45. package/dist/fields/datetime/datetime-field.d.ts +0 -16
  46. package/dist/fields/datetime/datetime-field.js +0 -37
  47. package/dist/fields/datetime/datetime-field.module.js +0 -5
  48. package/dist/fields/datetime/datetime-field_module.css +0 -4
  49. package/dist/fields/draggable-context-menu.d.ts +0 -6
  50. package/dist/fields/draggable-context-menu.js +0 -83
  51. package/dist/fields/draggable-context-menu.module.js +0 -15
  52. package/dist/fields/draggable-context-menu_module.css +0 -91
  53. package/dist/fields/field-helpers.d.ts +0 -26
  54. package/dist/fields/field-helpers.js +0 -50
  55. package/dist/fields/field-renderer.d.ts +0 -37
  56. package/dist/fields/field-renderer.js +0 -206
  57. package/dist/fields/field-renderer.module.js +0 -8
  58. package/dist/fields/field-renderer_module.css +0 -11
  59. package/dist/fields/file/file-field.d.ts +0 -19
  60. package/dist/fields/file/file-field.js +0 -226
  61. package/dist/fields/file/file-field.module.js +0 -18
  62. package/dist/fields/file/file-field_module.css +0 -131
  63. package/dist/fields/file/file-upload-field.d.ts +0 -21
  64. package/dist/fields/file/file-upload-field.js +0 -128
  65. package/dist/fields/file/file-upload-field.module.js +0 -15
  66. package/dist/fields/file/file-upload-field_module.css +0 -74
  67. package/dist/fields/group/group-field.d.ts +0 -15
  68. package/dist/fields/group/group-field.js +0 -59
  69. package/dist/fields/group/group-field.module.js +0 -9
  70. package/dist/fields/group/group-field_module.css +0 -27
  71. package/dist/fields/image/image-field.d.ts +0 -19
  72. package/dist/fields/image/image-field.js +0 -242
  73. package/dist/fields/image/image-field.module.js +0 -22
  74. package/dist/fields/image/image-field_module.css +0 -121
  75. package/dist/fields/image/image-upload-field.d.ts +0 -21
  76. package/dist/fields/image/image-upload-field.js +0 -187
  77. package/dist/fields/image/image-upload-field.module.js +0 -19
  78. package/dist/fields/image/image-upload-field_module.css +0 -92
  79. package/dist/fields/local-date-time.d.ts +0 -27
  80. package/dist/fields/local-date-time.js +0 -49
  81. package/dist/fields/locale-badge.d.ts +0 -18
  82. package/dist/fields/locale-badge.js +0 -10
  83. package/dist/fields/locale-badge.module.js +0 -5
  84. package/dist/fields/locale-badge_module.css +0 -27
  85. package/dist/fields/numerical/numerical-field.d.ts +0 -18
  86. package/dist/fields/numerical/numerical-field.js +0 -74
  87. package/dist/fields/relation/relation-display.d.ts +0 -40
  88. package/dist/fields/relation/relation-display.js +0 -58
  89. package/dist/fields/relation/relation-display.module.js +0 -9
  90. package/dist/fields/relation/relation-display_module.css +0 -21
  91. package/dist/fields/relation/relation-field.d.ts +0 -18
  92. package/dist/fields/relation/relation-field.js +0 -146
  93. package/dist/fields/relation/relation-field.module.js +0 -13
  94. package/dist/fields/relation/relation-field_module.css +0 -62
  95. package/dist/fields/relation/relation-picker.d.ts +0 -49
  96. package/dist/fields/relation/relation-picker.js +0 -233
  97. package/dist/fields/relation/relation-picker.module.js +0 -26
  98. package/dist/fields/relation/relation-picker_module.css +0 -124
  99. package/dist/fields/relation/relation-summary.d.ts +0 -31
  100. package/dist/fields/relation/relation-summary.js +0 -50
  101. package/dist/fields/relation/relation-summary.module.js +0 -11
  102. package/dist/fields/relation/relation-summary_module.css +0 -37
  103. package/dist/fields/select/select-field.d.ts +0 -16
  104. package/dist/fields/select/select-field.js +0 -50
  105. package/dist/fields/select/select-field.module.js +0 -5
  106. package/dist/fields/select/select-field_module.css +0 -4
  107. package/dist/fields/sortable-item.d.ts +0 -15
  108. package/dist/fields/sortable-item.js +0 -80
  109. package/dist/fields/sortable-item.module.js +0 -22
  110. package/dist/fields/sortable-item_module.css +0 -124
  111. package/dist/fields/text/text-field.d.ts +0 -20
  112. package/dist/fields/text/text-field.js +0 -104
  113. package/dist/fields/text/text-field.module.js +0 -6
  114. package/dist/fields/text/text-field_module.css +0 -5
  115. package/dist/fields/text-area/text-area-field.d.ts +0 -20
  116. package/dist/fields/text-area/text-area-field.js +0 -105
  117. package/dist/fields/text-area/text-area-field.module.js +0 -6
  118. package/dist/fields/text-area/text-area-field_module.css +0 -5
  119. package/dist/fields/use-field-change-handler.d.ts +0 -23
  120. package/dist/fields/use-field-change-handler.js +0 -52
  121. package/dist/forms/document-actions.d.ts +0 -48
  122. package/dist/forms/document-actions.js +0 -469
  123. package/dist/forms/document-actions.module.js +0 -34
  124. package/dist/forms/document-actions_module.css +0 -118
  125. package/dist/forms/form-context.d.ts +0 -89
  126. package/dist/forms/form-context.js +0 -466
  127. package/dist/forms/form-renderer.d.ts +0 -98
  128. package/dist/forms/form-renderer.js +0 -591
  129. package/dist/forms/form-renderer.module.js +0 -46
  130. package/dist/forms/form-renderer_module.css +0 -245
  131. package/dist/forms/navigation-guard.d.ts +0 -54
  132. package/dist/forms/navigation-guard.js +0 -22
  133. package/dist/forms/path-widget.d.ts +0 -36
  134. package/dist/forms/path-widget.js +0 -107
  135. package/dist/forms/path-widget.module.js +0 -8
  136. package/dist/forms/path-widget_module.css +0 -29
  137. package/dist/forms/upload-executor.d.ts +0 -57
  138. package/dist/forms/upload-executor.js +0 -92
  139. package/dist/services/field-services-context.d.ts +0 -16
  140. package/dist/services/field-services-context.js +0 -13
  141. package/dist/services/field-services-types.d.ts +0 -63
  142. package/dist/services/field-services-types.js +0 -1
  143. package/dist/widgets/diff-viewer/diff-modal.d.ts +0 -22
  144. package/dist/widgets/diff-viewer/diff-modal.js +0 -146
  145. package/dist/widgets/diff-viewer/diff-modal.module.js +0 -14
  146. package/dist/widgets/diff-viewer/diff-modal_module.css +0 -56
  147. package/dist/widgets/status-badge/status-badge.d.ts +0 -25
  148. package/dist/widgets/status-badge/status-badge.js +0 -35
  149. package/dist/widgets/status-badge/status-badge.module.js +0 -7
  150. package/dist/widgets/status-badge/status-badge_module.css +0 -20
  151. package/src/admin/group.module.css +0 -41
  152. package/src/admin/group.tsx +0 -40
  153. package/src/admin/row.module.css +0 -32
  154. package/src/admin/row.tsx +0 -33
  155. package/src/admin/tabs.module.css +0 -107
  156. package/src/admin/tabs.tsx +0 -82
  157. package/src/fields/array/array-field.module.css +0 -48
  158. package/src/fields/array/array-field.tsx +0 -266
  159. package/src/fields/blocks/blocks-field.module.css +0 -148
  160. package/src/fields/blocks/blocks-field.tsx +0 -312
  161. package/src/fields/checkbox/checkbox-field.module.css +0 -4
  162. package/src/fields/checkbox/checkbox-field.tsx +0 -54
  163. package/src/fields/column-formatter.tsx +0 -31
  164. package/src/fields/date-time-formatter.tsx +0 -22
  165. package/src/fields/datetime/datetime-field.module.css +0 -13
  166. package/src/fields/datetime/datetime-field.tsx +0 -54
  167. package/src/fields/draggable-context-menu.module.css +0 -127
  168. package/src/fields/draggable-context-menu.tsx +0 -85
  169. package/src/fields/field-helpers.ts +0 -69
  170. package/src/fields/field-renderer.module.css +0 -22
  171. package/src/fields/field-renderer.tsx +0 -288
  172. package/src/fields/file/file-field.module.css +0 -153
  173. package/src/fields/file/file-field.tsx +0 -271
  174. package/src/fields/file/file-upload-field.module.css +0 -101
  175. package/src/fields/file/file-upload-field.tsx +0 -183
  176. package/src/fields/group/group-field.module.css +0 -43
  177. package/src/fields/group/group-field.tsx +0 -84
  178. package/src/fields/image/image-field.module.css +0 -155
  179. package/src/fields/image/image-field.tsx +0 -291
  180. package/src/fields/image/image-upload-field.module.css +0 -123
  181. package/src/fields/image/image-upload-field.tsx +0 -270
  182. package/src/fields/local-date-time.tsx +0 -88
  183. package/src/fields/locale-badge.module.css +0 -37
  184. package/src/fields/locale-badge.tsx +0 -32
  185. package/src/fields/numerical/numerical-field.tsx +0 -114
  186. package/src/fields/relation/relation-display.module.css +0 -36
  187. package/src/fields/relation/relation-display.tsx +0 -130
  188. package/src/fields/relation/relation-field.module.css +0 -83
  189. package/src/fields/relation/relation-field.tsx +0 -206
  190. package/src/fields/relation/relation-picker.module.css +0 -168
  191. package/src/fields/relation/relation-picker.tsx +0 -325
  192. package/src/fields/relation/relation-summary.module.css +0 -55
  193. package/src/fields/relation/relation-summary.tsx +0 -123
  194. package/src/fields/select/select-field.module.css +0 -13
  195. package/src/fields/select/select-field.tsx +0 -61
  196. package/src/fields/sortable-item.module.css +0 -167
  197. package/src/fields/sortable-item.tsx +0 -101
  198. package/src/fields/text/text-field.module.css +0 -13
  199. package/src/fields/text/text-field.tsx +0 -146
  200. package/src/fields/text-area/text-area-field.module.css +0 -13
  201. package/src/fields/text-area/text-area-field.tsx +0 -147
  202. package/src/fields/use-field-change-handler.ts +0 -112
  203. package/src/forms/document-actions.module.css +0 -160
  204. package/src/forms/document-actions.tsx +0 -487
  205. package/src/forms/form-context.tsx +0 -704
  206. package/src/forms/form-renderer.module.css +0 -321
  207. package/src/forms/form-renderer.tsx +0 -888
  208. package/src/forms/navigation-guard.tsx +0 -98
  209. package/src/forms/path-widget.module.css +0 -41
  210. package/src/forms/path-widget.test.tsx +0 -217
  211. package/src/forms/path-widget.tsx +0 -181
  212. package/src/forms/upload-executor.ts +0 -190
  213. package/src/services/field-services-context.tsx +0 -35
  214. package/src/services/field-services-types.ts +0 -68
  215. package/src/widgets/diff-viewer/diff-modal.module.css +0 -79
  216. package/src/widgets/diff-viewer/diff-modal.tsx +0 -184
  217. package/src/widgets/status-badge/status-badge.module.css +0 -31
  218. package/src/widgets/status-badge/status-badge.tsx +0 -69
@@ -1,130 +0,0 @@
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 { CollectionDefinition, ColumnDefinition } from '@byline/core'
10
- import cx from 'classnames'
11
-
12
- import styles from './relation-display.module.css'
13
-
14
- // ---------------------------------------------------------------------------
15
- // Shared render helpers used by both the relation picker modal rows and
16
- // the relation-summary tile on the edit form. Kept in lock-step so a row
17
- // rendered in the picker looks identical to the selected tile.
18
- // ---------------------------------------------------------------------------
19
-
20
- /**
21
- * Render a single row cell from a `ColumnDefinition`, reading the value
22
- * from the document's `fields` (with a fallback to top-level metadata like
23
- * `status`, `updated_at`, `path`). Honours both formatter shapes — plain
24
- * function → its return value, `{ component }` → the component is rendered.
25
- */
26
- export function PickerCell({
27
- column,
28
- record,
29
- }: {
30
- column: ColumnDefinition
31
- record: Record<string, any>
32
- }) {
33
- const name = String(column.fieldName)
34
- const value = record?.fields?.[name] ?? record?.[name]
35
-
36
- let content: any
37
- if (column.formatter) {
38
- if (typeof column.formatter === 'function') {
39
- content = column.formatter(value, record)
40
- } else {
41
- const Comp = column.formatter.component
42
- content = <Comp value={value} record={record} />
43
- }
44
- } else if (value == null) {
45
- content = null
46
- } else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
47
- content = String(value)
48
- } else {
49
- content = null
50
- }
51
-
52
- return (
53
- <div
54
- className={cx(
55
- 'byline-relation-cell',
56
- styles.cell,
57
- column.align === 'center' && ['byline-relation-cell-center', styles['cell-center']],
58
- column.align === 'right' && ['byline-relation-cell-right', styles['cell-right']],
59
- column.className
60
- )}
61
- >
62
- {content}
63
- </div>
64
- )
65
- }
66
-
67
- /** First top-level `text` field name on a collection, or null. */
68
- export function resolveFallbackDisplayField(
69
- def: CollectionDefinition | null | undefined
70
- ): string | null {
71
- if (!def) return null
72
- const textField = def.fields.find((f) => f.type === 'text')
73
- return textField?.name ?? null
74
- }
75
-
76
- /** Resolve the row's primary label text from the document. */
77
- export function resolveRowLabel(
78
- doc: Record<string, any> | null | undefined,
79
- displayField: string | null
80
- ): string | null {
81
- if (!doc) return null
82
- if (displayField) {
83
- const v = doc.fields?.[displayField]
84
- if (typeof v === 'string' && v.length > 0) return v
85
- }
86
- if (typeof doc.fields?.title === 'string' && doc.fields.title.length > 0) {
87
- return doc.fields.title as string
88
- }
89
- if (typeof doc.path === 'string' && doc.path.length > 0) return doc.path as string
90
- return null
91
- }
92
-
93
- /**
94
- * Build the `fields` projection for the picker listing. Unions:
95
- * - caller-supplied `displayField`
96
- * - target schema's `useAsTitle` (always included, even when not visible)
97
- * - every `fieldName` declared in the admin config's `picker` columns
98
- * - `title` (metadata fallback for rows with no explicit picker columns)
99
- *
100
- * Note that the document `path` is top-level metadata on every list response
101
- * — it is always returned by `getCollectionDocuments` regardless of the
102
- * `fields` projection. Callers that need `{ title, path }` for downstream
103
- * normalisation (e.g. the rich-text link plugin) can rely on:
104
- * - `record.fields[def.useAsTitle]` for the title
105
- * - `record.path` for the path
106
- *
107
- * Returns `undefined` when no target definition is available, leaving the
108
- * listing endpoint to decide its own default projection.
109
- */
110
- export function resolveSelectFields(
111
- def: CollectionDefinition | null | undefined,
112
- displayField: string | undefined,
113
- pickerColumns: ColumnDefinition[] | undefined
114
- ): string[] | undefined {
115
- if (!def) return undefined
116
- const out = new Set<string>()
117
- if (displayField) out.add(displayField)
118
- if (def.useAsTitle) out.add(def.useAsTitle)
119
- const fallback = resolveFallbackDisplayField(def)
120
- if (fallback) out.add(fallback)
121
- if (pickerColumns) {
122
- for (const col of pickerColumns) {
123
- const name = String(col.fieldName)
124
- if (def.fields.some((f) => f.name === name)) out.add(name)
125
- }
126
- }
127
- if (def.fields.some((f) => f.name === 'title')) out.add('title')
128
- if (out.size === 0) return undefined
129
- return Array.from(out)
130
- }
@@ -1,83 +0,0 @@
1
- /**
2
- * RelationField — widget for `type: 'relation'` fields.
3
- *
4
- * Override handles:
5
- * .byline-field-relation — outer wrapper
6
- * .byline-field-relation-header — label row
7
- * .byline-field-relation-help — help text below the label
8
- * .byline-field-relation-error-tile — wrapper shown when target collection is unknown
9
- * .byline-field-relation-error-text — secondary error message below the title
10
- * .byline-field-relation-tile — bordered selected-value + actions container
11
- * .byline-field-relation-actions — top-right icon-button group (edit + remove)
12
- * .byline-field-relation-mono — monospace `<code>` for field/collection names
13
- */
14
-
15
- .header,
16
- :global(.byline-field-relation-header) {
17
- display: flex;
18
- align-items: baseline;
19
- gap: var(--spacing-8);
20
- margin-bottom: 0.25rem;
21
- }
22
-
23
- .help,
24
- :global(.byline-field-relation-help) {
25
- margin-bottom: 0.25rem;
26
- color: var(--gray-400);
27
- font-size: var(--font-size-xs);
28
- }
29
-
30
- .error-tile,
31
- :global(.byline-field-relation-error-tile) {
32
- display: flex;
33
- flex-direction: column;
34
- gap: 0.25rem;
35
- margin-top: 0.25rem;
36
- padding: var(--spacing-8);
37
- border: var(--border-width-thin) var(--border-style-solid) var(--red-700);
38
- background-color: oklch(from var(--red-900) l c h / 0.2);
39
- border-radius: var(--border-radius-md);
40
- color: var(--red-200);
41
- font-size: var(--font-size-xs);
42
- }
43
-
44
- .error-text,
45
- :global(.byline-field-relation-error-text) {
46
- color: oklch(from var(--red-400) l c h / 0.8);
47
- }
48
-
49
- .tile,
50
- :global(.byline-field-relation-tile) {
51
- display: flex;
52
- align-items: flex-start;
53
- justify-content: space-between;
54
- gap: var(--spacing-8);
55
- margin-top: 0.25rem;
56
- padding: var(--spacing-8);
57
- border: var(--border-width-thin) var(--border-style-solid) var(--primary-500);
58
- border-radius: var(--border-radius-md);
59
- color: var(--gray-200);
60
- font-size: var(--font-size-xs);
61
- }
62
-
63
- .actions,
64
- :global(.byline-field-relation-actions) {
65
- display: flex;
66
- align-items: center;
67
- gap: var(--spacing-4);
68
- flex-shrink: 0;
69
- }
70
-
71
- :global(.byline-field-relation-actions .byline-button) {
72
- color: var(--gray-900);
73
- }
74
-
75
- :global(.dark .byline-field-relation-actions .byline-button),
76
- :global([data-theme="dark"] .byline-field-relation-actions .byline-button) {
77
- color: var(--gray-200);
78
- }
79
-
80
- .mono,
81
- :global(.byline-field-relation-mono) {
82
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
83
- }
@@ -1,206 +0,0 @@
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 cx from 'classnames'
19
-
20
- import { useFieldError, useFieldValue } from '../../forms/form-context'
21
- import { CloseIcon } from '../../icons/close-icon.js'
22
- import { EditIcon } from '../../icons/edit-icon.js'
23
- import { Button, ErrorText, IconButton, Label } from '../../uikit.js'
24
- import styles from './relation-field.module.css'
25
- import { RelationPicker } from './relation-picker'
26
- import { RelationSummary } from './relation-summary'
27
-
28
- // The raw form value for a relation field is `RelatedDocumentValue`, but
29
- // when the edit loader runs server-side populate the value arrives as a
30
- // `PopulatedRelationValue` (same base shape, plus `_resolved` / `document`
31
- // discriminator keys). We accept both here and let `RelationSummary`
32
- // narrow internally.
33
- type IncomingRelationValue = RelatedDocumentValue & {
34
- _resolved?: boolean
35
- _cycle?: boolean
36
- document?: Record<string, any>
37
- }
38
-
39
- // ---------------------------------------------------------------------------
40
- // RelationField — widget for `type: 'relation'` fields
41
- // ---------------------------------------------------------------------------
42
-
43
- interface RelationFieldProps {
44
- field: FieldType
45
- value?: RelatedDocumentValue | null
46
- defaultValue?: RelatedDocumentValue | null
47
- onChange?: (value: RelatedDocumentValue | null) => void
48
- id?: string
49
- path?: string
50
- }
51
-
52
- export const RelationField = ({
53
- field,
54
- value,
55
- defaultValue,
56
- onChange,
57
- id,
58
- path,
59
- }: RelationFieldProps) => {
60
- const fieldPath = path ?? field.name
61
- const htmlId = id ?? fieldPath
62
- const fieldError = useFieldError(fieldPath)
63
- const fieldValue = useFieldValue<IncomingRelationValue | null | undefined>(fieldPath)
64
-
65
- const incomingValue: IncomingRelationValue | null =
66
- fieldValue !== undefined
67
- ? ((fieldValue as IncomingRelationValue | null) ?? null)
68
- : ((value as IncomingRelationValue | null) ??
69
- (defaultValue as IncomingRelationValue | null) ??
70
- null)
71
-
72
- // Resolve the target collection definition + admin config. The admin
73
- // config drives the picker-column rendering inside RelationSummary so
74
- // the selected tile matches the picker row exactly. Missing target →
75
- // render an inline error and disable the picker.
76
- const targetDef: CollectionDefinition | null = getCollectionDefinition(field.targetCollection)
77
- const targetAdminConfig: CollectionAdminConfig | null = getCollectionAdminConfig(
78
- field.targetCollection
79
- )
80
-
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
- const monoClass = cx('byline-field-relation-mono', styles.mono)
123
-
124
- return (
125
- <div className={`byline-field-relation ${field.name}`}>
126
- <div className={cx('byline-field-relation-header', styles.header)}>
127
- <Label
128
- id={`${htmlId}-label`}
129
- htmlFor={htmlId}
130
- label={field.label ?? field.name}
131
- required={!field.optional}
132
- />
133
- </div>
134
- {field.helpText && (
135
- <div className={cx('byline-field-relation-help', styles.help)}>{field.helpText}</div>
136
- )}
137
-
138
- {isUnknown ? (
139
- <div className={cx('byline-field-relation-error-tile', styles['error-tile'])}>
140
- <span>
141
- Relation field <code className={monoClass}>{field.name}</code> targets unknown
142
- collection <code className={monoClass}>{field.targetCollection}</code>.
143
- </span>
144
- <span className={cx('byline-field-relation-error-text', styles['error-text'])}>
145
- Register the collection in your Byline config or correct the target path.
146
- </span>
147
- </div>
148
- ) : incomingValue ? (
149
- <div className={cx('byline-field-relation-tile', styles.tile)}>
150
- <RelationSummary
151
- targetDefinition={targetDef}
152
- targetAdminConfig={targetAdminConfig}
153
- displayField={field.displayField}
154
- value={incomingValue}
155
- cachedRecord={cachedRecord}
156
- />
157
- <div className={cx('byline-field-relation-actions', styles.actions)}>
158
- <IconButton
159
- id={htmlId}
160
- type="button"
161
- intent="noeffect"
162
- size="xs"
163
- aria-label={`Change ${targetDef.labels.singular}`}
164
- onClick={() => setPickerOpen(true)}
165
- >
166
- <EditIcon width="15px" height="15px" />
167
- </IconButton>
168
- <IconButton
169
- type="button"
170
- intent="noeffect"
171
- size="xs"
172
- aria-label={`Remove ${targetDef.labels.singular}`}
173
- onClick={handleRemove}
174
- >
175
- <CloseIcon width="15px" height="15px" />
176
- </IconButton>
177
- </div>
178
- </div>
179
- ) : (
180
- <Button
181
- id={htmlId}
182
- size="xs"
183
- variant="outlined"
184
- intent="noeffect"
185
- type="button"
186
- onClick={() => setPickerOpen(true)}
187
- >
188
- Select {targetDef.labels.singular}…
189
- </Button>
190
- )}
191
-
192
- {fieldError && <ErrorText id={`${field.name}-error`} text={fieldError} />}
193
-
194
- {!isUnknown && (
195
- <RelationPicker
196
- targetCollectionPath={field.targetCollection}
197
- targetDefinition={targetDef}
198
- displayField={field.displayField}
199
- isOpen={pickerOpen}
200
- onSelect={handleSelect}
201
- onDismiss={() => setPickerOpen(false)}
202
- />
203
- )}
204
- </div>
205
- )
206
- }
@@ -1,168 +0,0 @@
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
- }