@byline/richtext-lexical 6.4.0 → 6.5.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/field/extensions/code-highlight/code-highlight-extension.js +2 -1
- package/dist/field/extensions/inline-image/inline-image-extension.js +4 -2
- package/dist/field/extensions/inline-image/inline-image-modal.css +67 -0
- package/dist/field/extensions/inline-image/inline-image-modal.js +173 -98
- package/dist/field/extensions/inline-image/inline-image-node-component.css +37 -0
- package/dist/field/extensions/inline-image/inline-image-node-component.d.ts +3 -1
- package/dist/field/extensions/inline-image/inline-image-node-component.js +39 -1
- package/dist/field/extensions/inline-image/inline-image-node.d.ts +13 -2
- package/dist/field/extensions/inline-image/inline-image-node.js +63 -13
- package/dist/field/extensions/inline-image/node-types.d.ts +19 -0
- package/dist/field/extensions/inline-image/populate.d.ts +22 -0
- package/dist/field/extensions/inline-image/populate.js +19 -1
- package/dist/field/extensions/inline-image/types.d.ts +7 -0
- package/dist/field/extensions/link/link-href.d.ts +30 -0
- package/dist/field/extensions/link/link-href.js +14 -0
- package/dist/field/extensions/link/link-modal.js +96 -288
- package/dist/field/extensions/link/link-target-fields.css +59 -0
- package/dist/field/extensions/link/link-target-fields.d.ts +38 -0
- package/dist/field/extensions/link/link-target-fields.js +227 -0
- package/dist/field/extensions/link/link-target-state.d.ts +54 -0
- package/dist/field/extensions/link/link-target-state.js +70 -0
- package/dist/field/extensions/link/populate.d.ts +15 -1
- package/dist/field/extensions/link/populate.js +55 -50
- package/dist/field/markdown/lexical-to-markdown.js +6 -1
- package/dist/field/plugins/toolbar-plugin/index.js +2 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +4 -2
- package/dist/validate/createEmptyEditorState.d.ts +2 -2
- package/dist/validate/hasText.d.ts +2 -2
- package/package.json +25 -24
- package/src/field/extensions/code-highlight/code-highlight-extension.ts +8 -1
- package/src/field/extensions/inline-image/inline-image-extension.tsx +5 -0
- package/src/field/extensions/inline-image/inline-image-modal.css +100 -0
- package/src/field/extensions/inline-image/inline-image-modal.tsx +178 -85
- package/src/field/extensions/inline-image/inline-image-node-component.css +44 -0
- package/src/field/extensions/inline-image/inline-image-node-component.tsx +52 -0
- package/src/field/extensions/inline-image/inline-image-node.test.tsx +331 -0
- package/src/field/extensions/inline-image/inline-image-node.tsx +108 -11
- package/src/field/extensions/inline-image/node-types.ts +19 -0
- package/src/field/extensions/inline-image/populate-link.test.node.ts +320 -0
- package/src/field/extensions/inline-image/populate.ts +44 -0
- package/src/field/extensions/inline-image/types.ts +7 -0
- package/src/field/extensions/link/link-href.test.node.ts +78 -0
- package/src/field/extensions/link/link-href.ts +52 -0
- package/src/field/extensions/link/link-modal.tsx +86 -325
- package/src/field/extensions/link/link-target-fields.css +81 -0
- package/src/field/extensions/link/link-target-fields.tsx +275 -0
- package/src/field/extensions/link/link-target-state.test.node.ts +197 -0
- package/src/field/extensions/link/link-target-state.ts +158 -0
- package/src/field/extensions/link/populate.ts +94 -67
- package/src/field/markdown/lexical-to-markdown.test.node.ts +68 -0
- package/src/field/markdown/lexical-to-markdown.ts +10 -1
- package/src/field/plugins/toolbar-plugin/index.tsx +7 -4
- package/src/server.ts +11 -5
- package/src/validate/createEmptyEditorState.ts +2 -7
- package/src/validate/hasText.test.ts +1 -2
- package/src/validate/hasText.ts +2 -9
|
@@ -1,311 +1,119 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Button, Checkbox, CloseIcon, IconButton, Input, Label, Modal, RadioGroup, RadioGroupItem, Select } from "@byline/ui/react";
|
|
4
|
+
import { getAdminConfig, isSingleton } from "@byline/core";
|
|
5
|
+
import { Button, CloseIcon, IconButton, Input, Modal } from "@byline/ui/react";
|
|
7
6
|
import { useModalFormState } from "../../shared/useModalFormState.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
url: '',
|
|
14
|
-
newTab: false,
|
|
15
|
-
targetCollection: linkable[0]?.path ?? null,
|
|
16
|
-
picked: null
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function fromLinkData(data, linkable) {
|
|
20
|
-
const base = emptyState(linkable);
|
|
21
|
-
if (!data) return base;
|
|
22
|
-
const fields = data.fields;
|
|
23
|
-
if (!fields) return base;
|
|
24
|
-
const url = 'internal' === fields.linkType ? '' : fields.url ?? '';
|
|
25
|
-
const isPlaceholderUrl = '' === url || 'https://' === url;
|
|
26
|
-
const wantsInternal = linkable.length > 0 && ('internal' === fields.linkType || isPlaceholderUrl);
|
|
27
|
-
const linkType = wantsInternal ? 'internal' : 'custom';
|
|
28
|
-
const picked = 'internal' === fields.linkType ? {
|
|
29
|
-
targetDocumentId: fields.targetDocumentId,
|
|
30
|
-
targetCollectionId: fields.targetCollectionId,
|
|
31
|
-
targetCollectionPath: fields.targetCollectionPath,
|
|
32
|
-
document: fields.document
|
|
33
|
-
} : null;
|
|
34
|
-
return {
|
|
35
|
-
text: data.text ?? '',
|
|
36
|
-
linkType,
|
|
37
|
-
url: isPlaceholderUrl ? '' : url,
|
|
38
|
-
newTab: fields.newTab ?? false,
|
|
39
|
-
targetCollection: picked?.targetCollectionPath ?? linkable[0]?.path ?? null,
|
|
40
|
-
picked
|
|
41
|
-
};
|
|
42
|
-
}
|
|
7
|
+
import { LinkTargetFields } from "./link-target-fields.js";
|
|
8
|
+
import { fromLinkAttributes, toLinkAttributes, validateLinkTarget } from "./link-target-state.js";
|
|
9
|
+
const LINK_TARGET_OPTIONS = {
|
|
10
|
+
allowNone: false
|
|
11
|
+
};
|
|
43
12
|
const LinkModal = ({ isOpen = false, onSubmit, onClose, data: dataFromProps })=>{
|
|
44
13
|
const linkable = useMemo(()=>getAdminConfig().collections.filter((collection)=>!isSingleton(collection) && true === collection.linksInEditor), []);
|
|
45
|
-
const [pickerOpen, setPickerOpen] = useState(false);
|
|
46
14
|
const [urlError, setUrlError] = useState(null);
|
|
47
|
-
const [state, setState] = useModalFormState(isOpen, ()=>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
label: c.labels.singular,
|
|
52
|
-
value: c.path
|
|
53
|
-
})), [
|
|
54
|
-
linkable
|
|
55
|
-
]);
|
|
56
|
-
const pickedLabel = useMemo(()=>{
|
|
57
|
-
if ('internal' !== state.linkType || !state.picked) return null;
|
|
58
|
-
const title = state.picked.document?.title;
|
|
59
|
-
if ('string' == typeof title && title.length > 0) return title;
|
|
60
|
-
const pickedDefinition = getCollectionDefinition(state.picked.targetCollectionPath);
|
|
61
|
-
const pickedDef = null == pickedDefinition || isSingleton(pickedDefinition) ? null : pickedDefinition;
|
|
62
|
-
const short = state.picked.targetDocumentId.slice(0, 8);
|
|
63
|
-
return `${pickedDef?.labels.singular ?? state.picked.targetCollectionPath} · ${short}…`;
|
|
64
|
-
}, [
|
|
65
|
-
state.linkType,
|
|
66
|
-
state.picked
|
|
67
|
-
]);
|
|
68
|
-
const handlePickerSelect = (selection)=>{
|
|
69
|
-
setPickerOpen(false);
|
|
70
|
-
setState((s)=>{
|
|
71
|
-
const targetCollection = s.targetCollection;
|
|
72
|
-
const titleField = getCollectionDefinition(targetCollection)?.useAsTitle;
|
|
73
|
-
const title = titleField ? selection.record?.fields?.[titleField] : void 0;
|
|
74
|
-
const path = selection.record?.path;
|
|
75
|
-
const document = {};
|
|
76
|
-
if ('string' == typeof title && title.length > 0) document.title = title;
|
|
77
|
-
if ('string' == typeof path && path.length > 0) document.path = path;
|
|
78
|
-
return {
|
|
79
|
-
...s,
|
|
80
|
-
picked: {
|
|
81
|
-
targetDocumentId: selection.targetDocumentId,
|
|
82
|
-
targetCollectionId: selection.targetCollectionId,
|
|
83
|
-
targetCollectionPath: targetCollection,
|
|
84
|
-
document: Object.keys(document).length > 0 ? document : void 0
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
});
|
|
88
|
-
};
|
|
15
|
+
const [state, setState] = useModalFormState(isOpen, ()=>({
|
|
16
|
+
text: dataFromProps?.text ?? '',
|
|
17
|
+
target: fromLinkAttributes(dataFromProps?.fields, linkable, LINK_TARGET_OPTIONS)
|
|
18
|
+
}), ()=>setUrlError(null));
|
|
89
19
|
const handleSave = ()=>{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if ('internal' === state.linkType) {
|
|
95
|
-
if (!state.picked) return void setUrlError('Pick a target document');
|
|
96
|
-
}
|
|
97
|
-
const picked = state.picked;
|
|
98
|
-
const fields = 'custom' === state.linkType ? {
|
|
99
|
-
linkType: 'custom',
|
|
100
|
-
url: state.url,
|
|
101
|
-
newTab: state.newTab
|
|
102
|
-
} : {
|
|
103
|
-
linkType: 'internal',
|
|
104
|
-
newTab: state.newTab,
|
|
105
|
-
targetDocumentId: picked.targetDocumentId,
|
|
106
|
-
targetCollectionId: picked.targetCollectionId,
|
|
107
|
-
targetCollectionPath: picked.targetCollectionPath,
|
|
108
|
-
document: picked.document
|
|
109
|
-
};
|
|
20
|
+
const problem = validateLinkTarget(state.target);
|
|
21
|
+
if (null != problem) return void setUrlError(problem);
|
|
22
|
+
const fields = toLinkAttributes(state.target);
|
|
23
|
+
if (null == fields) return void setUrlError('Pick a target document');
|
|
110
24
|
onSubmit({
|
|
111
25
|
text: state.text.length > 0 ? state.text : null,
|
|
112
|
-
fields
|
|
26
|
+
fields: fields
|
|
113
27
|
});
|
|
114
28
|
onClose();
|
|
115
29
|
};
|
|
116
30
|
if (!isOpen) return null;
|
|
117
|
-
return /*#__PURE__*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
31
|
+
return /*#__PURE__*/ jsx(Modal, {
|
|
32
|
+
isOpen: isOpen,
|
|
33
|
+
onDismiss: onClose,
|
|
34
|
+
closeOnOverlayClick: false,
|
|
35
|
+
children: /*#__PURE__*/ jsxs(Modal.Container, {
|
|
36
|
+
style: {
|
|
37
|
+
maxWidth: '480px',
|
|
38
|
+
width: '100%'
|
|
39
|
+
},
|
|
40
|
+
children: [
|
|
41
|
+
/*#__PURE__*/ jsxs(Modal.Header, {
|
|
42
|
+
className: "flex items-center justify-between pt-4 mb-4",
|
|
128
43
|
children: [
|
|
129
|
-
/*#__PURE__*/
|
|
130
|
-
className: "
|
|
131
|
-
children:
|
|
132
|
-
/*#__PURE__*/ jsx("h3", {
|
|
133
|
-
className: "m-0 text-xl",
|
|
134
|
-
children: "Edit link"
|
|
135
|
-
}),
|
|
136
|
-
/*#__PURE__*/ jsx(IconButton, {
|
|
137
|
-
"aria-label": "Close",
|
|
138
|
-
size: "xs",
|
|
139
|
-
onClick: onClose,
|
|
140
|
-
children: /*#__PURE__*/ jsx(CloseIcon, {
|
|
141
|
-
width: "15px",
|
|
142
|
-
height: "15px",
|
|
143
|
-
svgClassName: "white-icon"
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
]
|
|
44
|
+
/*#__PURE__*/ jsx("h3", {
|
|
45
|
+
className: "m-0 text-xl",
|
|
46
|
+
children: "Edit link"
|
|
147
47
|
}),
|
|
148
|
-
/*#__PURE__*/ jsx(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
label: "Link text",
|
|
157
|
-
placeholder: "Visible link text",
|
|
158
|
-
value: state.text,
|
|
159
|
-
onChange: (e)=>setState((s)=>({
|
|
160
|
-
...s,
|
|
161
|
-
text: e.target.value
|
|
162
|
-
}))
|
|
163
|
-
}),
|
|
164
|
-
linkable.length > 0 && /*#__PURE__*/ jsxs(RadioGroup, {
|
|
165
|
-
id: "link-type",
|
|
166
|
-
name: "linkType",
|
|
167
|
-
"aria-label": "Link type",
|
|
168
|
-
direction: "row",
|
|
169
|
-
value: state.linkType,
|
|
170
|
-
onValueChange: (value)=>setState((s)=>({
|
|
171
|
-
...s,
|
|
172
|
-
linkType: 'internal' === value ? 'internal' : 'custom'
|
|
173
|
-
})),
|
|
174
|
-
children: [
|
|
175
|
-
/*#__PURE__*/ jsx(RadioGroupItem, {
|
|
176
|
-
id: "link-type-internal",
|
|
177
|
-
value: "internal",
|
|
178
|
-
label: "Document"
|
|
179
|
-
}),
|
|
180
|
-
/*#__PURE__*/ jsx(RadioGroupItem, {
|
|
181
|
-
id: "link-type-custom",
|
|
182
|
-
value: "custom",
|
|
183
|
-
label: "Custom URL"
|
|
184
|
-
})
|
|
185
|
-
]
|
|
186
|
-
}),
|
|
187
|
-
'custom' === state.linkType && /*#__PURE__*/ jsx(Input, {
|
|
188
|
-
id: "link-url",
|
|
189
|
-
name: "url",
|
|
190
|
-
label: "URL",
|
|
191
|
-
placeholder: "https://example.com or /path",
|
|
192
|
-
value: state.url,
|
|
193
|
-
errorText: urlError ?? void 0,
|
|
194
|
-
error: null != urlError,
|
|
195
|
-
onChange: (e)=>{
|
|
196
|
-
setUrlError(null);
|
|
197
|
-
setState((s)=>({
|
|
198
|
-
...s,
|
|
199
|
-
url: e.target.value
|
|
200
|
-
}));
|
|
201
|
-
}
|
|
202
|
-
}),
|
|
203
|
-
'internal' === state.linkType && /*#__PURE__*/ jsxs("div", {
|
|
204
|
-
className: "flex flex-col gap-3 mt-2",
|
|
205
|
-
children: [
|
|
206
|
-
linkable.length > 1 && /*#__PURE__*/ jsxs("div", {
|
|
207
|
-
children: [
|
|
208
|
-
/*#__PURE__*/ jsx(Label, {
|
|
209
|
-
id: "link-target-collection",
|
|
210
|
-
htmlFor: "link-target-collection",
|
|
211
|
-
className: "mb-1",
|
|
212
|
-
label: "Target collection"
|
|
213
|
-
}),
|
|
214
|
-
/*#__PURE__*/ jsx(Select, {
|
|
215
|
-
size: "sm",
|
|
216
|
-
items: collectionItems,
|
|
217
|
-
placeholder: "Target collection",
|
|
218
|
-
value: state.targetCollection ?? void 0,
|
|
219
|
-
onValueChange: (value)=>{
|
|
220
|
-
if (null == value) return;
|
|
221
|
-
setState((s)=>({
|
|
222
|
-
...s,
|
|
223
|
-
targetCollection: value
|
|
224
|
-
}));
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
]
|
|
228
|
-
}),
|
|
229
|
-
/*#__PURE__*/ jsxs("div", {
|
|
230
|
-
className: "border rounded p-3",
|
|
231
|
-
children: [
|
|
232
|
-
/*#__PURE__*/ jsx(Label, {
|
|
233
|
-
id: "link-target-document",
|
|
234
|
-
htmlFor: "link-target-document",
|
|
235
|
-
className: "mb-1",
|
|
236
|
-
label: "Target document"
|
|
237
|
-
}),
|
|
238
|
-
/*#__PURE__*/ jsxs("div", {
|
|
239
|
-
className: "flex items-center justify-between gap-2",
|
|
240
|
-
children: [
|
|
241
|
-
pickedLabel && /*#__PURE__*/ jsx("span", {
|
|
242
|
-
className: "text-sm text-accent-400 truncate",
|
|
243
|
-
children: pickedLabel
|
|
244
|
-
}),
|
|
245
|
-
/*#__PURE__*/ jsx(Button, {
|
|
246
|
-
size: "sm",
|
|
247
|
-
variant: "outlined",
|
|
248
|
-
intent: "noeffect",
|
|
249
|
-
type: "button",
|
|
250
|
-
onClick: ()=>setPickerOpen(true),
|
|
251
|
-
disabled: !state.targetCollection,
|
|
252
|
-
children: pickedLabel ? 'Change' : `Pick ${targetDef?.labels.singular ?? 'document'}…`
|
|
253
|
-
})
|
|
254
|
-
]
|
|
255
|
-
})
|
|
256
|
-
]
|
|
257
|
-
}),
|
|
258
|
-
urlError && 'internal' === state.linkType && /*#__PURE__*/ jsx("span", {
|
|
259
|
-
className: "text-xs text-red-400",
|
|
260
|
-
children: urlError
|
|
261
|
-
})
|
|
262
|
-
]
|
|
263
|
-
}),
|
|
264
|
-
/*#__PURE__*/ jsx(Checkbox, {
|
|
265
|
-
id: "link-new-tab",
|
|
266
|
-
name: "newTab",
|
|
267
|
-
label: "Open in new tab",
|
|
268
|
-
checked: state.newTab,
|
|
269
|
-
onCheckedChange: (checked)=>setState((s)=>({
|
|
270
|
-
...s,
|
|
271
|
-
newTab: true === checked
|
|
272
|
-
}))
|
|
273
|
-
})
|
|
274
|
-
]
|
|
48
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
49
|
+
"aria-label": "Close",
|
|
50
|
+
size: "xs",
|
|
51
|
+
onClick: onClose,
|
|
52
|
+
children: /*#__PURE__*/ jsx(CloseIcon, {
|
|
53
|
+
width: "15px",
|
|
54
|
+
height: "15px",
|
|
55
|
+
svgClassName: "white-icon"
|
|
275
56
|
})
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
}),
|
|
60
|
+
/*#__PURE__*/ jsx(Modal.Content, {
|
|
61
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
62
|
+
className: "flex flex-col gap-4",
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ jsx(Input, {
|
|
65
|
+
id: "link-text",
|
|
66
|
+
className: "mb-2",
|
|
67
|
+
name: "text",
|
|
68
|
+
label: "Link text",
|
|
69
|
+
placeholder: "Visible link text",
|
|
70
|
+
value: state.text,
|
|
71
|
+
onChange: (e)=>setState((s)=>({
|
|
72
|
+
...s,
|
|
73
|
+
text: e.target.value
|
|
74
|
+
}))
|
|
75
|
+
}),
|
|
76
|
+
/*#__PURE__*/ jsx(LinkTargetFields, {
|
|
77
|
+
idPrefix: "link",
|
|
78
|
+
state: state.target,
|
|
79
|
+
onChange: (target)=>{
|
|
80
|
+
setUrlError(null);
|
|
81
|
+
setState((s)=>({
|
|
82
|
+
...s,
|
|
83
|
+
target
|
|
84
|
+
}));
|
|
85
|
+
},
|
|
86
|
+
linkable: linkable,
|
|
87
|
+
allowNone: false,
|
|
88
|
+
error: urlError,
|
|
89
|
+
legend: "Link type"
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
})
|
|
93
|
+
}),
|
|
94
|
+
/*#__PURE__*/ jsxs(Modal.Actions, {
|
|
95
|
+
className: "flex gap-3",
|
|
96
|
+
children: [
|
|
97
|
+
/*#__PURE__*/ jsx(Button, {
|
|
98
|
+
size: "sm",
|
|
99
|
+
intent: "noeffect",
|
|
100
|
+
type: "button",
|
|
101
|
+
onClick: onClose,
|
|
102
|
+
className: "min-w-[70px]",
|
|
103
|
+
children: "Cancel"
|
|
276
104
|
}),
|
|
277
|
-
/*#__PURE__*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
onClick: onClose,
|
|
285
|
-
className: "min-w-[70px]",
|
|
286
|
-
children: "Cancel"
|
|
287
|
-
}),
|
|
288
|
-
/*#__PURE__*/ jsx(Button, {
|
|
289
|
-
size: "sm",
|
|
290
|
-
intent: "primary",
|
|
291
|
-
type: "button",
|
|
292
|
-
onClick: handleSave,
|
|
293
|
-
className: "min-w-[70px]",
|
|
294
|
-
children: "Save"
|
|
295
|
-
})
|
|
296
|
-
]
|
|
105
|
+
/*#__PURE__*/ jsx(Button, {
|
|
106
|
+
size: "sm",
|
|
107
|
+
intent: "primary",
|
|
108
|
+
type: "button",
|
|
109
|
+
onClick: handleSave,
|
|
110
|
+
className: "min-w-[70px]",
|
|
111
|
+
children: "Save"
|
|
297
112
|
})
|
|
298
113
|
]
|
|
299
114
|
})
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
targetCollectionPath: state.targetCollection,
|
|
303
|
-
targetDefinition: targetDef,
|
|
304
|
-
isOpen: pickerOpen,
|
|
305
|
-
onSelect: handlePickerSelect,
|
|
306
|
-
onDismiss: ()=>setPickerOpen(false)
|
|
307
|
-
})
|
|
308
|
-
]
|
|
115
|
+
]
|
|
116
|
+
})
|
|
309
117
|
});
|
|
310
118
|
};
|
|
311
119
|
export { LinkModal };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.byline-link-target-frame {
|
|
2
|
+
border: var(--border-width-thin, 1px) var(--border-style-solid, solid) var(--field-border);
|
|
3
|
+
border-radius: var(--border-radius-md, 6px);
|
|
4
|
+
margin-top: .25rem;
|
|
5
|
+
padding: .5rem;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.byline-link-target-tile {
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
align-items: flex-start;
|
|
11
|
+
gap: var(--spacing-8, .5rem);
|
|
12
|
+
padding: var(--spacing-8, .5rem);
|
|
13
|
+
border: var(--border-width-thin, 1px) var(--border-style-solid, solid) var(--primary-500);
|
|
14
|
+
border-radius: var(--border-radius-md, 6px);
|
|
15
|
+
font-size: var(--font-size-xs);
|
|
16
|
+
display: flex;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.byline-link-target-summary {
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: .125rem;
|
|
22
|
+
min-width: 0;
|
|
23
|
+
display: flex;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.byline-link-target-kind {
|
|
27
|
+
color: var(--gray-500);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.byline-link-target-value {
|
|
31
|
+
color: var(--gray-900);
|
|
32
|
+
text-overflow: ellipsis;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.byline-link-target-actions {
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: var(--spacing-4, .25rem);
|
|
40
|
+
flex-shrink: 0;
|
|
41
|
+
display: flex;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.byline-link-target-actions .byline-button {
|
|
45
|
+
color: var(--gray-900);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
:is([data-theme="dark"], .dark) .byline-link-target-frame {
|
|
49
|
+
border-color: var(--canvas-700, var(--field-border));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:is([data-theme="dark"], .dark) .byline-link-target-value {
|
|
53
|
+
color: var(--gray-100);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:is([data-theme="dark"], .dark) .byline-link-target-actions .byline-button {
|
|
57
|
+
color: var(--gray-200);
|
|
58
|
+
}
|
|
59
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
* The "where does this point?" sub-form, shared by the link modal and the
|
|
10
|
+
* inline-image modal.
|
|
11
|
+
*
|
|
12
|
+
* Purely presentational — every decision lives in `link-target-state.ts`,
|
|
13
|
+
* which is where the rules are tested. This file owns the markup, the
|
|
14
|
+
* picker's open/closed state, and nothing else.
|
|
15
|
+
*
|
|
16
|
+
* `idPrefix` is required rather than defaulted: two of these can be
|
|
17
|
+
* mounted in one document (an image modal over a link modal), and
|
|
18
|
+
* duplicate element ids break the `Label`/`htmlFor` association that
|
|
19
|
+
* screen readers depend on.
|
|
20
|
+
*/
|
|
21
|
+
import type * as React from 'react';
|
|
22
|
+
import type { MultiCollectionDefinition } from '@byline/core';
|
|
23
|
+
import type { LinkTargetState } from './link-target-state.js';
|
|
24
|
+
import './link-target-fields.css';
|
|
25
|
+
export interface LinkTargetFieldsProps {
|
|
26
|
+
idPrefix: string;
|
|
27
|
+
state: LinkTargetState;
|
|
28
|
+
onChange: (next: LinkTargetState) => void;
|
|
29
|
+
/** Collections with `linksInEditor: true`. Empty disables the picker. */
|
|
30
|
+
linkable: MultiCollectionDefinition[];
|
|
31
|
+
/** Offer a "no link" choice. See `LinkTargetOptions.allowNone`. */
|
|
32
|
+
allowNone: boolean;
|
|
33
|
+
/** Validation message from `validateLinkTarget`, if the form was submitted. */
|
|
34
|
+
error?: string | null;
|
|
35
|
+
/** Label above the kind radio. */
|
|
36
|
+
legend?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare const LinkTargetFields: React.FC<LinkTargetFieldsProps>;
|