@byline/admin 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fields/array/array-field.d.ts +14 -0
- package/dist/fields/array/array-field.js +177 -0
- package/dist/fields/array/array-field.module.js +11 -0
- package/dist/fields/array/array-field_module.css +32 -0
- package/dist/fields/blocks/blocks-field.d.ts +13 -0
- package/dist/fields/blocks/blocks-field.js +245 -0
- package/dist/fields/blocks/blocks-field.module.js +26 -0
- package/dist/fields/blocks/blocks-field_module.css +107 -0
- package/dist/fields/checkbox/checkbox-field.d.ts +16 -0
- package/dist/fields/checkbox/checkbox-field.js +28 -0
- package/dist/fields/checkbox/checkbox-field.module.js +6 -0
- package/dist/fields/checkbox/checkbox-field_module.css +4 -0
- package/dist/fields/column-formatter.d.ts +20 -0
- package/dist/fields/column-formatter.js +15 -0
- package/dist/fields/date-time-formatter.d.ts +16 -0
- package/dist/fields/date-time-formatter.js +8 -0
- package/dist/fields/datetime/datetime-field.d.ts +16 -0
- package/dist/fields/datetime/datetime-field.js +37 -0
- package/dist/fields/datetime/datetime-field.module.js +5 -0
- package/dist/fields/datetime/datetime-field_module.css +4 -0
- package/dist/fields/draggable-context-menu.d.ts +6 -0
- package/dist/fields/draggable-context-menu.js +85 -0
- package/dist/fields/draggable-context-menu.module.js +15 -0
- package/dist/fields/draggable-context-menu_module.css +91 -0
- package/dist/fields/field-helpers.d.ts +26 -0
- package/dist/fields/field-helpers.js +50 -0
- package/dist/fields/field-renderer.d.ts +37 -0
- package/dist/fields/field-renderer.js +206 -0
- package/dist/fields/field-renderer.module.js +8 -0
- package/dist/fields/field-renderer_module.css +11 -0
- package/dist/fields/field-services-context.d.ts +16 -0
- package/dist/fields/field-services-context.js +13 -0
- package/dist/fields/field-services-types.d.ts +63 -0
- package/dist/fields/field-services-types.js +1 -0
- package/dist/fields/file/file-field.d.ts +19 -0
- package/dist/fields/file/file-field.js +225 -0
- package/dist/fields/file/file-field.module.js +18 -0
- package/dist/fields/file/file-field_module.css +131 -0
- package/dist/fields/file/file-upload-field.d.ts +21 -0
- package/dist/fields/file/file-upload-field.js +130 -0
- package/dist/fields/file/file-upload-field.module.js +15 -0
- package/dist/fields/file/file-upload-field_module.css +74 -0
- package/dist/fields/group/group-field.d.ts +15 -0
- package/dist/fields/group/group-field.js +59 -0
- package/dist/fields/group/group-field.module.js +9 -0
- package/dist/fields/group/group-field_module.css +27 -0
- package/dist/fields/image/image-field.d.ts +19 -0
- package/dist/fields/image/image-field.js +241 -0
- package/dist/fields/image/image-field.module.js +22 -0
- package/dist/fields/image/image-field_module.css +121 -0
- package/dist/fields/image/image-upload-field.d.ts +21 -0
- package/dist/fields/image/image-upload-field.js +190 -0
- package/dist/fields/image/image-upload-field.module.js +19 -0
- package/dist/fields/image/image-upload-field_module.css +92 -0
- package/dist/fields/local-date-time.d.ts +27 -0
- package/dist/fields/local-date-time.js +49 -0
- package/dist/fields/locale-badge.d.ts +18 -0
- package/dist/fields/locale-badge.js +10 -0
- package/dist/fields/locale-badge.module.js +5 -0
- package/dist/fields/locale-badge_module.css +27 -0
- package/dist/fields/numerical/numerical-field.d.ts +18 -0
- package/dist/fields/numerical/numerical-field.js +74 -0
- package/dist/fields/relation/relation-display.d.ts +40 -0
- package/dist/fields/relation/relation-display.js +58 -0
- package/dist/fields/relation/relation-display.module.js +9 -0
- package/dist/fields/relation/relation-display_module.css +21 -0
- package/dist/fields/relation/relation-field.d.ts +18 -0
- package/dist/fields/relation/relation-field.js +138 -0
- package/dist/fields/relation/relation-field.module.js +13 -0
- package/dist/fields/relation/relation-field_module.css +62 -0
- package/dist/fields/relation/relation-picker.d.ts +49 -0
- package/dist/fields/relation/relation-picker.js +236 -0
- package/dist/fields/relation/relation-picker.module.js +26 -0
- package/dist/fields/relation/relation-picker_module.css +124 -0
- package/dist/fields/relation/relation-summary.d.ts +31 -0
- package/dist/fields/relation/relation-summary.js +50 -0
- package/dist/fields/relation/relation-summary.module.js +11 -0
- package/dist/fields/relation/relation-summary_module.css +37 -0
- package/dist/fields/select/select-field.d.ts +16 -0
- package/dist/fields/select/select-field.js +50 -0
- package/dist/fields/select/select-field.module.js +5 -0
- package/dist/fields/select/select-field_module.css +4 -0
- package/dist/fields/sortable-item.d.ts +15 -0
- package/dist/fields/sortable-item.js +81 -0
- package/dist/fields/sortable-item.module.js +22 -0
- package/dist/fields/sortable-item_module.css +124 -0
- package/dist/fields/text/text-field.d.ts +20 -0
- package/dist/fields/text/text-field.js +104 -0
- package/dist/fields/text/text-field.module.js +6 -0
- package/dist/fields/text/text-field_module.css +5 -0
- package/dist/fields/text-area/text-area-field.d.ts +20 -0
- package/dist/fields/text-area/text-area-field.js +105 -0
- package/dist/fields/text-area/text-area-field.module.js +6 -0
- package/dist/fields/text-area/text-area-field_module.css +5 -0
- package/dist/fields/use-field-change-handler.d.ts +23 -0
- package/dist/fields/use-field-change-handler.js +52 -0
- package/dist/forms/document-actions.d.ts +48 -0
- package/dist/forms/document-actions.js +475 -0
- package/dist/forms/document-actions.module.js +34 -0
- package/dist/forms/document-actions_module.css +118 -0
- package/dist/forms/form-context.d.ts +89 -0
- package/dist/forms/form-context.js +466 -0
- package/dist/forms/form-renderer.d.ts +98 -0
- package/dist/forms/form-renderer.js +597 -0
- package/dist/forms/form-renderer.module.js +46 -0
- package/dist/forms/form-renderer_module.css +245 -0
- package/dist/forms/navigation-guard.d.ts +54 -0
- package/dist/forms/navigation-guard.js +22 -0
- package/dist/forms/path-widget.d.ts +36 -0
- package/dist/forms/path-widget.js +116 -0
- package/dist/forms/path-widget.module.js +8 -0
- package/dist/forms/path-widget_module.css +29 -0
- package/dist/forms/upload-executor.d.ts +57 -0
- package/dist/forms/upload-executor.js +94 -0
- package/dist/lib/translate-validation-error.d.ts +36 -0
- package/dist/lib/translate-validation-error.js +11 -0
- package/dist/modules/admin-account/commands.d.ts +2 -1
- package/dist/modules/admin-account/commands.js +13 -2
- package/dist/modules/admin-account/components/change-password.js +45 -36
- package/dist/modules/admin-account/components/container.js +185 -134
- package/dist/modules/admin-account/components/preferences.d.ts +8 -0
- package/dist/modules/admin-account/components/preferences.js +152 -0
- package/dist/modules/admin-account/components/preferences.module.js +11 -0
- package/dist/modules/admin-account/components/preferences_module.css +41 -0
- package/dist/modules/admin-account/components/update.js +50 -31
- package/dist/modules/admin-account/index.d.ts +3 -3
- package/dist/modules/admin-account/index.js +2 -2
- package/dist/modules/admin-account/schemas.d.ts +4 -0
- package/dist/modules/admin-account/schemas.js +4 -1
- package/dist/modules/admin-account/service.d.ts +1 -0
- package/dist/modules/admin-account/service.js +8 -0
- package/dist/modules/admin-permissions/components/inspector.js +31 -41
- package/dist/modules/admin-roles/components/create.js +43 -26
- package/dist/modules/admin-roles/components/permissions.js +26 -35
- package/dist/modules/admin-roles/components/update.js +26 -16
- package/dist/modules/admin-users/components/create.js +60 -40
- package/dist/modules/admin-users/components/roles.js +9 -15
- package/dist/modules/admin-users/components/set-password.js +30 -31
- package/dist/modules/admin-users/components/update.js +58 -39
- package/dist/modules/admin-users/dto.js +1 -0
- package/dist/modules/admin-users/repository.d.ts +17 -0
- package/dist/modules/admin-users/schemas.d.ts +4 -0
- package/dist/modules/admin-users/schemas.js +6 -2
- package/dist/modules/auth/components/sign-in-form.js +10 -8
- package/dist/presentation/group.d.ts +27 -0
- package/dist/presentation/group.js +14 -0
- package/dist/presentation/group.module.js +6 -0
- package/dist/presentation/group_module.css +19 -0
- package/dist/presentation/row.d.ts +25 -0
- package/dist/presentation/row.js +8 -0
- package/dist/presentation/row.module.js +5 -0
- package/dist/presentation/row_module.css +18 -0
- package/dist/presentation/tabs.d.ts +25 -0
- package/dist/presentation/tabs.js +39 -0
- package/dist/presentation/tabs.module.js +10 -0
- package/dist/presentation/tabs_module.css +68 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +36 -0
- package/dist/services/admin-services-types.d.ts +16 -0
- package/dist/widgets/diff-viewer/diff-modal.d.ts +22 -0
- package/dist/widgets/diff-viewer/diff-modal.js +149 -0
- package/dist/widgets/diff-viewer/diff-modal.module.js +14 -0
- package/dist/widgets/diff-viewer/diff-modal_module.css +56 -0
- package/dist/widgets/status-badge/status-badge.d.ts +25 -0
- package/dist/widgets/status-badge/status-badge.js +37 -0
- package/dist/widgets/status-badge/status-badge.module.js +7 -0
- package/dist/widgets/status-badge/status-badge_module.css +20 -0
- package/package.json +14 -4
- package/src/fields/array/array-field.module.css +48 -0
- package/src/fields/array/array-field.tsx +267 -0
- package/src/fields/blocks/blocks-field.module.css +148 -0
- package/src/fields/blocks/blocks-field.tsx +323 -0
- package/src/fields/checkbox/checkbox-field.module.css +4 -0
- package/src/fields/checkbox/checkbox-field.tsx +54 -0
- package/src/fields/column-formatter.tsx +31 -0
- package/src/fields/date-time-formatter.tsx +22 -0
- package/src/fields/datetime/datetime-field.module.css +13 -0
- package/src/fields/datetime/datetime-field.tsx +54 -0
- package/src/fields/draggable-context-menu.module.css +127 -0
- package/src/fields/draggable-context-menu.tsx +87 -0
- package/src/fields/field-helpers.ts +69 -0
- package/src/fields/field-renderer.module.css +22 -0
- package/src/fields/field-renderer.tsx +288 -0
- package/src/fields/field-services-context.tsx +35 -0
- package/src/fields/field-services-types.ts +68 -0
- package/src/fields/file/file-field.module.css +153 -0
- package/src/fields/file/file-field.tsx +286 -0
- package/src/fields/file/file-upload-field.module.css +101 -0
- package/src/fields/file/file-upload-field.tsx +187 -0
- package/src/fields/group/group-field.module.css +43 -0
- package/src/fields/group/group-field.tsx +84 -0
- package/src/fields/image/image-field.module.css +155 -0
- package/src/fields/image/image-field.tsx +306 -0
- package/src/fields/image/image-upload-field.module.css +123 -0
- package/src/fields/image/image-upload-field.tsx +276 -0
- package/src/fields/local-date-time.tsx +88 -0
- package/src/fields/locale-badge.module.css +37 -0
- package/src/fields/locale-badge.tsx +32 -0
- package/src/fields/numerical/numerical-field.tsx +114 -0
- package/src/fields/relation/relation-display.module.css +36 -0
- package/src/fields/relation/relation-display.tsx +130 -0
- package/src/fields/relation/relation-field.module.css +83 -0
- package/src/fields/relation/relation-field.tsx +211 -0
- package/src/fields/relation/relation-picker.module.css +168 -0
- package/src/fields/relation/relation-picker.tsx +326 -0
- package/src/fields/relation/relation-summary.module.css +55 -0
- package/src/fields/relation/relation-summary.tsx +123 -0
- package/src/fields/select/select-field.module.css +13 -0
- package/src/fields/select/select-field.tsx +61 -0
- package/src/fields/sortable-item.module.css +167 -0
- package/src/fields/sortable-item.tsx +106 -0
- package/src/fields/text/text-field.module.css +13 -0
- package/src/fields/text/text-field.tsx +146 -0
- package/src/fields/text-area/text-area-field.module.css +13 -0
- package/src/fields/text-area/text-area-field.tsx +147 -0
- package/src/fields/use-field-change-handler.ts +112 -0
- package/src/forms/document-actions.module.css +160 -0
- package/src/forms/document-actions.tsx +482 -0
- package/src/forms/form-context.tsx +704 -0
- package/src/forms/form-renderer.module.css +321 -0
- package/src/forms/form-renderer.tsx +891 -0
- package/src/forms/navigation-guard.tsx +98 -0
- package/src/forms/path-widget.module.css +41 -0
- package/src/forms/path-widget.test.tsx +217 -0
- package/src/forms/path-widget.tsx +183 -0
- package/src/forms/upload-executor.ts +192 -0
- package/src/lib/translate-validation-error.ts +56 -0
- package/src/modules/admin-account/commands.ts +13 -0
- package/src/modules/admin-account/components/change-password.tsx +46 -31
- package/src/modules/admin-account/components/container.tsx +83 -38
- package/src/modules/admin-account/components/preferences.module.css +60 -0
- package/src/modules/admin-account/components/preferences.tsx +203 -0
- package/src/modules/admin-account/components/update.tsx +53 -27
- package/src/modules/admin-account/index.ts +3 -0
- package/src/modules/admin-account/schemas.ts +13 -0
- package/src/modules/admin-account/service.ts +12 -0
- package/src/modules/admin-permissions/components/inspector.tsx +22 -14
- package/src/modules/admin-roles/components/create.tsx +51 -23
- package/src/modules/admin-roles/components/permissions.tsx +25 -21
- package/src/modules/admin-roles/components/update.tsx +37 -19
- package/src/modules/admin-users/components/create.tsx +63 -34
- package/src/modules/admin-users/components/roles.tsx +9 -8
- package/src/modules/admin-users/components/set-password.tsx +34 -28
- package/src/modules/admin-users/components/update.tsx +58 -36
- package/src/modules/admin-users/dto.ts +1 -0
- package/src/modules/admin-users/repository.ts +17 -0
- package/src/modules/admin-users/schemas.ts +12 -0
- package/src/modules/auth/components/sign-in-form.tsx +14 -8
- package/src/presentation/group.module.css +41 -0
- package/src/presentation/group.tsx +40 -0
- package/src/presentation/row.module.css +32 -0
- package/src/presentation/row.tsx +33 -0
- package/src/presentation/tabs.module.css +107 -0
- package/src/presentation/tabs.tsx +84 -0
- package/src/react.ts +84 -0
- package/src/services/admin-services-types.ts +18 -0
- package/src/widgets/diff-viewer/diff-modal.module.css +79 -0
- package/src/widgets/diff-viewer/diff-modal.tsx +186 -0
- package/src/widgets/status-badge/status-badge.module.css +31 -0
- package/src/widgets/status-badge/status-badge.tsx +71 -0
|
@@ -0,0 +1,147 @@
|
|
|
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 { useCallback } from 'react'
|
|
10
|
+
|
|
11
|
+
import type { Field, FieldComponentSlots, TextAreaField as FieldType } from '@byline/core'
|
|
12
|
+
import { Label, TextArea } from '@byline/ui/react'
|
|
13
|
+
import cx from 'classnames'
|
|
14
|
+
|
|
15
|
+
import { useFieldError, useFieldValue } from '../../forms/form-context'
|
|
16
|
+
import { LocaleBadge } from '../locale-badge'
|
|
17
|
+
import styles from './text-area-field.module.css'
|
|
18
|
+
|
|
19
|
+
export const TextAreaField = ({
|
|
20
|
+
field,
|
|
21
|
+
value,
|
|
22
|
+
defaultValue,
|
|
23
|
+
onChange,
|
|
24
|
+
id,
|
|
25
|
+
path,
|
|
26
|
+
locale,
|
|
27
|
+
components,
|
|
28
|
+
}: {
|
|
29
|
+
field: FieldType
|
|
30
|
+
value?: string
|
|
31
|
+
defaultValue?: string
|
|
32
|
+
onChange?: (value: string) => void
|
|
33
|
+
id?: string
|
|
34
|
+
path?: string
|
|
35
|
+
/** When provided, renders a LocaleBadge next to the field label. */
|
|
36
|
+
locale?: string
|
|
37
|
+
/** Optional UI component slot overrides from the admin config. */
|
|
38
|
+
components?: FieldComponentSlots
|
|
39
|
+
}) => {
|
|
40
|
+
const fieldPath = path ?? field.name
|
|
41
|
+
const fieldError = useFieldError(fieldPath)
|
|
42
|
+
const fieldValue = useFieldValue<string | undefined>(fieldPath)
|
|
43
|
+
const incomingValue = value ?? fieldValue ?? defaultValue ?? ''
|
|
44
|
+
const htmlId = id ?? fieldPath
|
|
45
|
+
|
|
46
|
+
const handleChange = useCallback(
|
|
47
|
+
(value: string) => {
|
|
48
|
+
if (onChange) {
|
|
49
|
+
onChange(value)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[onChange]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// Custom component slots (from admin config)
|
|
56
|
+
const slots = components
|
|
57
|
+
const CustomLabel = slots?.Label
|
|
58
|
+
const CustomHelpText = slots?.HelpText
|
|
59
|
+
const CustomField = slots?.Field
|
|
60
|
+
const BeforeField = slots?.beforeField
|
|
61
|
+
const AfterField = slots?.afterField
|
|
62
|
+
|
|
63
|
+
// Shared props available to every slot component
|
|
64
|
+
const slotBaseProps = {
|
|
65
|
+
field: field as Field,
|
|
66
|
+
path: fieldPath,
|
|
67
|
+
value: incomingValue,
|
|
68
|
+
error: fieldError,
|
|
69
|
+
id: htmlId,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// When a locale is active, render a custom Label+badge and suppress the
|
|
73
|
+
// TextArea's own label so the locale indicator appears in the label row.
|
|
74
|
+
const showBadge = !!locale && !!field.label
|
|
75
|
+
|
|
76
|
+
// Determine whether the label is handled externally (by a custom slot or
|
|
77
|
+
// the locale badge row) so TextArea doesn't render its own.
|
|
78
|
+
const hasCustomLabel = !!CustomLabel
|
|
79
|
+
const suppressInputLabel = showBadge || hasCustomLabel
|
|
80
|
+
const suppressInputHelpText = !!CustomHelpText
|
|
81
|
+
|
|
82
|
+
const labelRowClass = cx('byline-field-text-area-label-row', styles['label-row'])
|
|
83
|
+
|
|
84
|
+
// ── Label rendering ──────────────────────────────────────────
|
|
85
|
+
const renderLabel = () => {
|
|
86
|
+
if (hasCustomLabel) {
|
|
87
|
+
return (
|
|
88
|
+
<div className={labelRowClass}>
|
|
89
|
+
<CustomLabel {...slotBaseProps} label={field.label} required={!field.optional} />
|
|
90
|
+
{showBadge && <LocaleBadge locale={locale!} />}
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
if (showBadge) {
|
|
95
|
+
return (
|
|
96
|
+
<div className={labelRowClass}>
|
|
97
|
+
<Label
|
|
98
|
+
id={`${htmlId}-label`}
|
|
99
|
+
htmlFor={htmlId}
|
|
100
|
+
label={field.label!}
|
|
101
|
+
required={!field.optional}
|
|
102
|
+
/>
|
|
103
|
+
<LocaleBadge locale={locale!} />
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Field input rendering ────────────────────────────────────
|
|
111
|
+
const renderInput = () => {
|
|
112
|
+
if (CustomField) {
|
|
113
|
+
return (
|
|
114
|
+
<CustomField
|
|
115
|
+
{...slotBaseProps}
|
|
116
|
+
onChange={handleChange}
|
|
117
|
+
defaultValue={defaultValue}
|
|
118
|
+
placeholder={field.placeholder}
|
|
119
|
+
/>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
return (
|
|
123
|
+
<TextArea
|
|
124
|
+
id={htmlId}
|
|
125
|
+
name={field.name}
|
|
126
|
+
label={suppressInputLabel ? undefined : field.label}
|
|
127
|
+
required={!field.optional}
|
|
128
|
+
helpText={suppressInputHelpText ? undefined : field.helpText}
|
|
129
|
+
value={incomingValue}
|
|
130
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => handleChange(e.target.value)}
|
|
131
|
+
error={fieldError != null}
|
|
132
|
+
errorText={fieldError}
|
|
133
|
+
rows={4}
|
|
134
|
+
/>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className={`byline-field-text-area ${field.name}`}>
|
|
140
|
+
{renderLabel()}
|
|
141
|
+
{BeforeField && <BeforeField {...slotBaseProps} />}
|
|
142
|
+
{renderInput()}
|
|
143
|
+
{AfterField && <AfterField {...slotBaseProps} />}
|
|
144
|
+
{CustomHelpText && <CustomHelpText {...slotBaseProps} helpText={field.helpText} />}
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
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 { useCallback } from 'react'
|
|
10
|
+
|
|
11
|
+
import type { Field, FieldBeforeChangeResult, FieldHookContext } from '@byline/core'
|
|
12
|
+
import { normalizeHooks } from '@byline/core'
|
|
13
|
+
|
|
14
|
+
import { useFormContext } from '../forms/form-context'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Returns a change handler for the given field that runs through the
|
|
18
|
+
* field-hook pipeline before committing the value:
|
|
19
|
+
*
|
|
20
|
+
* 1. `clearFieldError(path)`
|
|
21
|
+
* 2. `field.hooks.beforeValidate(ctx)` — advisory: may set an error on
|
|
22
|
+
* the field but the value is **always** committed (user can keep typing)
|
|
23
|
+
* 3. `field.hooks.beforeChange(ctx)` — may return `{ value }` to replace
|
|
24
|
+
* or `{ error }` to block the change entirely
|
|
25
|
+
* 4. `setFieldValue(path, finalValue)`
|
|
26
|
+
*
|
|
27
|
+
* When the field has no hooks the function is a zero-overhead pass-through
|
|
28
|
+
* to `setFieldValue` (no promises, no extra allocations).
|
|
29
|
+
*/
|
|
30
|
+
export function useFieldChangeHandler(field: Field, path: string) {
|
|
31
|
+
const { setFieldValue, getFieldValue, getFieldValues, setFieldError, clearFieldError } =
|
|
32
|
+
useFormContext()
|
|
33
|
+
|
|
34
|
+
return useCallback(
|
|
35
|
+
(value: any) => {
|
|
36
|
+
const hooks = field.hooks
|
|
37
|
+
|
|
38
|
+
// ── fast path: no hooks defined ────────────────────────────
|
|
39
|
+
const validateFns = normalizeHooks(hooks?.beforeValidate)
|
|
40
|
+
const changeFns = normalizeHooks(hooks?.beforeChange)
|
|
41
|
+
|
|
42
|
+
if (validateFns.length === 0 && changeFns.length === 0) {
|
|
43
|
+
setFieldValue(path, value)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── slow path: run async hook pipeline ─────────────────────
|
|
48
|
+
const previousValue = getFieldValue(path)
|
|
49
|
+
const ctx: FieldHookContext = {
|
|
50
|
+
value,
|
|
51
|
+
previousValue,
|
|
52
|
+
data: getFieldValues(),
|
|
53
|
+
path,
|
|
54
|
+
field,
|
|
55
|
+
operation: 'change',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
clearFieldError(path)
|
|
59
|
+
|
|
60
|
+
// When there are only advisory beforeValidate hooks (no beforeChange that can
|
|
61
|
+
// block or transform the value), commit synchronously so that controlled inputs
|
|
62
|
+
// (e.g. <input value={...}>) receive the updated value before React's next
|
|
63
|
+
// render. Deferring even one microtask tick (via await) causes React to
|
|
64
|
+
// reconcile the stale value prop against the DOM and reset cursor position.
|
|
65
|
+
if (changeFns.length === 0) {
|
|
66
|
+
setFieldValue(path, value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void (async () => {
|
|
70
|
+
try {
|
|
71
|
+
// 1. beforeValidate (advisory — value is always committed)
|
|
72
|
+
let advisoryError: string | undefined
|
|
73
|
+
for (const fn of validateFns) {
|
|
74
|
+
const result = (await fn(ctx)) as FieldBeforeChangeResult | undefined
|
|
75
|
+
if (result?.error) {
|
|
76
|
+
advisoryError = result.error
|
|
77
|
+
}
|
|
78
|
+
if (result?.value !== undefined) {
|
|
79
|
+
ctx.value = result.value
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 2. beforeChange
|
|
84
|
+
for (const fn of changeFns) {
|
|
85
|
+
const result = (await fn(ctx)) as FieldBeforeChangeResult | undefined
|
|
86
|
+
if (result?.error) {
|
|
87
|
+
setFieldError(path, result.error)
|
|
88
|
+
return // block the change
|
|
89
|
+
}
|
|
90
|
+
if (result?.value !== undefined) {
|
|
91
|
+
ctx.value = result.value
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 3. commit the (possibly transformed) value and surface any advisory error.
|
|
96
|
+
// If there were no beforeChange hooks we already committed synchronously above;
|
|
97
|
+
// this call is a no-op if the value hasn't been altered by a hook.
|
|
98
|
+
setFieldValue(path, ctx.value)
|
|
99
|
+
if (advisoryError) {
|
|
100
|
+
setFieldError(path, advisoryError)
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
// Surface unexpected hook errors as field errors rather than crashing
|
|
104
|
+
const message = err instanceof Error ? err.message : 'Unexpected hook error'
|
|
105
|
+
setFieldError(path, message)
|
|
106
|
+
}
|
|
107
|
+
})()
|
|
108
|
+
},
|
|
109
|
+
// field reference is stable per render cycle; path is derived from props
|
|
110
|
+
[field, path, setFieldValue, getFieldValue, getFieldValues, setFieldError, clearFieldError]
|
|
111
|
+
)
|
|
112
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocumentActions — overflow menu (Unpublish / Delete) on the form chrome.
|
|
3
|
+
*
|
|
4
|
+
* Override handles:
|
|
5
|
+
* .byline-form-actions-icon — the rotated ellipsis trigger icon
|
|
6
|
+
* .byline-form-actions-menu — dropdown content panel
|
|
7
|
+
* .byline-form-actions-item — a row inside the dropdown
|
|
8
|
+
* .byline-form-actions-item-icon — fixed-width icon column
|
|
9
|
+
* .byline-form-actions-item-text — label column
|
|
10
|
+
* .byline-form-actions-delete — destructive label colour
|
|
11
|
+
* .byline-form-actions-modal-head — modal header layout
|
|
12
|
+
* .byline-form-actions-modal-title — modal heading text
|
|
13
|
+
* .byline-form-actions-sr-only — visually-hidden autofocus shim
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
.icon,
|
|
17
|
+
:global(.byline-form-actions-icon) {
|
|
18
|
+
transform: rotate(90deg);
|
|
19
|
+
color: var(--primary-500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.menu,
|
|
23
|
+
:global(.byline-form-actions-menu) {
|
|
24
|
+
min-width: 140px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.item,
|
|
28
|
+
:global(.byline-form-actions-item) {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
margin-left: var(--spacing-8);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.item-icon,
|
|
35
|
+
:global(.byline-form-actions-item-icon) {
|
|
36
|
+
display: inline-block;
|
|
37
|
+
width: 28px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.item-text,
|
|
41
|
+
:global(.byline-form-actions-item-text) {
|
|
42
|
+
display: inline-block;
|
|
43
|
+
width: 100%;
|
|
44
|
+
flex: 1;
|
|
45
|
+
font-size: var(--font-size-sm);
|
|
46
|
+
text-align: left;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.delete,
|
|
50
|
+
:global(.byline-form-actions-delete) {
|
|
51
|
+
flex: 1;
|
|
52
|
+
width: 100%;
|
|
53
|
+
border: none;
|
|
54
|
+
background: none;
|
|
55
|
+
padding: 0;
|
|
56
|
+
color: var(--red-600);
|
|
57
|
+
line-height: 1;
|
|
58
|
+
text-align: left;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.modal-head,
|
|
63
|
+
:global(.byline-form-actions-modal-head) {
|
|
64
|
+
padding-top: 1rem;
|
|
65
|
+
margin-bottom: var(--spacing-8);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.modal-title,
|
|
69
|
+
:global(.byline-form-actions-modal-title) {
|
|
70
|
+
margin: 0 0 var(--spacing-8) 0;
|
|
71
|
+
font-size: var(--font-size-2xl);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.sr-only,
|
|
75
|
+
:global(.byline-form-actions-sr-only) {
|
|
76
|
+
position: absolute;
|
|
77
|
+
width: 1px;
|
|
78
|
+
height: 1px;
|
|
79
|
+
padding: 0;
|
|
80
|
+
margin: -1px;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
clip: rect(0, 0, 0, 0);
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
border: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.list,
|
|
88
|
+
:global(.byline-form-actions-list) {
|
|
89
|
+
margin: var(--spacing-8) 0;
|
|
90
|
+
padding-left: 1.25rem;
|
|
91
|
+
font-size: var(--font-size-sm);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.preview,
|
|
95
|
+
:global(.byline-form-actions-preview) {
|
|
96
|
+
margin-top: var(--spacing-16);
|
|
97
|
+
padding: var(--spacing-8) var(--spacing-12);
|
|
98
|
+
background: var(--surface-2, rgba(0, 0, 0, 0.03));
|
|
99
|
+
border: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.08));
|
|
100
|
+
border-radius: var(--radius-md, 4px);
|
|
101
|
+
font-size: var(--font-size-sm);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.preview-label,
|
|
105
|
+
:global(.byline-form-actions-preview-label) {
|
|
106
|
+
font-weight: 500;
|
|
107
|
+
margin-bottom: var(--spacing-4);
|
|
108
|
+
opacity: 0.75;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.preview-row,
|
|
112
|
+
:global(.byline-form-actions-preview-row) {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: var(--spacing-8);
|
|
116
|
+
flex-wrap: wrap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.preview-before,
|
|
120
|
+
:global(.byline-form-actions-preview-before) {
|
|
121
|
+
font-family: var(--font-mono, monospace);
|
|
122
|
+
opacity: 0.7;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.preview-arrow,
|
|
126
|
+
:global(.byline-form-actions-preview-arrow) {
|
|
127
|
+
opacity: 0.5;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.preview-after,
|
|
131
|
+
:global(.byline-form-actions-preview-after) {
|
|
132
|
+
font-family: var(--font-mono, monospace);
|
|
133
|
+
font-weight: 500;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.copy-row,
|
|
137
|
+
:global(.byline-form-actions-copy-row) {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
flex-wrap: wrap;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.copy-label,
|
|
144
|
+
:global(.byline-form-actions-copy-label) {
|
|
145
|
+
/* Layout-only handle; spacing is set inline alongside the field. */
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.copy-source,
|
|
149
|
+
:global(.byline-form-actions-copy-source) {
|
|
150
|
+
font-family: var(--font-mono, monospace);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* ─── Dark theme variants ───────────────────────────────────── */
|
|
154
|
+
|
|
155
|
+
:is([data-theme="dark"], :global(.dark)) {
|
|
156
|
+
.delete,
|
|
157
|
+
:global(.byline-form-actions-delete) {
|
|
158
|
+
color: var(--red-400);
|
|
159
|
+
}
|
|
160
|
+
}
|