@byline/ui 1.7.7 → 1.8.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.
|
@@ -487,6 +487,7 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
|
|
|
487
487
|
useAsPath: useAsPath,
|
|
488
488
|
collectionPath: collectionPath ?? '',
|
|
489
489
|
defaultLocale: defaultLocale,
|
|
490
|
+
activeLocale: contentLocale,
|
|
490
491
|
mode: mode
|
|
491
492
|
}),
|
|
492
493
|
(layout.sidebar ?? []).map((name)=>renderItem(name))
|
|
@@ -5,6 +5,15 @@ export interface PathWidgetProps {
|
|
|
5
5
|
collectionPath: string;
|
|
6
6
|
/** Default content locale, forwarded to the slugifier as context. */
|
|
7
7
|
defaultLocale: string;
|
|
8
|
+
/**
|
|
9
|
+
* The locale currently being edited in the form. When this differs
|
|
10
|
+
* from `defaultLocale` (i.e. the editor is editing a translation),
|
|
11
|
+
* the widget renders read-only — phase 1 paths are default-locale
|
|
12
|
+
* territory, and the lifecycle drops translation-locale path changes
|
|
13
|
+
* with a warn. Locking the input prevents the warn path being hit
|
|
14
|
+
* through the admin form and gives editors a clear cue.
|
|
15
|
+
*/
|
|
16
|
+
activeLocale: string;
|
|
8
17
|
/** `'create'` shows the live derived preview as placeholder text. */
|
|
9
18
|
mode: 'create' | 'edit';
|
|
10
19
|
}
|
|
@@ -22,4 +31,4 @@ export interface PathWidgetProps {
|
|
|
22
31
|
* Stable override handles: `.byline-form-path`, `.byline-form-path-header`,
|
|
23
32
|
* `.byline-form-path-regenerate`.
|
|
24
33
|
*/
|
|
25
|
-
export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, mode }: PathWidgetProps) => import("react").JSX.Element;
|
|
34
|
+
export declare const PathWidget: ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode, }: PathWidgetProps) => import("react").JSX.Element;
|
|
@@ -11,10 +11,11 @@ function coerceToString(value) {
|
|
|
11
11
|
if (value instanceof Date) return value.toISOString();
|
|
12
12
|
return String(value);
|
|
13
13
|
}
|
|
14
|
-
const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode })=>{
|
|
14
|
+
const PathWidget = ({ useAsPath, collectionPath, defaultLocale, activeLocale, mode })=>{
|
|
15
15
|
const { setSystemPath } = useFormContext();
|
|
16
16
|
const systemPath = useSystemPath();
|
|
17
17
|
const sourceValue = useFieldValue(useAsPath ?? '');
|
|
18
|
+
const isReadOnly = activeLocale !== defaultLocale;
|
|
18
19
|
const livePreview = useMemo(()=>{
|
|
19
20
|
if (!useAsPath) return '';
|
|
20
21
|
const asString = coerceToString(sourceValue);
|
|
@@ -52,13 +53,16 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode })=>{
|
|
|
52
53
|
defaultLocale,
|
|
53
54
|
collectionPath
|
|
54
55
|
]);
|
|
55
|
-
const
|
|
56
|
-
const
|
|
56
|
+
const validationHint = inputValue.length > 0 && formatted !== inputValue ? `Suggested: "${formatted}"` : void 0;
|
|
57
|
+
const readOnlyHint = isReadOnly ? `Path is set in the default locale ("${defaultLocale}") and applies across translations.` : void 0;
|
|
58
|
+
const hint = readOnlyHint ?? validationHint;
|
|
59
|
+
const placeholder = !isReadOnly && 'create' === mode && livePreview.length > 0 ? `Will be saved as "${livePreview}"` : void 0;
|
|
57
60
|
const srDescription = [
|
|
58
61
|
'System-managed URL path for this document.',
|
|
59
62
|
placeholder,
|
|
60
63
|
hint
|
|
61
64
|
].filter(Boolean).join(' ');
|
|
65
|
+
const showRegenerate = !isReadOnly && useAsPath && livePreview.length > 0 && livePreview !== systemPath;
|
|
62
66
|
return /*#__PURE__*/ jsxs("div", {
|
|
63
67
|
className: "byline-form-path",
|
|
64
68
|
children: [
|
|
@@ -70,7 +74,7 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode })=>{
|
|
|
70
74
|
htmlFor: "system-path",
|
|
71
75
|
label: "Path"
|
|
72
76
|
}),
|
|
73
|
-
|
|
77
|
+
showRegenerate && /*#__PURE__*/ jsxs("button", {
|
|
74
78
|
type: "button",
|
|
75
79
|
onClick: handleRegenerate,
|
|
76
80
|
className: classnames('byline-form-path-regenerate', path_widget_module.regenerate),
|
|
@@ -89,6 +93,7 @@ const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode })=>{
|
|
|
89
93
|
placeholder: placeholder,
|
|
90
94
|
onChange: (e)=>handleChange(e.target.value),
|
|
91
95
|
helpText: hint,
|
|
96
|
+
readOnly: isReadOnly,
|
|
92
97
|
"aria-describedby": "system-path-description"
|
|
93
98
|
}),
|
|
94
99
|
/*#__PURE__*/ jsx("span", {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": false,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.8.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=20.9.0"
|
|
9
9
|
},
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"react-diff-viewer-continued": "^4.2.2",
|
|
66
66
|
"zod": "^4.4.2",
|
|
67
67
|
"zod-form-data": "^3.0.1",
|
|
68
|
-
"@byline/
|
|
69
|
-
"@byline/
|
|
70
|
-
"@byline/
|
|
68
|
+
"@byline/client": "1.8.0",
|
|
69
|
+
"@byline/admin": "1.8.0",
|
|
70
|
+
"@byline/core": "1.8.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"react": "^19.0.0",
|
|
@@ -35,6 +35,15 @@ export interface PathWidgetProps {
|
|
|
35
35
|
collectionPath: string
|
|
36
36
|
/** Default content locale, forwarded to the slugifier as context. */
|
|
37
37
|
defaultLocale: string
|
|
38
|
+
/**
|
|
39
|
+
* The locale currently being edited in the form. When this differs
|
|
40
|
+
* from `defaultLocale` (i.e. the editor is editing a translation),
|
|
41
|
+
* the widget renders read-only — phase 1 paths are default-locale
|
|
42
|
+
* territory, and the lifecycle drops translation-locale path changes
|
|
43
|
+
* with a warn. Locking the input prevents the warn path being hit
|
|
44
|
+
* through the admin form and gives editors a clear cue.
|
|
45
|
+
*/
|
|
46
|
+
activeLocale: string
|
|
38
47
|
/** `'create'` shows the live derived preview as placeholder text. */
|
|
39
48
|
mode: 'create' | 'edit'
|
|
40
49
|
}
|
|
@@ -53,11 +62,23 @@ export interface PathWidgetProps {
|
|
|
53
62
|
* Stable override handles: `.byline-form-path`, `.byline-form-path-header`,
|
|
54
63
|
* `.byline-form-path-regenerate`.
|
|
55
64
|
*/
|
|
56
|
-
export const PathWidget = ({
|
|
65
|
+
export const PathWidget = ({
|
|
66
|
+
useAsPath,
|
|
67
|
+
collectionPath,
|
|
68
|
+
defaultLocale,
|
|
69
|
+
activeLocale,
|
|
70
|
+
mode,
|
|
71
|
+
}: PathWidgetProps) => {
|
|
57
72
|
const { setSystemPath } = useFormContext()
|
|
58
73
|
const systemPath = useSystemPath()
|
|
59
74
|
const sourceValue = useFieldValue<unknown>(useAsPath ?? '')
|
|
60
75
|
|
|
76
|
+
// Phase 1: paths are written/edited only under the default content
|
|
77
|
+
// locale. When editing a translation, the widget locks down — the
|
|
78
|
+
// input is read-only, the Regenerate action is suppressed, and a
|
|
79
|
+
// helpText line explains why.
|
|
80
|
+
const isReadOnly = activeLocale !== defaultLocale
|
|
81
|
+
|
|
61
82
|
// Live preview — what the server would derive from the current source
|
|
62
83
|
// field value if no override were set. Used as placeholder in create
|
|
63
84
|
// mode and as the target of the "Regenerate" action.
|
|
@@ -93,26 +114,40 @@ export const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode }: P
|
|
|
93
114
|
return slugify(inputValue, { locale: defaultLocale, collectionPath })
|
|
94
115
|
}, [inputValue, defaultLocale, collectionPath])
|
|
95
116
|
|
|
96
|
-
const
|
|
117
|
+
const validationHint =
|
|
97
118
|
inputValue.length > 0 && formatted !== inputValue ? `Suggested: "${formatted}"` : undefined
|
|
98
119
|
|
|
120
|
+
// When read-only, replace the live validation hint with a fixed
|
|
121
|
+
// explanatory line so editors understand why the field is locked.
|
|
122
|
+
const readOnlyHint = isReadOnly
|
|
123
|
+
? `Path is set in the default locale ("${defaultLocale}") and applies across translations.`
|
|
124
|
+
: undefined
|
|
125
|
+
|
|
126
|
+
const hint = readOnlyHint ?? validationHint
|
|
127
|
+
|
|
99
128
|
const placeholder =
|
|
100
|
-
mode === 'create' && livePreview.length > 0
|
|
129
|
+
!isReadOnly && mode === 'create' && livePreview.length > 0
|
|
130
|
+
? `Will be saved as "${livePreview}"`
|
|
131
|
+
: undefined
|
|
101
132
|
|
|
102
133
|
// Screen-reader description. The input's base purpose ("System-managed
|
|
103
134
|
// URL path") plus whichever of the visible hints (placeholder preview
|
|
104
|
-
// in create mode, "Suggested" validation hint
|
|
105
|
-
// visible helpText/placeholder cover sighted
|
|
106
|
-
// the same information addressable via
|
|
135
|
+
// in create mode, "Suggested" validation hint, or read-only explainer)
|
|
136
|
+
// currently applies. The visible helpText/placeholder cover sighted
|
|
137
|
+
// users; this element makes the same information addressable via
|
|
138
|
+
// aria-describedby for AT.
|
|
107
139
|
const srDescription = ['System-managed URL path for this document.', placeholder, hint]
|
|
108
140
|
.filter(Boolean)
|
|
109
141
|
.join(' ')
|
|
110
142
|
|
|
143
|
+
const showRegenerate =
|
|
144
|
+
!isReadOnly && useAsPath && livePreview.length > 0 && livePreview !== systemPath
|
|
145
|
+
|
|
111
146
|
return (
|
|
112
147
|
<div className="byline-form-path">
|
|
113
148
|
<div className={cx('byline-form-path-header', styles.header)}>
|
|
114
149
|
<Label id="system-path-label" htmlFor="system-path" label="Path" />
|
|
115
|
-
{
|
|
150
|
+
{showRegenerate && (
|
|
116
151
|
<button
|
|
117
152
|
type="button"
|
|
118
153
|
onClick={handleRegenerate}
|
|
@@ -130,6 +165,7 @@ export const PathWidget = ({ useAsPath, collectionPath, defaultLocale, mode }: P
|
|
|
130
165
|
placeholder={placeholder}
|
|
131
166
|
onChange={(e) => handleChange(e.target.value)}
|
|
132
167
|
helpText={hint}
|
|
168
|
+
readOnly={isReadOnly}
|
|
133
169
|
aria-describedby="system-path-description"
|
|
134
170
|
/>
|
|
135
171
|
<span
|