@byline/ui 2.5.2 → 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.
- package/dist/dnd/draggable-sortable/demo/draggable-list-demo.js +1 -1
- package/dist/react.d.ts +18 -54
- package/dist/react.js +0 -35
- package/dist/uikit.d.ts +1 -0
- package/dist/uikit.js +1 -0
- package/package.json +2 -8
- package/src/dnd/draggable-sortable/demo/draggable-list-demo.tsx +1 -1
- package/src/react.ts +20 -68
- package/src/uikit.ts +1 -0
- package/dist/admin/group.d.ts +0 -27
- package/dist/admin/group.js +0 -14
- package/dist/admin/group.module.js +0 -6
- package/dist/admin/group_module.css +0 -19
- package/dist/admin/row.d.ts +0 -25
- package/dist/admin/row.js +0 -8
- package/dist/admin/row.module.js +0 -5
- package/dist/admin/row_module.css +0 -18
- package/dist/admin/tabs.d.ts +0 -25
- package/dist/admin/tabs.js +0 -35
- package/dist/admin/tabs.module.js +0 -10
- package/dist/admin/tabs_module.css +0 -68
- package/dist/fields/array/array-field.d.ts +0 -14
- package/dist/fields/array/array-field.js +0 -176
- package/dist/fields/array/array-field.module.js +0 -11
- package/dist/fields/array/array-field_module.css +0 -32
- package/dist/fields/blocks/blocks-field.d.ts +0 -13
- package/dist/fields/blocks/blocks-field.js +0 -244
- package/dist/fields/blocks/blocks-field.module.js +0 -26
- package/dist/fields/blocks/blocks-field_module.css +0 -107
- package/dist/fields/checkbox/checkbox-field.d.ts +0 -16
- package/dist/fields/checkbox/checkbox-field.js +0 -28
- package/dist/fields/checkbox/checkbox-field.module.js +0 -6
- package/dist/fields/checkbox/checkbox-field_module.css +0 -4
- package/dist/fields/column-formatter.d.ts +0 -20
- package/dist/fields/column-formatter.js +0 -15
- package/dist/fields/date-time-formatter.d.ts +0 -16
- package/dist/fields/date-time-formatter.js +0 -8
- package/dist/fields/datetime/datetime-field.d.ts +0 -16
- package/dist/fields/datetime/datetime-field.js +0 -37
- package/dist/fields/datetime/datetime-field.module.js +0 -5
- package/dist/fields/datetime/datetime-field_module.css +0 -4
- package/dist/fields/draggable-context-menu.d.ts +0 -6
- package/dist/fields/draggable-context-menu.js +0 -83
- package/dist/fields/draggable-context-menu.module.js +0 -15
- package/dist/fields/draggable-context-menu_module.css +0 -91
- package/dist/fields/field-helpers.d.ts +0 -26
- package/dist/fields/field-helpers.js +0 -50
- package/dist/fields/field-renderer.d.ts +0 -37
- package/dist/fields/field-renderer.js +0 -206
- package/dist/fields/field-renderer.module.js +0 -8
- package/dist/fields/field-renderer_module.css +0 -11
- package/dist/fields/file/file-field.d.ts +0 -19
- package/dist/fields/file/file-field.js +0 -226
- package/dist/fields/file/file-field.module.js +0 -18
- package/dist/fields/file/file-field_module.css +0 -131
- package/dist/fields/file/file-upload-field.d.ts +0 -21
- package/dist/fields/file/file-upload-field.js +0 -128
- package/dist/fields/file/file-upload-field.module.js +0 -15
- package/dist/fields/file/file-upload-field_module.css +0 -74
- package/dist/fields/group/group-field.d.ts +0 -15
- package/dist/fields/group/group-field.js +0 -59
- package/dist/fields/group/group-field.module.js +0 -9
- package/dist/fields/group/group-field_module.css +0 -27
- package/dist/fields/image/image-field.d.ts +0 -19
- package/dist/fields/image/image-field.js +0 -242
- package/dist/fields/image/image-field.module.js +0 -22
- package/dist/fields/image/image-field_module.css +0 -121
- package/dist/fields/image/image-upload-field.d.ts +0 -21
- package/dist/fields/image/image-upload-field.js +0 -187
- package/dist/fields/image/image-upload-field.module.js +0 -19
- package/dist/fields/image/image-upload-field_module.css +0 -92
- package/dist/fields/local-date-time.d.ts +0 -27
- package/dist/fields/local-date-time.js +0 -49
- package/dist/fields/locale-badge.d.ts +0 -18
- package/dist/fields/locale-badge.js +0 -10
- package/dist/fields/locale-badge.module.js +0 -5
- package/dist/fields/locale-badge_module.css +0 -27
- package/dist/fields/numerical/numerical-field.d.ts +0 -18
- package/dist/fields/numerical/numerical-field.js +0 -74
- package/dist/fields/relation/relation-display.d.ts +0 -40
- package/dist/fields/relation/relation-display.js +0 -58
- package/dist/fields/relation/relation-display.module.js +0 -9
- package/dist/fields/relation/relation-display_module.css +0 -21
- package/dist/fields/relation/relation-field.d.ts +0 -18
- package/dist/fields/relation/relation-field.js +0 -146
- package/dist/fields/relation/relation-field.module.js +0 -13
- package/dist/fields/relation/relation-field_module.css +0 -62
- package/dist/fields/relation/relation-picker.d.ts +0 -49
- package/dist/fields/relation/relation-picker.js +0 -233
- package/dist/fields/relation/relation-picker.module.js +0 -26
- package/dist/fields/relation/relation-picker_module.css +0 -124
- package/dist/fields/relation/relation-summary.d.ts +0 -31
- package/dist/fields/relation/relation-summary.js +0 -50
- package/dist/fields/relation/relation-summary.module.js +0 -11
- package/dist/fields/relation/relation-summary_module.css +0 -37
- package/dist/fields/select/select-field.d.ts +0 -16
- package/dist/fields/select/select-field.js +0 -50
- package/dist/fields/select/select-field.module.js +0 -5
- package/dist/fields/select/select-field_module.css +0 -4
- package/dist/fields/sortable-item.d.ts +0 -15
- package/dist/fields/sortable-item.js +0 -80
- package/dist/fields/sortable-item.module.js +0 -22
- package/dist/fields/sortable-item_module.css +0 -124
- package/dist/fields/text/text-field.d.ts +0 -20
- package/dist/fields/text/text-field.js +0 -104
- package/dist/fields/text/text-field.module.js +0 -6
- package/dist/fields/text/text-field_module.css +0 -5
- package/dist/fields/text-area/text-area-field.d.ts +0 -20
- package/dist/fields/text-area/text-area-field.js +0 -105
- package/dist/fields/text-area/text-area-field.module.js +0 -6
- package/dist/fields/text-area/text-area-field_module.css +0 -5
- package/dist/fields/use-field-change-handler.d.ts +0 -23
- package/dist/fields/use-field-change-handler.js +0 -52
- package/dist/forms/document-actions.d.ts +0 -48
- package/dist/forms/document-actions.js +0 -469
- package/dist/forms/document-actions.module.js +0 -34
- package/dist/forms/document-actions_module.css +0 -118
- package/dist/forms/form-context.d.ts +0 -89
- package/dist/forms/form-context.js +0 -466
- package/dist/forms/form-renderer.d.ts +0 -98
- package/dist/forms/form-renderer.js +0 -591
- package/dist/forms/form-renderer.module.js +0 -46
- package/dist/forms/form-renderer_module.css +0 -245
- package/dist/forms/navigation-guard.d.ts +0 -54
- package/dist/forms/navigation-guard.js +0 -22
- package/dist/forms/path-widget.d.ts +0 -36
- package/dist/forms/path-widget.js +0 -107
- package/dist/forms/path-widget.module.js +0 -8
- package/dist/forms/path-widget_module.css +0 -29
- package/dist/forms/upload-executor.d.ts +0 -57
- package/dist/forms/upload-executor.js +0 -92
- package/dist/services/field-services-context.d.ts +0 -16
- package/dist/services/field-services-context.js +0 -13
- package/dist/services/field-services-types.d.ts +0 -63
- package/dist/services/field-services-types.js +0 -1
- package/dist/widgets/diff-viewer/diff-modal.d.ts +0 -22
- package/dist/widgets/diff-viewer/diff-modal.js +0 -146
- package/dist/widgets/diff-viewer/diff-modal.module.js +0 -14
- package/dist/widgets/diff-viewer/diff-modal_module.css +0 -56
- package/dist/widgets/status-badge/status-badge.d.ts +0 -25
- package/dist/widgets/status-badge/status-badge.js +0 -35
- package/dist/widgets/status-badge/status-badge.module.js +0 -7
- package/dist/widgets/status-badge/status-badge_module.css +0 -20
- package/src/admin/group.module.css +0 -41
- package/src/admin/group.tsx +0 -40
- package/src/admin/row.module.css +0 -32
- package/src/admin/row.tsx +0 -33
- package/src/admin/tabs.module.css +0 -107
- package/src/admin/tabs.tsx +0 -82
- package/src/fields/array/array-field.module.css +0 -48
- package/src/fields/array/array-field.tsx +0 -266
- package/src/fields/blocks/blocks-field.module.css +0 -148
- package/src/fields/blocks/blocks-field.tsx +0 -312
- package/src/fields/checkbox/checkbox-field.module.css +0 -4
- package/src/fields/checkbox/checkbox-field.tsx +0 -54
- package/src/fields/column-formatter.tsx +0 -31
- package/src/fields/date-time-formatter.tsx +0 -22
- package/src/fields/datetime/datetime-field.module.css +0 -13
- package/src/fields/datetime/datetime-field.tsx +0 -54
- package/src/fields/draggable-context-menu.module.css +0 -127
- package/src/fields/draggable-context-menu.tsx +0 -85
- package/src/fields/field-helpers.ts +0 -69
- package/src/fields/field-renderer.module.css +0 -22
- package/src/fields/field-renderer.tsx +0 -288
- package/src/fields/file/file-field.module.css +0 -153
- package/src/fields/file/file-field.tsx +0 -271
- package/src/fields/file/file-upload-field.module.css +0 -101
- package/src/fields/file/file-upload-field.tsx +0 -183
- package/src/fields/group/group-field.module.css +0 -43
- package/src/fields/group/group-field.tsx +0 -84
- package/src/fields/image/image-field.module.css +0 -155
- package/src/fields/image/image-field.tsx +0 -291
- package/src/fields/image/image-upload-field.module.css +0 -123
- package/src/fields/image/image-upload-field.tsx +0 -270
- package/src/fields/local-date-time.tsx +0 -88
- package/src/fields/locale-badge.module.css +0 -37
- package/src/fields/locale-badge.tsx +0 -32
- package/src/fields/numerical/numerical-field.tsx +0 -114
- package/src/fields/relation/relation-display.module.css +0 -36
- package/src/fields/relation/relation-display.tsx +0 -130
- package/src/fields/relation/relation-field.module.css +0 -83
- package/src/fields/relation/relation-field.tsx +0 -206
- package/src/fields/relation/relation-picker.module.css +0 -168
- package/src/fields/relation/relation-picker.tsx +0 -325
- package/src/fields/relation/relation-summary.module.css +0 -55
- package/src/fields/relation/relation-summary.tsx +0 -123
- package/src/fields/select/select-field.module.css +0 -13
- package/src/fields/select/select-field.tsx +0 -61
- package/src/fields/sortable-item.module.css +0 -167
- package/src/fields/sortable-item.tsx +0 -101
- package/src/fields/text/text-field.module.css +0 -13
- package/src/fields/text/text-field.tsx +0 -146
- package/src/fields/text-area/text-area-field.module.css +0 -13
- package/src/fields/text-area/text-area-field.tsx +0 -147
- package/src/fields/use-field-change-handler.ts +0 -112
- package/src/forms/document-actions.module.css +0 -160
- package/src/forms/document-actions.tsx +0 -487
- package/src/forms/form-context.tsx +0 -704
- package/src/forms/form-renderer.module.css +0 -321
- package/src/forms/form-renderer.tsx +0 -888
- package/src/forms/navigation-guard.tsx +0 -98
- package/src/forms/path-widget.module.css +0 -41
- package/src/forms/path-widget.test.tsx +0 -217
- package/src/forms/path-widget.tsx +0 -181
- package/src/forms/upload-executor.ts +0 -190
- package/src/services/field-services-context.tsx +0 -35
- package/src/services/field-services-types.ts +0 -68
- package/src/widgets/diff-viewer/diff-modal.module.css +0 -79
- package/src/widgets/diff-viewer/diff-modal.tsx +0 -184
- package/src/widgets/status-badge/status-badge.module.css +0 -31
- package/src/widgets/status-badge/status-badge.tsx +0 -69
|
@@ -1,312 +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 { useEffect, useMemo, useState } from 'react'
|
|
10
|
-
|
|
11
|
-
import type {
|
|
12
|
-
BlocksField as BlocksFieldType,
|
|
13
|
-
Field,
|
|
14
|
-
GroupField as GroupFieldType,
|
|
15
|
-
} from '@byline/core'
|
|
16
|
-
import cx from 'classnames'
|
|
17
|
-
|
|
18
|
-
import { DraggableSortable, moveItem } from '../../dnd/draggable-sortable'
|
|
19
|
-
import { defaultScalarForField } from '../../fields/field-helpers'
|
|
20
|
-
import { GroupField } from '../../fields/group/group-field'
|
|
21
|
-
import { SortableItem } from '../../fields/sortable-item'
|
|
22
|
-
import { useFormContext } from '../../forms/form-context'
|
|
23
|
-
import { Card, CloseIcon, IconButton, Modal, PlusIcon } from '../../uikit.js'
|
|
24
|
-
import styles from './blocks-field.module.css'
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// BlocksField — renders `type: 'blocks'` fields. Children are heterogeneous
|
|
28
|
-
// group fields selected via a modal picker. Supports D&D.
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
export const BlocksField = ({
|
|
32
|
-
field,
|
|
33
|
-
defaultValue,
|
|
34
|
-
path,
|
|
35
|
-
}: {
|
|
36
|
-
field: BlocksFieldType
|
|
37
|
-
defaultValue: any
|
|
38
|
-
path: string
|
|
39
|
-
}) => {
|
|
40
|
-
const { appendPatch, getFieldValue, getFieldValues, setFieldStore } = useFormContext()
|
|
41
|
-
const [items, setItems] = useState<{ id: string; data: any }[]>([])
|
|
42
|
-
const [showAddBlockModal, setShowAddBlockModal] = useState(false)
|
|
43
|
-
const [pendingInsertIndex, setPendingInsertIndex] = useState<number | null>(null)
|
|
44
|
-
|
|
45
|
-
const availableBlocks = useMemo(() => field.blocks ?? [], [field.blocks])
|
|
46
|
-
const [selectedBlockName, setSelectedBlockName] = useState<string>(
|
|
47
|
-
() => availableBlocks[0]?.blockType
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (
|
|
52
|
-
selectedBlockName == null ||
|
|
53
|
-
!availableBlocks.some((b) => b.blockType === selectedBlockName)
|
|
54
|
-
) {
|
|
55
|
-
setSelectedBlockName(availableBlocks[0]?.blockType)
|
|
56
|
-
}
|
|
57
|
-
}, [availableBlocks, selectedBlockName])
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
if (Array.isArray(defaultValue)) {
|
|
61
|
-
setItems(
|
|
62
|
-
defaultValue.map((item: any) => ({
|
|
63
|
-
id:
|
|
64
|
-
item && typeof item === 'object' && '_id' in item
|
|
65
|
-
? String((item as { _id: string })._id)
|
|
66
|
-
: crypto.randomUUID(),
|
|
67
|
-
data: item,
|
|
68
|
-
}))
|
|
69
|
-
)
|
|
70
|
-
} else {
|
|
71
|
-
setItems([])
|
|
72
|
-
}
|
|
73
|
-
}, [defaultValue])
|
|
74
|
-
|
|
75
|
-
const handleDragEnd = ({
|
|
76
|
-
moveFromIndex,
|
|
77
|
-
moveToIndex,
|
|
78
|
-
}: {
|
|
79
|
-
moveFromIndex: number
|
|
80
|
-
moveToIndex: number
|
|
81
|
-
}) => {
|
|
82
|
-
setItems((prev) => moveItem(prev, moveFromIndex, moveToIndex))
|
|
83
|
-
const currentArray = (getFieldValue(path) ?? defaultValue) as any[]
|
|
84
|
-
|
|
85
|
-
if (Array.isArray(currentArray)) {
|
|
86
|
-
const clampedFrom = Math.max(0, Math.min(moveFromIndex, currentArray.length - 1))
|
|
87
|
-
const clampedTo = Math.max(0, Math.min(moveToIndex, currentArray.length - 1))
|
|
88
|
-
if (clampedFrom === clampedTo) return
|
|
89
|
-
|
|
90
|
-
const item = currentArray[clampedFrom]
|
|
91
|
-
const itemId =
|
|
92
|
-
item && typeof item === 'object' && '_id' in item
|
|
93
|
-
? String((item as { _id: string })._id)
|
|
94
|
-
: String(clampedFrom)
|
|
95
|
-
|
|
96
|
-
appendPatch({
|
|
97
|
-
kind: 'array.move',
|
|
98
|
-
path: path,
|
|
99
|
-
itemId,
|
|
100
|
-
toIndex: clampedTo,
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const handleAddItem = async (forcedVariantName?: string, atIndex?: number) => {
|
|
106
|
-
setShowAddBlockModal(false)
|
|
107
|
-
setPendingInsertIndex(null)
|
|
108
|
-
|
|
109
|
-
const variant =
|
|
110
|
-
(forcedVariantName != null
|
|
111
|
-
? availableBlocks.find((v) => v.blockType === forcedVariantName)
|
|
112
|
-
: undefined) ?? availableBlocks[0]
|
|
113
|
-
|
|
114
|
-
if (!variant) return
|
|
115
|
-
|
|
116
|
-
const compositeFields = (variant.fields ?? []) as Field[]
|
|
117
|
-
|
|
118
|
-
const newId = crypto.randomUUID()
|
|
119
|
-
const newItem: Record<string, any> = {
|
|
120
|
-
_id: newId,
|
|
121
|
-
_type: variant.blockType,
|
|
122
|
-
}
|
|
123
|
-
for (const f of compositeFields) {
|
|
124
|
-
newItem[f.name] = await defaultScalarForField(f, getFieldValues)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const currentArray = (getFieldValue(path) ?? defaultValue) as any[]
|
|
128
|
-
const insertAt = atIndex != null ? atIndex : currentArray ? currentArray.length : 0
|
|
129
|
-
|
|
130
|
-
const newItemWrapper = { id: newId, data: newItem }
|
|
131
|
-
setItems((prev) => {
|
|
132
|
-
const next = [...prev]
|
|
133
|
-
next.splice(insertAt, 0, newItemWrapper)
|
|
134
|
-
return next
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
appendPatch({
|
|
138
|
-
kind: 'array.insert',
|
|
139
|
-
path: path,
|
|
140
|
-
index: insertAt,
|
|
141
|
-
item: newItem,
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
const newArrayValue = currentArray ? [...currentArray] : []
|
|
145
|
-
newArrayValue.splice(insertAt, 0, newItem)
|
|
146
|
-
setFieldStore(path, newArrayValue)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const handleRemoveItem = (index: number) => {
|
|
150
|
-
const currentArray = (getFieldValue(path) ?? defaultValue) as any[]
|
|
151
|
-
if (!Array.isArray(currentArray) || index < 0 || index >= currentArray.length) return
|
|
152
|
-
|
|
153
|
-
const item = currentArray[index]
|
|
154
|
-
const itemId =
|
|
155
|
-
item && typeof item === 'object' && '_id' in item
|
|
156
|
-
? String((item as { _id: string })._id)
|
|
157
|
-
: String(index)
|
|
158
|
-
|
|
159
|
-
setItems((prev) => prev.filter((_, i) => i !== index))
|
|
160
|
-
|
|
161
|
-
appendPatch({
|
|
162
|
-
kind: 'array.remove',
|
|
163
|
-
path: path,
|
|
164
|
-
itemId,
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
const newArrayValue = [...currentArray]
|
|
168
|
-
newArrayValue.splice(index, 1)
|
|
169
|
-
setFieldStore(path, newArrayValue)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const handleInsertBelow = (index: number, forcedVariantName?: string) => {
|
|
173
|
-
if (availableBlocks.length > 1 && forcedVariantName == null) {
|
|
174
|
-
setPendingInsertIndex(index + 1)
|
|
175
|
-
setShowAddBlockModal(true)
|
|
176
|
-
} else {
|
|
177
|
-
void handleAddItem(forcedVariantName, index + 1)
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const renderItem = (itemWrapper: { id: string; data: any }, index: number) => {
|
|
182
|
-
const item = itemWrapper.data
|
|
183
|
-
const arrayElementPath = `${path}[${index}]`
|
|
184
|
-
|
|
185
|
-
if (!item || typeof item !== 'object' || typeof item._type !== 'string') return null
|
|
186
|
-
|
|
187
|
-
const subField = field.blocks?.find((b) => b.blockType === item._type)
|
|
188
|
-
if (subField == null) return null
|
|
189
|
-
|
|
190
|
-
// Extract field data (everything except _id and _type)
|
|
191
|
-
const { _id, _type, ...fieldData } = item
|
|
192
|
-
const label = subField.label ?? _type
|
|
193
|
-
|
|
194
|
-
// Render the block's children directly with arrayElementPath as the
|
|
195
|
-
// path (not basePath). FieldRenderer would append the group name
|
|
196
|
-
// (e.g. "richTextBlock") producing paths like
|
|
197
|
-
// "content[0].richTextBlock.constrainedWidth", but the flat block
|
|
198
|
-
// shape stores fields directly on the item so the correct path is
|
|
199
|
-
// "content[0].constrainedWidth".
|
|
200
|
-
const body = (
|
|
201
|
-
<GroupField
|
|
202
|
-
key={subField.blockType}
|
|
203
|
-
field={
|
|
204
|
-
{
|
|
205
|
-
type: 'group',
|
|
206
|
-
name: subField.blockType,
|
|
207
|
-
fields: subField.fields,
|
|
208
|
-
label: undefined,
|
|
209
|
-
} as GroupFieldType
|
|
210
|
-
}
|
|
211
|
-
defaultValue={fieldData}
|
|
212
|
-
path={arrayElementPath}
|
|
213
|
-
/>
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<SortableItem
|
|
218
|
-
key={itemWrapper.id}
|
|
219
|
-
id={itemWrapper.id}
|
|
220
|
-
label={label ?? subField.blockType}
|
|
221
|
-
onAddBelow={() => handleInsertBelow(index)}
|
|
222
|
-
onRemove={() => handleRemoveItem(index)}
|
|
223
|
-
>
|
|
224
|
-
{body}
|
|
225
|
-
</SortableItem>
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return (
|
|
230
|
-
<div className={`byline-field-blocks ${field.name}`}>
|
|
231
|
-
{field.label && (
|
|
232
|
-
<h3 className={cx('byline-field-blocks-title', styles.title)}>{field.label}</h3>
|
|
233
|
-
)}
|
|
234
|
-
<DraggableSortable
|
|
235
|
-
ids={items.map((i) => i.id)}
|
|
236
|
-
onDragEnd={handleDragEnd}
|
|
237
|
-
className={cx('byline-field-blocks-stack', styles.stack)}
|
|
238
|
-
>
|
|
239
|
-
{items.map((item, index) => renderItem(item, index))}
|
|
240
|
-
<div className={cx('byline-field-blocks-add-row', styles['add-row'])}>
|
|
241
|
-
<IconButton
|
|
242
|
-
onClick={() => {
|
|
243
|
-
setPendingInsertIndex(null)
|
|
244
|
-
setShowAddBlockModal(true)
|
|
245
|
-
}}
|
|
246
|
-
disabled={!selectedBlockName}
|
|
247
|
-
aria-label="Add block"
|
|
248
|
-
>
|
|
249
|
-
<PlusIcon />
|
|
250
|
-
</IconButton>
|
|
251
|
-
</div>
|
|
252
|
-
</DraggableSortable>
|
|
253
|
-
<Modal
|
|
254
|
-
isOpen={showAddBlockModal}
|
|
255
|
-
closeOnOverlayClick={true}
|
|
256
|
-
onDismiss={() => {
|
|
257
|
-
setShowAddBlockModal(false)
|
|
258
|
-
setPendingInsertIndex(null)
|
|
259
|
-
}}
|
|
260
|
-
>
|
|
261
|
-
<Modal.Container style={{ maxWidth: '600px' }}>
|
|
262
|
-
<Modal.Header className={cx('byline-field-blocks-modal-head', styles['modal-head'])}>
|
|
263
|
-
<h3 className={cx('byline-field-blocks-modal-title', styles['modal-title'])}>Blocks</h3>
|
|
264
|
-
<IconButton
|
|
265
|
-
arial-label="Close"
|
|
266
|
-
size="xs"
|
|
267
|
-
onClick={() => {
|
|
268
|
-
setShowAddBlockModal(false)
|
|
269
|
-
setPendingInsertIndex(null)
|
|
270
|
-
}}
|
|
271
|
-
>
|
|
272
|
-
<CloseIcon width="16px" height="16px" svgClassName="white-icon" />
|
|
273
|
-
</IconButton>
|
|
274
|
-
</Modal.Header>
|
|
275
|
-
<Modal.Content className={cx('byline-field-blocks-card-cursor', styles['modal-content'])}>
|
|
276
|
-
<div className={cx('byline-field-blocks-grid', styles.grid)}>
|
|
277
|
-
{availableBlocks.map((b, index) => (
|
|
278
|
-
<Card
|
|
279
|
-
key={b.blockType}
|
|
280
|
-
hover
|
|
281
|
-
onClick={() => void handleAddItem(b.blockType, pendingInsertIndex ?? undefined)}
|
|
282
|
-
className={cx('byline-field-blocks-card', styles.card)}
|
|
283
|
-
>
|
|
284
|
-
<Card.Header>
|
|
285
|
-
<div className={cx('byline-field-blocks-card-head', styles['card-head'])}>
|
|
286
|
-
<Card.Title
|
|
287
|
-
className={cx('byline-field-blocks-card-title', styles['card-title'])}
|
|
288
|
-
>
|
|
289
|
-
{b.label ?? b.blockType}
|
|
290
|
-
</Card.Title>
|
|
291
|
-
<span className={cx('byline-field-blocks-card-index', styles['card-index'])}>
|
|
292
|
-
{index + 1}
|
|
293
|
-
</span>
|
|
294
|
-
</div>
|
|
295
|
-
<code className={cx('byline-field-blocks-card-code', styles['card-code'])}>
|
|
296
|
-
{b.blockType}
|
|
297
|
-
</code>
|
|
298
|
-
</Card.Header>
|
|
299
|
-
<Card.Content>
|
|
300
|
-
<p className={cx('byline-field-blocks-card-body', styles['card-body'])}>
|
|
301
|
-
{b.helpText ?? b.label ?? b.blockType}
|
|
302
|
-
</p>
|
|
303
|
-
</Card.Content>
|
|
304
|
-
</Card>
|
|
305
|
-
))}
|
|
306
|
-
</div>
|
|
307
|
-
</Modal.Content>
|
|
308
|
-
</Modal.Container>
|
|
309
|
-
</Modal>
|
|
310
|
-
</div>
|
|
311
|
-
)
|
|
312
|
-
}
|
|
@@ -1,54 +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 { CheckboxField as FieldType } from '@byline/core'
|
|
10
|
-
|
|
11
|
-
import { useFieldError, useFieldValue } from '../../forms/form-context'
|
|
12
|
-
import { Checkbox } from '../../uikit.js'
|
|
13
|
-
import styles from './checkbox-field.module.css'
|
|
14
|
-
|
|
15
|
-
export const CheckboxField = ({
|
|
16
|
-
field,
|
|
17
|
-
value,
|
|
18
|
-
defaultValue,
|
|
19
|
-
onChange,
|
|
20
|
-
id,
|
|
21
|
-
path,
|
|
22
|
-
}: {
|
|
23
|
-
field: FieldType
|
|
24
|
-
value?: boolean
|
|
25
|
-
defaultValue?: boolean
|
|
26
|
-
onChange?: (value: boolean) => void
|
|
27
|
-
id?: string
|
|
28
|
-
path?: string
|
|
29
|
-
}) => {
|
|
30
|
-
const fieldPath = path ?? field.name
|
|
31
|
-
const fieldError = useFieldError(fieldPath)
|
|
32
|
-
const fieldValue = useFieldValue<boolean | undefined>(fieldPath)
|
|
33
|
-
const checked = value ?? fieldValue ?? defaultValue ?? false
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div className={`byline-field-checkbox ${field.name} ${styles['field-checkbox']}`}>
|
|
37
|
-
<Checkbox
|
|
38
|
-
id={id ?? fieldPath}
|
|
39
|
-
name={field.name}
|
|
40
|
-
label={field.label}
|
|
41
|
-
checked={checked}
|
|
42
|
-
size="sm"
|
|
43
|
-
helpText={field.helpText}
|
|
44
|
-
// TODO: Handle indeterminate state
|
|
45
|
-
onCheckedChange={(value: boolean | 'indeterminate') => {
|
|
46
|
-
const next = value === 'indeterminate' ? false : Boolean(value)
|
|
47
|
-
onChange?.(next)
|
|
48
|
-
}}
|
|
49
|
-
error={fieldError != null}
|
|
50
|
-
errorText={fieldError}
|
|
51
|
-
/>
|
|
52
|
-
</div>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
@@ -1,31 +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 { ColumnFormatter, FormatterProps } from '@byline/core'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Type guard: returns true when the formatter is a `{ component }` wrapper
|
|
13
|
-
* rather than a plain function.
|
|
14
|
-
*/
|
|
15
|
-
export function isComponentFormatter<T>(
|
|
16
|
-
fmt: ColumnFormatter<T>
|
|
17
|
-
): fmt is { component: (props: FormatterProps<T>) => any } {
|
|
18
|
-
return typeof fmt === 'object' && fmt !== null && 'component' in fmt
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Render a cell value through its column formatter (if any).
|
|
23
|
-
* Handles both plain-function and `{ component }` formatters.
|
|
24
|
-
*/
|
|
25
|
-
export function renderFormatted(value: any, document: any, formatter: ColumnFormatter) {
|
|
26
|
-
if (isComponentFormatter(formatter)) {
|
|
27
|
-
const Component = formatter.component
|
|
28
|
-
return <Component value={value} record={document} />
|
|
29
|
-
}
|
|
30
|
-
return formatter(value, document)
|
|
31
|
-
}
|
|
@@ -1,22 +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 { FormatterProps } from '@byline/core'
|
|
10
|
-
|
|
11
|
-
import { LocalDateTime } from './local-date-time'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* SSR-safe date-time column formatter for list views.
|
|
15
|
-
*
|
|
16
|
-
* Uses the `LocalDateTime` component which renders a placeholder on the server
|
|
17
|
-
* and formats to the user's locale/timezone after hydration, avoiding
|
|
18
|
-
* server/client mismatches.
|
|
19
|
-
*/
|
|
20
|
-
export function DateTimeFormatter({ value }: FormatterProps) {
|
|
21
|
-
return <LocalDateTime value={value} />
|
|
22
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DateTimeField — date/time picker form widget.
|
|
3
|
-
*
|
|
4
|
-
* Override handles:
|
|
5
|
-
* .byline-field-datetime — the wrapper div
|
|
6
|
-
* .byline-field-datetime-dirty — added to the inner picker when the
|
|
7
|
-
* field has unsaved local changes
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
.dirty,
|
|
11
|
-
:global(.byline-field-datetime-dirty) {
|
|
12
|
-
border-color: var(--blue-300);
|
|
13
|
-
}
|
|
@@ -1,54 +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 { DateTimeField as FieldType } from '@byline/core'
|
|
10
|
-
import cx from 'classnames'
|
|
11
|
-
|
|
12
|
-
import { useFieldError, useFieldValue, useIsDirty } from '../../forms/form-context'
|
|
13
|
-
import { DatePicker, ErrorText } from '../../uikit.js'
|
|
14
|
-
import styles from './datetime-field.module.css'
|
|
15
|
-
|
|
16
|
-
export const DateTimeField = ({
|
|
17
|
-
field,
|
|
18
|
-
value,
|
|
19
|
-
defaultValue,
|
|
20
|
-
onChange,
|
|
21
|
-
id,
|
|
22
|
-
path,
|
|
23
|
-
}: {
|
|
24
|
-
field: FieldType
|
|
25
|
-
value?: Date | null
|
|
26
|
-
defaultValue?: Date | null
|
|
27
|
-
onChange?: (value: Date | null) => void
|
|
28
|
-
id?: string
|
|
29
|
-
path?: string
|
|
30
|
-
}) => {
|
|
31
|
-
const fieldPath = path ?? field.name
|
|
32
|
-
const fieldError = useFieldError(fieldPath)
|
|
33
|
-
const isDirty = useIsDirty(fieldPath)
|
|
34
|
-
const fieldValue = useFieldValue<Date | null | undefined>(fieldPath)
|
|
35
|
-
const incomingValue = value ?? fieldValue ?? defaultValue ?? null
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<div className={`byline-field-datetime ${field.name}`}>
|
|
39
|
-
<DatePicker
|
|
40
|
-
id={id ?? fieldPath}
|
|
41
|
-
name={field.name}
|
|
42
|
-
label={field.label}
|
|
43
|
-
required={!field.optional}
|
|
44
|
-
initialValue={incomingValue}
|
|
45
|
-
mode={field.mode || 'datetime'}
|
|
46
|
-
yearsInFuture={field.yearsInFuture || 1}
|
|
47
|
-
yearsInPast={field.yearsInPast || 10}
|
|
48
|
-
onDateChange={(date) => onChange?.(date)}
|
|
49
|
-
className={cx(isDirty && ['byline-field-datetime-dirty', styles.dirty])}
|
|
50
|
-
/>
|
|
51
|
-
{fieldError && <ErrorText id={`${field.name}-error`} text={fieldError} />}
|
|
52
|
-
</div>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DraggableContextMenu — small `…` dropdown attached to draggable list rows.
|
|
3
|
-
*
|
|
4
|
-
* Override handles:
|
|
5
|
-
* .byline-draggable-menu — dropdown content panel
|
|
6
|
-
* .byline-draggable-menu-item — individual menu item
|
|
7
|
-
* .byline-draggable-menu-row — flex row inside an item
|
|
8
|
-
* .byline-draggable-menu-icon-slot — fixed-width icon column
|
|
9
|
-
* .byline-draggable-menu-label — label text column
|
|
10
|
-
* .byline-draggable-menu-label-danger — destructive-action label
|
|
11
|
-
* .byline-draggable-menu-separator — separator between items
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
.menu,
|
|
15
|
-
:global(.byline-draggable-menu) {
|
|
16
|
-
z-index: 40;
|
|
17
|
-
width: 8.5rem;
|
|
18
|
-
padding: 0.25rem 0.375rem;
|
|
19
|
-
background-color: white;
|
|
20
|
-
border: var(--border-width-thin) var(--border-style-solid) var(--gray-200);
|
|
21
|
-
border-radius: var(--border-radius-sm);
|
|
22
|
-
box-shadow:
|
|
23
|
-
0 4px 6px -1px rgb(0 0 0 / 0.1),
|
|
24
|
-
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.item,
|
|
28
|
-
:global(.byline-draggable-menu-item) {
|
|
29
|
-
display: flex;
|
|
30
|
-
align-items: center;
|
|
31
|
-
gap: 0.25rem;
|
|
32
|
-
width: 100%;
|
|
33
|
-
padding: 5px 2px;
|
|
34
|
-
border-radius: var(--border-radius-sm);
|
|
35
|
-
color: var(--gray-600);
|
|
36
|
-
cursor: default;
|
|
37
|
-
user-select: none;
|
|
38
|
-
outline: none;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@media (min-width: 48rem) {
|
|
42
|
-
.item,
|
|
43
|
-
:global(.byline-draggable-menu-item) {
|
|
44
|
-
font-size: var(--font-size-sm);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.item:hover,
|
|
49
|
-
.item:focus,
|
|
50
|
-
:global(.byline-draggable-menu-item):hover,
|
|
51
|
-
:global(.byline-draggable-menu-item):focus {
|
|
52
|
-
background-color: oklch(from var(--canvas-50) l c h / 0.3);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.row,
|
|
56
|
-
:global(.byline-draggable-menu-row) {
|
|
57
|
-
display: flex;
|
|
58
|
-
align-items: center;
|
|
59
|
-
gap: 0.25rem;
|
|
60
|
-
width: 100%;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.icon-slot,
|
|
64
|
-
:global(.byline-draggable-menu-icon-slot) {
|
|
65
|
-
display: inline-block;
|
|
66
|
-
width: 22px;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.label,
|
|
70
|
-
:global(.byline-draggable-menu-label) {
|
|
71
|
-
display: inline-block;
|
|
72
|
-
flex: 1;
|
|
73
|
-
width: 100%;
|
|
74
|
-
align-self: flex-start;
|
|
75
|
-
text-align: left;
|
|
76
|
-
color: black;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.label-danger,
|
|
80
|
-
:global(.byline-draggable-menu-label-danger) {
|
|
81
|
-
color: var(--red-600);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.delete-icon,
|
|
85
|
-
:global(.byline-draggable-menu-delete-icon) {
|
|
86
|
-
stroke: var(--red-600);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.separator,
|
|
90
|
-
:global(.byline-draggable-menu-separator) {
|
|
91
|
-
width: 90%;
|
|
92
|
-
margin: 0.25rem auto;
|
|
93
|
-
border: none;
|
|
94
|
-
border-top: var(--border-width-thin) var(--border-style-solid) var(--gray-200);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* ─── Dark theme variants ───────────────────────────────────── */
|
|
98
|
-
|
|
99
|
-
:is([data-theme="dark"], :global(.dark)) {
|
|
100
|
-
.menu,
|
|
101
|
-
:global(.byline-draggable-menu) {
|
|
102
|
-
background-color: var(--canvas-800);
|
|
103
|
-
border-color: var(--canvas-700);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.item,
|
|
107
|
-
:global(.byline-draggable-menu-item) {
|
|
108
|
-
color: var(--gray-300);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.item:hover,
|
|
112
|
-
.item:focus,
|
|
113
|
-
:global(.byline-draggable-menu-item):hover,
|
|
114
|
-
:global(.byline-draggable-menu-item):focus {
|
|
115
|
-
background-color: var(--canvas-900);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.label,
|
|
119
|
-
:global(.byline-draggable-menu-label) {
|
|
120
|
-
color: var(--gray-300);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.separator,
|
|
124
|
-
:global(.byline-draggable-menu-separator) {
|
|
125
|
-
border-top-color: var(--gray-900);
|
|
126
|
-
}
|
|
127
|
-
}
|