@byline/admin 4.0.0 → 4.2.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.js +20 -8
- package/dist/fields/array/array-field.module.js +5 -1
- package/dist/fields/array/array-field_module.css +23 -0
- package/dist/fields/blocks/blocks-field.js +33 -12
- package/dist/fields/blocks/blocks-field.module.js +2 -0
- package/dist/fields/blocks/blocks-field_module.css +37 -0
- package/dist/fields/code/code-editor.d.ts +20 -0
- package/dist/fields/code/code-editor.js +239 -0
- package/dist/fields/code/code-field.d.ts +21 -0
- package/dist/fields/code/code-field.js +124 -0
- package/dist/fields/code/code-field.module.js +7 -0
- package/dist/fields/code/code-field_module.css +58 -0
- package/dist/fields/field-helpers.js +1 -0
- package/dist/fields/field-renderer.js +15 -1
- package/dist/fields/group/group-field.d.ts +10 -2
- package/dist/fields/group/group-field.js +4 -2
- package/dist/fields/relation/relation-picker.js +8 -2
- package/dist/fields/select/select-field.js +4 -2
- package/dist/fields/select/select-field.module.js +1 -0
- package/dist/fields/select/select-field_module.css +4 -0
- package/dist/fields/text/text-field_module.css +1 -0
- package/dist/fields/text-area/text-area-field_module.css +1 -0
- package/dist/forms/form-renderer.d.ts +1 -1
- package/dist/forms/tree-placement-widget.d.ts +1 -1
- package/package.json +18 -5
- package/src/fields/array/array-field.module.css +30 -0
- package/src/fields/array/array-field.tsx +15 -2
- package/src/fields/blocks/blocks-field.module.css +53 -0
- package/src/fields/blocks/blocks-field.tsx +33 -1
- package/src/fields/code/code-editor.tsx +246 -0
- package/src/fields/code/code-field.module.css +84 -0
- package/src/fields/code/code-field.tsx +191 -0
- package/src/fields/field-helpers.ts +1 -0
- package/src/fields/field-renderer.tsx +19 -2
- package/src/fields/field-services-types.ts +1 -1
- package/src/fields/group/group-field.tsx +12 -1
- package/src/fields/relation/relation-picker.tsx +11 -0
- package/src/fields/select/select-field.module.css +5 -0
- package/src/fields/select/select-field.tsx +9 -2
- package/src/fields/text/text-field.module.css +1 -0
- package/src/fields/text-area/text-area-field.module.css +1 -0
- package/src/forms/form-renderer.tsx +1 -1
- package/src/forms/tree-placement-widget.tsx +1 -1
|
@@ -175,14 +175,26 @@ const ArrayField = ({ field, defaultValue, path, disableSorting = false, collect
|
|
|
175
175
|
className: classnames('byline-field-array-stack', array_field_module.stack),
|
|
176
176
|
children: [
|
|
177
177
|
items.map((item, index)=>renderItem(item, index)),
|
|
178
|
-
/*#__PURE__*/
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
178
|
+
/*#__PURE__*/ jsxs("div", {
|
|
179
|
+
className: classnames('byline-field-array-add-row', array_field_module["add-row"]),
|
|
180
|
+
children: [
|
|
181
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
182
|
+
onClick: ()=>{
|
|
183
|
+
handleAddItem();
|
|
184
|
+
},
|
|
185
|
+
"aria-label": t('fields.array.addItemAriaLabel'),
|
|
186
|
+
children: /*#__PURE__*/ jsx(PlusIcon, {})
|
|
187
|
+
}),
|
|
188
|
+
/*#__PURE__*/ jsx("button", {
|
|
189
|
+
type: "button",
|
|
190
|
+
tabIndex: -1,
|
|
191
|
+
onClick: ()=>{
|
|
192
|
+
handleAddItem();
|
|
193
|
+
},
|
|
194
|
+
className: classnames('byline-field-array-add-label', array_field_module["add-label"]),
|
|
195
|
+
children: t('fields.array.addItem')
|
|
196
|
+
})
|
|
197
|
+
]
|
|
186
198
|
})
|
|
187
199
|
]
|
|
188
200
|
})
|
|
@@ -6,6 +6,10 @@ const array_field_module = {
|
|
|
6
6
|
"group-header": "group-header-KJxpIV",
|
|
7
7
|
groupHeader: "group-header-KJxpIV",
|
|
8
8
|
"group-fields": "group-fields-Bmi_hA",
|
|
9
|
-
groupFields: "group-fields-Bmi_hA"
|
|
9
|
+
groupFields: "group-fields-Bmi_hA",
|
|
10
|
+
"add-row": "add-row-bCHtVe",
|
|
11
|
+
addRow: "add-row-bCHtVe",
|
|
12
|
+
"add-label": "add-label-KdsV2D",
|
|
13
|
+
addLabel: "add-label-KdsV2D"
|
|
10
14
|
};
|
|
11
15
|
export default array_field_module;
|
|
@@ -30,3 +30,26 @@
|
|
|
30
30
|
display: flex;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
:is(.add-row-bCHtVe, .byline-field-array-add-row) {
|
|
34
|
+
color: var(--muted);
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: var(--spacing-8);
|
|
37
|
+
font-size: .9rem;
|
|
38
|
+
display: flex;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:is(.add-label-KdsV2D, .byline-field-array-add-label) {
|
|
42
|
+
appearance: none;
|
|
43
|
+
font: inherit;
|
|
44
|
+
color: inherit;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
background: none;
|
|
47
|
+
border: none;
|
|
48
|
+
padding: 0;
|
|
49
|
+
transition: color .12s;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:is(.add-row-bCHtVe:hover .add-label-KdsV2D, .byline-field-array-add-row:hover .byline-field-array-add-label) {
|
|
53
|
+
color: var(--text);
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { getClientConfig } from "@byline/core";
|
|
3
4
|
import { useTranslation } from "@byline/i18n/react";
|
|
4
5
|
import { Card, CloseIcon, DraggableSortable, IconButton, Modal, PlusIcon, moveItem } from "@byline/ui/react";
|
|
5
6
|
import classnames from "classnames";
|
|
@@ -17,6 +18,11 @@ const BlocksField = ({ field, defaultValue, path, contentLocale })=>{
|
|
|
17
18
|
const availableBlocks = useMemo(()=>field.blocks ?? [], [
|
|
18
19
|
field.blocks
|
|
19
20
|
]);
|
|
21
|
+
const blockAdminByType = useMemo(()=>{
|
|
22
|
+
const map = new Map();
|
|
23
|
+
for (const entry of getClientConfig().blockAdmin ?? [])map.set(entry.blockType, entry);
|
|
24
|
+
return map;
|
|
25
|
+
}, []);
|
|
20
26
|
const [selectedBlockName, setSelectedBlockName] = useState(()=>availableBlocks[0]?.blockType ?? '');
|
|
21
27
|
useEffect(()=>{
|
|
22
28
|
if (null == selectedBlockName || !availableBlocks.some((b)=>b.blockType === selectedBlockName)) setSelectedBlockName(availableBlocks[0]?.blockType ?? '');
|
|
@@ -130,7 +136,8 @@ const BlocksField = ({ field, defaultValue, path, contentLocale })=>{
|
|
|
130
136
|
},
|
|
131
137
|
defaultValue: fieldData,
|
|
132
138
|
path: arrayElementPath,
|
|
133
|
-
contentLocale: contentLocale
|
|
139
|
+
contentLocale: contentLocale,
|
|
140
|
+
fieldAdmin: blockAdminByType.get(item._type)?.fields
|
|
134
141
|
}, subField.blockType);
|
|
135
142
|
return /*#__PURE__*/ jsx(SortableItem, {
|
|
136
143
|
id: itemWrapper.id,
|
|
@@ -153,17 +160,31 @@ const BlocksField = ({ field, defaultValue, path, contentLocale })=>{
|
|
|
153
160
|
className: classnames('byline-field-blocks-stack', blocks_field_module.stack),
|
|
154
161
|
children: [
|
|
155
162
|
items.map((item, index)=>renderItem(item, index)),
|
|
156
|
-
/*#__PURE__*/
|
|
163
|
+
/*#__PURE__*/ jsxs("div", {
|
|
157
164
|
className: classnames('byline-field-blocks-add-row', blocks_field_module["add-row"]),
|
|
158
|
-
children:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
children: [
|
|
166
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
167
|
+
onClick: ()=>{
|
|
168
|
+
setPendingInsertIndex(null);
|
|
169
|
+
setShowAddBlockModal(true);
|
|
170
|
+
},
|
|
171
|
+
disabled: !selectedBlockName,
|
|
172
|
+
"aria-label": t('fields.blocks.addBlockAriaLabel'),
|
|
173
|
+
variant: "outlined",
|
|
174
|
+
children: /*#__PURE__*/ jsx(PlusIcon, {})
|
|
175
|
+
}),
|
|
176
|
+
/*#__PURE__*/ jsx("button", {
|
|
177
|
+
type: "button",
|
|
178
|
+
tabIndex: -1,
|
|
179
|
+
disabled: !selectedBlockName,
|
|
180
|
+
onClick: ()=>{
|
|
181
|
+
setPendingInsertIndex(null);
|
|
182
|
+
setShowAddBlockModal(true);
|
|
183
|
+
},
|
|
184
|
+
className: classnames('byline-field-blocks-add-label', blocks_field_module["add-label"]),
|
|
185
|
+
children: t('fields.blocks.addBlock')
|
|
186
|
+
})
|
|
187
|
+
]
|
|
167
188
|
})
|
|
168
189
|
]
|
|
169
190
|
}),
|
|
@@ -176,7 +197,7 @@ const BlocksField = ({ field, defaultValue, path, contentLocale })=>{
|
|
|
176
197
|
},
|
|
177
198
|
children: /*#__PURE__*/ jsxs(Modal.Container, {
|
|
178
199
|
style: {
|
|
179
|
-
maxWidth: '
|
|
200
|
+
maxWidth: '700px'
|
|
180
201
|
},
|
|
181
202
|
children: [
|
|
182
203
|
/*#__PURE__*/ jsxs(Modal.Header, {
|
|
@@ -4,6 +4,8 @@ const blocks_field_module = {
|
|
|
4
4
|
stack: "stack-Xxa89J",
|
|
5
5
|
"add-row": "add-row-gEqjM_",
|
|
6
6
|
addRow: "add-row-gEqjM_",
|
|
7
|
+
"add-label": "add-label-jiOURu",
|
|
8
|
+
addLabel: "add-label-jiOURu",
|
|
7
9
|
"modal-head": "modal-head-BTxR9C",
|
|
8
10
|
modalHead: "modal-head-BTxR9C",
|
|
9
11
|
"modal-title": "modal-title-jwOfU_",
|
|
@@ -11,11 +11,32 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
:is(.add-row-gEqjM_, .byline-field-blocks-add-row) {
|
|
14
|
+
color: var(--muted);
|
|
14
15
|
align-items: center;
|
|
15
16
|
gap: var(--spacing-8);
|
|
17
|
+
font-size: .9rem;
|
|
16
18
|
display: flex;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
:is(.add-label-jiOURu, .byline-field-blocks-add-label) {
|
|
22
|
+
appearance: none;
|
|
23
|
+
font: inherit;
|
|
24
|
+
color: inherit;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
background: none;
|
|
27
|
+
border: none;
|
|
28
|
+
padding: 0;
|
|
29
|
+
transition: color .12s;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:is(.add-label-jiOURu:disabled, .byline-field-blocks-add-label:disabled) {
|
|
33
|
+
cursor: default;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
:is(.add-row-gEqjM_:hover .add-label-jiOURu:not(:disabled), .byline-field-blocks-add-row:hover .byline-field-blocks-add-label:not(:disabled)) {
|
|
37
|
+
color: var(--text);
|
|
38
|
+
}
|
|
39
|
+
|
|
19
40
|
:is(.modal-head-BTxR9C, .byline-field-blocks-modal-head) {
|
|
20
41
|
margin-bottom: var(--spacing-8);
|
|
21
42
|
padding-top: 1rem;
|
|
@@ -28,6 +49,22 @@
|
|
|
28
49
|
|
|
29
50
|
:is(.modal-content-S12b_T, .byline-field-blocks-card-cursor) {
|
|
30
51
|
cursor: pointer;
|
|
52
|
+
overscroll-behavior: contain;
|
|
53
|
+
min-height: 0;
|
|
54
|
+
max-height: 80dvh;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@media (min-width: 40rem) {
|
|
59
|
+
:is(.modal-content-S12b_T, .byline-field-blocks-card-cursor) {
|
|
60
|
+
max-height: 70dvh;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@media (min-width: 64rem) {
|
|
65
|
+
:is(.modal-content-S12b_T, .byline-field-blocks-card-cursor) {
|
|
66
|
+
max-height: 60dvh;
|
|
67
|
+
}
|
|
31
68
|
}
|
|
32
69
|
|
|
33
70
|
:is(.grid-gALL6j, .byline-field-blocks-grid) {
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
export interface CodeEditorProps {
|
|
9
|
+
id?: string;
|
|
10
|
+
/** Initial document. External updates sync only while the editor is unfocused. */
|
|
11
|
+
value: string;
|
|
12
|
+
/** Highlight language (canonical name or alias). Unknown → plain text. */
|
|
13
|
+
language?: string;
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
onChange?: (value: string) => void;
|
|
16
|
+
ariaInvalid?: boolean;
|
|
17
|
+
ariaDescribedBy?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const CodeEditor: ({ id, value, language, readOnly, onChange, ariaInvalid, ariaDescribedBy, }: CodeEditorProps) => import("react").JSX.Element;
|
|
20
|
+
export default CodeEditor;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { defaultKeymap, history as commands_history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
4
|
+
import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
|
|
5
|
+
import { Compartment, EditorState } from "@codemirror/state";
|
|
6
|
+
import { EditorView, keymap, lineNumbers } from "@codemirror/view";
|
|
7
|
+
import { tags } from "@lezer/highlight";
|
|
8
|
+
const LANGUAGE_LOADERS = {
|
|
9
|
+
javascript: ()=>import("@codemirror/lang-javascript").then((m)=>m.javascript()),
|
|
10
|
+
jsx: ()=>import("@codemirror/lang-javascript").then((m)=>m.javascript({
|
|
11
|
+
jsx: true
|
|
12
|
+
})),
|
|
13
|
+
typescript: ()=>import("@codemirror/lang-javascript").then((m)=>m.javascript({
|
|
14
|
+
typescript: true
|
|
15
|
+
})),
|
|
16
|
+
tsx: ()=>import("@codemirror/lang-javascript").then((m)=>m.javascript({
|
|
17
|
+
jsx: true,
|
|
18
|
+
typescript: true
|
|
19
|
+
})),
|
|
20
|
+
json: ()=>import("@codemirror/lang-json").then((m)=>m.json()),
|
|
21
|
+
html: ()=>import("@codemirror/lang-html").then((m)=>m.html()),
|
|
22
|
+
css: ()=>import("@codemirror/lang-css").then((m)=>m.css()),
|
|
23
|
+
markdown: ()=>import("@codemirror/lang-markdown").then((m)=>m.markdown()),
|
|
24
|
+
python: ()=>import("@codemirror/lang-python").then((m)=>m.python()),
|
|
25
|
+
sql: ()=>import("@codemirror/lang-sql").then((m)=>m.sql()),
|
|
26
|
+
yaml: ()=>import("@codemirror/lang-yaml").then((m)=>m.yaml())
|
|
27
|
+
};
|
|
28
|
+
const LANGUAGE_ALIASES = {
|
|
29
|
+
js: "javascript",
|
|
30
|
+
ts: "typescript",
|
|
31
|
+
md: 'markdown',
|
|
32
|
+
py: 'python',
|
|
33
|
+
yml: 'yaml'
|
|
34
|
+
};
|
|
35
|
+
const resolveLanguageLoader = (language)=>{
|
|
36
|
+
if (!language) return null;
|
|
37
|
+
const key = LANGUAGE_ALIASES[language] ?? language;
|
|
38
|
+
return LANGUAGE_LOADERS[key] ?? null;
|
|
39
|
+
};
|
|
40
|
+
const bylineCodeTheme = EditorView.theme({
|
|
41
|
+
'&': {
|
|
42
|
+
backgroundColor: 'var(--byline-code-bg)',
|
|
43
|
+
color: 'var(--byline-code-fg)',
|
|
44
|
+
fontSize: 'var(--byline-code-font-size, 13px)',
|
|
45
|
+
border: '1px solid var(--byline-code-border)',
|
|
46
|
+
borderRadius: 'var(--byline-code-radius, 4px)'
|
|
47
|
+
},
|
|
48
|
+
'&.cm-focused': {
|
|
49
|
+
outline: '2px solid var(--byline-code-focus-ring)',
|
|
50
|
+
outlineOffset: '-1px'
|
|
51
|
+
},
|
|
52
|
+
'.cm-content': {
|
|
53
|
+
fontFamily: 'var(--byline-code-font, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)',
|
|
54
|
+
caretColor: 'var(--byline-code-caret)',
|
|
55
|
+
minHeight: 'var(--byline-code-min-height, 6rem)'
|
|
56
|
+
},
|
|
57
|
+
'.cm-scroller': {
|
|
58
|
+
fontFamily: 'inherit',
|
|
59
|
+
maxHeight: 'var(--byline-code-max-height, 32rem)'
|
|
60
|
+
},
|
|
61
|
+
'.cm-gutters': {
|
|
62
|
+
backgroundColor: 'var(--byline-code-gutter-bg)',
|
|
63
|
+
color: 'var(--byline-code-gutter-fg)',
|
|
64
|
+
border: 'none',
|
|
65
|
+
borderRight: '1px solid var(--byline-code-border)'
|
|
66
|
+
},
|
|
67
|
+
'.cm-activeLine': {
|
|
68
|
+
backgroundColor: 'var(--byline-code-active-line)'
|
|
69
|
+
},
|
|
70
|
+
'.cm-activeLineGutter': {
|
|
71
|
+
backgroundColor: 'var(--byline-code-active-line)'
|
|
72
|
+
},
|
|
73
|
+
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection': {
|
|
74
|
+
backgroundColor: 'var(--byline-code-selection) !important'
|
|
75
|
+
},
|
|
76
|
+
'.cm-cursor': {
|
|
77
|
+
borderLeftColor: 'var(--byline-code-caret)'
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const bylineHighlightStyle = HighlightStyle.define([
|
|
81
|
+
{
|
|
82
|
+
tag: tags.keyword,
|
|
83
|
+
color: 'var(--byline-code-keyword)'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
tag: [
|
|
87
|
+
tags.string,
|
|
88
|
+
tags.special(tags.string)
|
|
89
|
+
],
|
|
90
|
+
color: 'var(--byline-code-string)'
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
tag: tags.comment,
|
|
94
|
+
color: 'var(--byline-code-comment)',
|
|
95
|
+
fontStyle: 'italic'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
tag: [
|
|
99
|
+
tags.number,
|
|
100
|
+
tags.bool,
|
|
101
|
+
tags["null"]
|
|
102
|
+
],
|
|
103
|
+
color: 'var(--byline-code-number)'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
tag: [
|
|
107
|
+
tags["function"](tags.variableName),
|
|
108
|
+
tags["function"](tags.propertyName)
|
|
109
|
+
],
|
|
110
|
+
color: 'var(--byline-code-function)'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
tag: [
|
|
114
|
+
tags.typeName,
|
|
115
|
+
tags.className,
|
|
116
|
+
tags.tagName
|
|
117
|
+
],
|
|
118
|
+
color: 'var(--byline-code-type)'
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
tag: [
|
|
122
|
+
tags.propertyName,
|
|
123
|
+
tags.attributeName
|
|
124
|
+
],
|
|
125
|
+
color: 'var(--byline-code-property)'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
tag: [
|
|
129
|
+
tags.operator,
|
|
130
|
+
tags.definitionKeyword
|
|
131
|
+
],
|
|
132
|
+
color: 'var(--byline-code-operator)'
|
|
133
|
+
}
|
|
134
|
+
]);
|
|
135
|
+
const CodeEditor = ({ id, value, language, readOnly, onChange, ariaInvalid, ariaDescribedBy })=>{
|
|
136
|
+
const containerRef = useRef(null);
|
|
137
|
+
const viewRef = useRef(null);
|
|
138
|
+
const onChangeRef = useRef(onChange);
|
|
139
|
+
onChangeRef.current = onChange;
|
|
140
|
+
const languageCompartment = useRef(new Compartment());
|
|
141
|
+
const readOnlyCompartment = useRef(new Compartment());
|
|
142
|
+
const ariaCompartment = useRef(new Compartment());
|
|
143
|
+
const initialValueRef = useRef(value);
|
|
144
|
+
useEffect(()=>{
|
|
145
|
+
if (null == containerRef.current) return;
|
|
146
|
+
const state = EditorState.create({
|
|
147
|
+
doc: initialValueRef.current,
|
|
148
|
+
extensions: [
|
|
149
|
+
lineNumbers(),
|
|
150
|
+
commands_history(),
|
|
151
|
+
bracketMatching(),
|
|
152
|
+
keymap.of([
|
|
153
|
+
...defaultKeymap,
|
|
154
|
+
...historyKeymap,
|
|
155
|
+
indentWithTab
|
|
156
|
+
]),
|
|
157
|
+
EditorView.lineWrapping,
|
|
158
|
+
bylineCodeTheme,
|
|
159
|
+
syntaxHighlighting(bylineHighlightStyle),
|
|
160
|
+
languageCompartment.current.of([]),
|
|
161
|
+
readOnlyCompartment.current.of([]),
|
|
162
|
+
ariaCompartment.current.of([]),
|
|
163
|
+
EditorView.updateListener.of((update)=>{
|
|
164
|
+
if (update.docChanged) onChangeRef.current?.(update.state.doc.toString());
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
});
|
|
168
|
+
const view = new EditorView({
|
|
169
|
+
state,
|
|
170
|
+
parent: containerRef.current
|
|
171
|
+
});
|
|
172
|
+
viewRef.current = view;
|
|
173
|
+
return ()=>{
|
|
174
|
+
view.destroy();
|
|
175
|
+
viewRef.current = null;
|
|
176
|
+
};
|
|
177
|
+
}, []);
|
|
178
|
+
useEffect(()=>{
|
|
179
|
+
const view = viewRef.current;
|
|
180
|
+
if (null == view || view.hasFocus) return;
|
|
181
|
+
const current = view.state.doc.toString();
|
|
182
|
+
if (current !== value) view.dispatch({
|
|
183
|
+
changes: {
|
|
184
|
+
from: 0,
|
|
185
|
+
to: current.length,
|
|
186
|
+
insert: value
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}, [
|
|
190
|
+
value
|
|
191
|
+
]);
|
|
192
|
+
useEffect(()=>{
|
|
193
|
+
let cancelled = false;
|
|
194
|
+
const view = viewRef.current;
|
|
195
|
+
if (null == view) return;
|
|
196
|
+
const loader = resolveLanguageLoader(language);
|
|
197
|
+
if (null == loader) return void view.dispatch({
|
|
198
|
+
effects: languageCompartment.current.reconfigure([])
|
|
199
|
+
});
|
|
200
|
+
loader().then((extension)=>{
|
|
201
|
+
if (!cancelled && null != viewRef.current) viewRef.current.dispatch({
|
|
202
|
+
effects: languageCompartment.current.reconfigure(extension)
|
|
203
|
+
});
|
|
204
|
+
}).catch(()=>{});
|
|
205
|
+
return ()=>{
|
|
206
|
+
cancelled = true;
|
|
207
|
+
};
|
|
208
|
+
}, [
|
|
209
|
+
language
|
|
210
|
+
]);
|
|
211
|
+
useEffect(()=>{
|
|
212
|
+
viewRef.current?.dispatch({
|
|
213
|
+
effects: readOnlyCompartment.current.reconfigure([
|
|
214
|
+
EditorState.readOnly.of(true === readOnly),
|
|
215
|
+
EditorView.editable.of(true !== readOnly)
|
|
216
|
+
])
|
|
217
|
+
});
|
|
218
|
+
}, [
|
|
219
|
+
readOnly
|
|
220
|
+
]);
|
|
221
|
+
useEffect(()=>{
|
|
222
|
+
const attributes = {};
|
|
223
|
+
if (ariaInvalid) attributes['aria-invalid'] = 'true';
|
|
224
|
+
if (ariaDescribedBy) attributes['aria-describedby'] = ariaDescribedBy;
|
|
225
|
+
viewRef.current?.dispatch({
|
|
226
|
+
effects: ariaCompartment.current.reconfigure(EditorView.contentAttributes.of(attributes))
|
|
227
|
+
});
|
|
228
|
+
}, [
|
|
229
|
+
ariaInvalid,
|
|
230
|
+
ariaDescribedBy
|
|
231
|
+
]);
|
|
232
|
+
return /*#__PURE__*/ jsx("div", {
|
|
233
|
+
ref: containerRef,
|
|
234
|
+
id: id,
|
|
235
|
+
className: "byline-code-editor"
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
const code_editor = CodeEditor;
|
|
239
|
+
export default code_editor;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
import React from 'react';
|
|
9
|
+
import type { FieldComponentSlots, CodeField as FieldType } from '@byline/core';
|
|
10
|
+
export declare const CodeField: ({ field, value, defaultValue, onChange, id, path, locale, components, }: {
|
|
11
|
+
field: FieldType;
|
|
12
|
+
value?: string;
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
onChange?: (value: string) => void;
|
|
15
|
+
id?: string;
|
|
16
|
+
path?: string;
|
|
17
|
+
/** When provided, renders a LocaleBadge next to the field label. */
|
|
18
|
+
locale?: string;
|
|
19
|
+
/** Optional UI component slot overrides from the admin config. */
|
|
20
|
+
components?: FieldComponentSlots;
|
|
21
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import react, { Suspense, useCallback } from "react";
|
|
3
|
+
import { ErrorText, HelpText, Label } from "@byline/ui/react";
|
|
4
|
+
import classnames from "classnames";
|
|
5
|
+
import { useFieldError, useFieldValue } from "../../forms/form-context.js";
|
|
6
|
+
import { LocaleBadge } from "../locale-badge.js";
|
|
7
|
+
import code_field_module from "./code-field.module.js";
|
|
8
|
+
const CodeEditor = /*#__PURE__*/ react.lazy(()=>import("./code-editor.js"));
|
|
9
|
+
const siblingFieldPath = (fieldPath, siblingName)=>{
|
|
10
|
+
const lastDot = fieldPath.lastIndexOf('.');
|
|
11
|
+
return -1 === lastDot ? siblingName : `${fieldPath.slice(0, lastDot + 1)}${siblingName}`;
|
|
12
|
+
};
|
|
13
|
+
const CodeField = ({ field, value, defaultValue, onChange, id, path, locale, components })=>{
|
|
14
|
+
const fieldPath = path ?? field.name;
|
|
15
|
+
const fieldError = useFieldError(fieldPath);
|
|
16
|
+
const fieldValue = useFieldValue(fieldPath);
|
|
17
|
+
const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
|
|
18
|
+
const htmlId = id ?? fieldPath;
|
|
19
|
+
const siblingPath = field.languageField ? siblingFieldPath(fieldPath, field.languageField) : fieldPath;
|
|
20
|
+
const siblingLanguage = useFieldValue(siblingPath);
|
|
21
|
+
const effectiveLanguage = (field.languageField ? siblingLanguage : void 0) || field.language;
|
|
22
|
+
const handleChange = useCallback((value)=>{
|
|
23
|
+
if (onChange) onChange(value);
|
|
24
|
+
}, [
|
|
25
|
+
onChange
|
|
26
|
+
]);
|
|
27
|
+
const slots = components;
|
|
28
|
+
const CustomLabel = slots?.Label;
|
|
29
|
+
const CustomHelpText = slots?.HelpText;
|
|
30
|
+
const CustomField = slots?.Field;
|
|
31
|
+
const BeforeField = slots?.beforeField;
|
|
32
|
+
const AfterField = slots?.afterField;
|
|
33
|
+
const slotBaseProps = {
|
|
34
|
+
field: field,
|
|
35
|
+
path: fieldPath,
|
|
36
|
+
value: incomingValue,
|
|
37
|
+
error: fieldError,
|
|
38
|
+
id: htmlId
|
|
39
|
+
};
|
|
40
|
+
const showBadge = !!locale && !!field.label;
|
|
41
|
+
const hasCustomLabel = !!CustomLabel;
|
|
42
|
+
const labelRowClass = classnames('byline-field-code-label-row', code_field_module["label-row"]);
|
|
43
|
+
const renderLabel = ()=>{
|
|
44
|
+
if (hasCustomLabel) return /*#__PURE__*/ jsxs("div", {
|
|
45
|
+
className: labelRowClass,
|
|
46
|
+
children: [
|
|
47
|
+
/*#__PURE__*/ jsx(CustomLabel, {
|
|
48
|
+
...slotBaseProps,
|
|
49
|
+
label: field.label,
|
|
50
|
+
required: !field.optional
|
|
51
|
+
}),
|
|
52
|
+
showBadge && /*#__PURE__*/ jsx(LocaleBadge, {
|
|
53
|
+
locale: locale
|
|
54
|
+
})
|
|
55
|
+
]
|
|
56
|
+
});
|
|
57
|
+
if (field.label) return /*#__PURE__*/ jsxs("div", {
|
|
58
|
+
className: labelRowClass,
|
|
59
|
+
children: [
|
|
60
|
+
/*#__PURE__*/ jsx(Label, {
|
|
61
|
+
id: `${htmlId}-label`,
|
|
62
|
+
htmlFor: htmlId,
|
|
63
|
+
label: field.label,
|
|
64
|
+
required: !field.optional
|
|
65
|
+
}),
|
|
66
|
+
showBadge && /*#__PURE__*/ jsx(LocaleBadge, {
|
|
67
|
+
locale: locale
|
|
68
|
+
})
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
const renderInput = ()=>{
|
|
74
|
+
if (CustomField) return /*#__PURE__*/ jsx(CustomField, {
|
|
75
|
+
...slotBaseProps,
|
|
76
|
+
onChange: handleChange,
|
|
77
|
+
defaultValue: defaultValue,
|
|
78
|
+
placeholder: field.placeholder
|
|
79
|
+
});
|
|
80
|
+
return /*#__PURE__*/ jsx(Suspense, {
|
|
81
|
+
fallback: /*#__PURE__*/ jsx("textarea", {
|
|
82
|
+
className: classnames('byline-field-code-loading', code_field_module.loading),
|
|
83
|
+
readOnly: true,
|
|
84
|
+
value: incomingValue,
|
|
85
|
+
rows: 6,
|
|
86
|
+
"aria-label": field.label
|
|
87
|
+
}),
|
|
88
|
+
children: /*#__PURE__*/ jsx(CodeEditor, {
|
|
89
|
+
id: htmlId,
|
|
90
|
+
value: incomingValue,
|
|
91
|
+
language: effectiveLanguage,
|
|
92
|
+
onChange: handleChange,
|
|
93
|
+
readOnly: true === field.readOnly,
|
|
94
|
+
ariaInvalid: null != fieldError,
|
|
95
|
+
ariaDescribedBy: null != fieldError ? `error-for-${htmlId}` : field.helpText ? `help-for-${htmlId}` : void 0
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
100
|
+
className: `byline-field-code ${field.name}`,
|
|
101
|
+
children: [
|
|
102
|
+
renderLabel(),
|
|
103
|
+
BeforeField && /*#__PURE__*/ jsx(BeforeField, {
|
|
104
|
+
...slotBaseProps
|
|
105
|
+
}),
|
|
106
|
+
renderInput(),
|
|
107
|
+
AfterField && /*#__PURE__*/ jsx(AfterField, {
|
|
108
|
+
...slotBaseProps
|
|
109
|
+
}),
|
|
110
|
+
null != fieldError && /*#__PURE__*/ jsx(ErrorText, {
|
|
111
|
+
id: `error-for-${htmlId}`,
|
|
112
|
+
text: fieldError
|
|
113
|
+
}),
|
|
114
|
+
CustomHelpText ? /*#__PURE__*/ jsx(CustomHelpText, {
|
|
115
|
+
...slotBaseProps,
|
|
116
|
+
helpText: field.helpText
|
|
117
|
+
}) : null == fieldError && field.helpText && /*#__PURE__*/ jsx(HelpText, {
|
|
118
|
+
id: `help-for-${htmlId}`,
|
|
119
|
+
text: field.helpText
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
export { CodeField };
|