@blocklet/editor 1.6.233 → 1.6.236
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/lib/config.js +3 -11
- package/lib/ext/AIPlugin/index.js +1 -1
- package/lib/ext/Aide/context.d.ts +2 -4
- package/lib/ext/Aide/context.js +9 -57
- package/lib/ext/Alert/AlertNode.js +1 -1
- package/lib/ext/ContentLocale.d.ts +7 -0
- package/lib/ext/ContentLocale.js +15 -0
- package/lib/ext/FilePlugin/FileNode.d.ts +44 -0
- package/lib/ext/FilePlugin/FileNode.js +194 -0
- package/lib/ext/FilePlugin/FilePlugin.d.ts +7 -0
- package/lib/ext/FilePlugin/FilePlugin.js +27 -0
- package/lib/ext/FilePlugin/index.d.ts +2 -0
- package/lib/ext/FilePlugin/index.js +2 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentNode.d.ts +31 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentNode.js +96 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentPlugin.d.ts +18 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentPlugin.js +38 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentRenderer.d.ts +9 -0
- package/lib/ext/PagesKitComponent/PagesKitComponentRenderer.js +75 -0
- package/lib/ext/PagesKitComponent/PropertyField.d.ts +13 -0
- package/lib/ext/PagesKitComponent/PropertyField.js +65 -0
- package/lib/ext/PagesKitComponent/utils.d.ts +25 -0
- package/lib/ext/PagesKitComponent/utils.js +16 -0
- package/lib/ext/PdfPlugin/PdfNode.d.ts +7 -7
- package/lib/ext/PdfPlugin/PdfNode.js +8 -6
- package/lib/ext/SubpageListingPlugin/SubpageListingComponent.js +1 -1
- package/lib/ext/VideoPlugin/VideoPlugin.d.ts +1 -1
- package/lib/ext/VideoPlugin/VideoPlugin.js +2 -3
- package/lib/ext/utils.d.ts +1 -0
- package/lib/ext/utils.js +3 -0
- package/lib/main/editor.js +3 -1
- package/lib/main/nodes/ImageComponent.js +3 -3
- package/lib/main/nodes/ImageNode.css +10 -3
- package/lib/main/nodes/PlaygroundNodes.js +4 -0
- package/lib/main/plugins/ComponentPickerPlugin/index.js +23 -5
- package/lib/main/plugins/ImagesPlugin/index.d.ts +1 -0
- package/lib/main/plugins/ImagesPlugin/index.js +3 -0
- package/lib/main/plugins/ToolbarPlugin/index.js +1 -5
- package/lib/main/themes/customTheme.js +1 -1
- package/lib/main/themes/defaultTheme.js +7 -5
- package/lib/main/ui/ImageResizer.js +2 -2
- package/package.json +6 -4
package/lib/config.js
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import memoize from 'lodash/memoize';
|
|
3
2
|
import { createContext, useContext, useMemo } from 'react';
|
|
4
3
|
const EditorConfigContext = createContext({});
|
|
5
4
|
export function EditorConfigProvider({ children, value }) {
|
|
5
|
+
const parentCtx = useContext(EditorConfigContext);
|
|
6
6
|
const _value = useMemo(() => {
|
|
7
|
-
const result = { ...value };
|
|
8
|
-
if (result.AI) {
|
|
9
|
-
result.AI.fetchTemplateTags = result.AI.fetchTemplateTags ? memoize(result.AI.fetchTemplateTags) : undefined;
|
|
10
|
-
// 注意 memoize 默认以首个参数作为 cache key, 两个属性值相同但引用不同的 object 会被认为是不同的 key
|
|
11
|
-
result.AI.fetchTemplates = result.AI.fetchTemplates
|
|
12
|
-
? memoize(result.AI.fetchTemplates, (arg) => JSON.stringify(arg))
|
|
13
|
-
: undefined;
|
|
14
|
-
result.AI.checkAvailable = memoize(result.AI.checkAvailable);
|
|
15
|
-
}
|
|
7
|
+
const result = { ...parentCtx, ...value };
|
|
16
8
|
return result;
|
|
17
|
-
}, [value]);
|
|
9
|
+
}, [value, parentCtx]);
|
|
18
10
|
return _jsx(EditorConfigContext.Provider, { value: _value, children: children });
|
|
19
11
|
}
|
|
20
12
|
export const useEditorConfig = () => useContext(EditorConfigContext);
|
|
@@ -189,7 +189,7 @@ export default function AidePluginWrapper() {
|
|
|
189
189
|
const _copy = (output) => {
|
|
190
190
|
copy(output);
|
|
191
191
|
};
|
|
192
|
-
return (_jsx(AideProvider, { checkAvailable: AI.checkAvailable,
|
|
192
|
+
return (_jsx(AideProvider, { checkAvailable: AI.checkAvailable, completions: AI.completions, replaceSelection: _replaceSelection, copy: _copy, insertBelow: _insertBelow, children: _jsx(AidePlugin, {}) }));
|
|
193
193
|
}
|
|
194
194
|
return null;
|
|
195
195
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import type { Completions,
|
|
2
|
+
import type { Completions, FetchTemplatesRequest, Mode, ParameterizedTemplate, ParameterizedTemplateCompletionsPayload, Status } from './types';
|
|
3
3
|
import { Template } from './types';
|
|
4
4
|
interface AideProviderProps {
|
|
5
5
|
children: ReactNode;
|
|
6
|
-
fetchTemplateTags?: FetchTemplateTags;
|
|
7
|
-
fetchTemplates?: FetchTemplates;
|
|
8
6
|
completions: Completions;
|
|
9
7
|
checkAvailable: () => Promise<boolean> | boolean;
|
|
10
8
|
replaceSelection?: (selection: any, output: string) => void;
|
|
@@ -46,5 +44,5 @@ interface AideContextType extends AideContextState, Pick<AideProviderProps, 'rep
|
|
|
46
44
|
}
|
|
47
45
|
export declare const AideContext: import("react").Context<AideContextType>;
|
|
48
46
|
export declare const useAideContext: () => AideContextType;
|
|
49
|
-
export declare function AideProvider({ checkAvailable,
|
|
47
|
+
export declare function AideProvider({ checkAvailable, completions, replaceSelection, insertBelow, copy, children, }: AideProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
50
48
|
export {};
|
package/lib/ext/Aide/context.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useCallback, useContext,
|
|
3
|
-
import { useGetState } from 'ahooks';
|
|
2
|
+
import { createContext, useCallback, useContext, useMemo, useRef } from 'react';
|
|
3
|
+
import { useGetState, useRequest } from 'ahooks';
|
|
4
4
|
import builtinTemplates from './builtin-templates';
|
|
5
5
|
import { Template } from './types';
|
|
6
6
|
export const AideContext = createContext({});
|
|
7
7
|
export const useAideContext = () => useContext(AideContext);
|
|
8
|
-
export function AideProvider({ checkAvailable,
|
|
8
|
+
export function AideProvider({ checkAvailable, completions, replaceSelection, insertBelow, copy, children, }) {
|
|
9
9
|
const [state, setState, getState] = useGetState({
|
|
10
10
|
available: false,
|
|
11
11
|
templates: builtinTemplates,
|
|
@@ -39,60 +39,12 @@ export function AideProvider({ checkAvailable, fetchTemplateTags, fetchTemplates
|
|
|
39
39
|
...params,
|
|
40
40
|
}));
|
|
41
41
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (result.length > 0) {
|
|
49
|
-
const hasMore = result.length === limit + 1;
|
|
50
|
-
const templates = hasMore ? result.slice(0, -1) : result;
|
|
51
|
-
setState((prev) => ({
|
|
52
|
-
...prev,
|
|
53
|
-
externalTemplates: {
|
|
54
|
-
...prev.externalTemplates,
|
|
55
|
-
templates: offset === 0 ? templates : [...prev.externalTemplates.templates, ...templates],
|
|
56
|
-
hasMore,
|
|
57
|
-
tag,
|
|
58
|
-
loading: false,
|
|
59
|
-
},
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
console.error(e);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
const init = async () => {
|
|
70
|
-
const available = await checkAvailable();
|
|
71
|
-
if (available) {
|
|
72
|
-
setState((prev) => ({ ...prev, available }));
|
|
73
|
-
// load templates & tags
|
|
74
|
-
const loadTemplateTags = async () => {
|
|
75
|
-
if (fetchTemplateTags) {
|
|
76
|
-
try {
|
|
77
|
-
const tags = await fetchTemplateTags();
|
|
78
|
-
setState((prev) => ({
|
|
79
|
-
...prev,
|
|
80
|
-
externalTemplates: {
|
|
81
|
-
...prev.externalTemplates,
|
|
82
|
-
tags,
|
|
83
|
-
},
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
console.error(e);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
await Promise.all([loadTemplateTags(), loadMoreTemplates()]);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
init();
|
|
95
|
-
}, []);
|
|
42
|
+
// @deprecated
|
|
43
|
+
const loadMoreTemplates = async () => { };
|
|
44
|
+
useRequest(async () => {
|
|
45
|
+
const available = await checkAvailable();
|
|
46
|
+
setState((prev) => ({ ...prev, available }));
|
|
47
|
+
}, { cacheKey: 'editor-aide-available' });
|
|
96
48
|
const activateAI = useCallback(
|
|
97
49
|
// eslint-disable-next-line require-await
|
|
98
50
|
async ({ mode = 'normal', selectionText, selection }) => {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
interface ContextType {
|
|
3
|
+
locale: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const useContentLocale: () => string;
|
|
6
|
+
export declare const ContentLocaleProvider: ({ locale, children }: PropsWithChildren & ContextType) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
4
|
+
const ContentLocaleContext = createContext(null);
|
|
5
|
+
// 该 hook 关注 editor content 的 locale, 区别于 LocaleContext
|
|
6
|
+
// (编辑 post 翻译版本时, 会切换 ContentLocaleContext)
|
|
7
|
+
export const useContentLocale = () => {
|
|
8
|
+
const { locale: contextLocale } = useLocaleContext();
|
|
9
|
+
const ctx = useContext(ContentLocaleContext);
|
|
10
|
+
return ctx?.locale || contextLocale;
|
|
11
|
+
};
|
|
12
|
+
export const ContentLocaleProvider = ({ locale, children }) => {
|
|
13
|
+
const value = useMemo(() => ({ locale }), [locale]);
|
|
14
|
+
return _jsx(ContentLocaleContext.Provider, { value: value, children: children });
|
|
15
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from 'lexical';
|
|
3
|
+
import { DecoratorNode } from 'lexical';
|
|
4
|
+
export declare const getAbsoluteUrl: ({ fileUrl, name }: {
|
|
5
|
+
fileUrl: string;
|
|
6
|
+
name?: string | undefined;
|
|
7
|
+
}) => string;
|
|
8
|
+
export interface FilePayload {
|
|
9
|
+
key?: NodeKey;
|
|
10
|
+
src: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
mimetype?: string;
|
|
13
|
+
width?: number | string;
|
|
14
|
+
height?: number | string;
|
|
15
|
+
}
|
|
16
|
+
export type SerializedFileNode = Spread<{
|
|
17
|
+
height?: number | string;
|
|
18
|
+
src: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
mimetype?: string;
|
|
21
|
+
width?: number | string;
|
|
22
|
+
}, SerializedLexicalNode>;
|
|
23
|
+
export declare class FileNode extends DecoratorNode<JSX.Element> {
|
|
24
|
+
__src: string;
|
|
25
|
+
__name?: string;
|
|
26
|
+
__mimetype?: string;
|
|
27
|
+
__width: 'inherit' | number | string;
|
|
28
|
+
__height: 'inherit' | number | string;
|
|
29
|
+
static getType(): string;
|
|
30
|
+
static clone(node: FileNode): FileNode;
|
|
31
|
+
static importJSON(serializedNode: SerializedFileNode): FileNode;
|
|
32
|
+
exportDOM(): DOMExportOutput;
|
|
33
|
+
static importDOM(): DOMConversionMap | null;
|
|
34
|
+
constructor(src: string, name?: string, mimetype?: string, width?: 'inherit' | number | string, height?: 'inherit' | number | string, key?: NodeKey);
|
|
35
|
+
exportJSON(): SerializedFileNode;
|
|
36
|
+
setWidthAndHeight(width: 'inherit' | number, height: 'inherit' | number): void;
|
|
37
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
38
|
+
updateDOM(): false;
|
|
39
|
+
getSrc(): string;
|
|
40
|
+
setSrc(src: string): void;
|
|
41
|
+
decorate(): JSX.Element;
|
|
42
|
+
}
|
|
43
|
+
export declare function $createFileNode({ height, src, name, mimetype, width, key }: FilePayload): FileNode;
|
|
44
|
+
export declare function $isFileNode(node: LexicalNode | null | undefined): node is FileNode;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Tooltip } from '@mui/material';
|
|
3
|
+
import { useEffect, Suspense, useState } from 'react';
|
|
4
|
+
import { Link } from 'react-router-dom';
|
|
5
|
+
import joinURL from 'url-join';
|
|
6
|
+
import { Icon } from '@iconify/react';
|
|
7
|
+
import { $applyNodeReplacement, DecoratorNode } from 'lexical';
|
|
8
|
+
import mime from 'mime-types';
|
|
9
|
+
import TablerFileIcon from '@iconify/icons-tabler/file';
|
|
10
|
+
import { withQuery } from 'ufo';
|
|
11
|
+
const iconSize = 80;
|
|
12
|
+
const baseColor = 'primary.main';
|
|
13
|
+
const FileComponent = ({ src, mimetype, name }) => {
|
|
14
|
+
const [ext, setExt] = useState('file');
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
setExt(mime.extension(mimetype || '') || 'file');
|
|
17
|
+
}, [mimetype]);
|
|
18
|
+
return (_jsx(Tooltip, { title: name, placement: "top", children: _jsxs(Box, { component: Link, to: src, target: "_blank", sx: {
|
|
19
|
+
// remove a tag underline and color
|
|
20
|
+
color: baseColor,
|
|
21
|
+
textDecoration: 'none',
|
|
22
|
+
display: 'inline-flex',
|
|
23
|
+
flexDirection: 'column',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
gap: 1,
|
|
26
|
+
pt: 1.25,
|
|
27
|
+
mt: 1,
|
|
28
|
+
border: 2,
|
|
29
|
+
borderColor: baseColor,
|
|
30
|
+
borderRadius: 1,
|
|
31
|
+
transition: 'all 0.3s',
|
|
32
|
+
userSelect: 'none',
|
|
33
|
+
':hover': {
|
|
34
|
+
opacity: 0.75,
|
|
35
|
+
},
|
|
36
|
+
}, children: [_jsxs(Box, { sx: {
|
|
37
|
+
position: 'relative',
|
|
38
|
+
display: 'inline-flex',
|
|
39
|
+
}, children: [_jsx(Icon, { icon: TablerFileIcon, style: { fontSize: iconSize } }), _jsx(Box, { sx: {
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
left: 0,
|
|
42
|
+
right: 0,
|
|
43
|
+
top: '50%',
|
|
44
|
+
textAlign: 'center',
|
|
45
|
+
margin: 'auto',
|
|
46
|
+
fontWeight: 900,
|
|
47
|
+
maxWidth: iconSize * 0.45,
|
|
48
|
+
whiteSpace: 'nowrap',
|
|
49
|
+
overflow: 'hidden',
|
|
50
|
+
textOverflow: 'ellipsis',
|
|
51
|
+
fontSize: 14,
|
|
52
|
+
}, children: ext })] }), _jsx(Box, { sx: {
|
|
53
|
+
bgcolor: baseColor,
|
|
54
|
+
color: 'white',
|
|
55
|
+
px: 1,
|
|
56
|
+
lineHeight: 2,
|
|
57
|
+
fontSize: 13,
|
|
58
|
+
maxWidth: iconSize * 1.5,
|
|
59
|
+
whiteSpace: 'nowrap',
|
|
60
|
+
overflow: 'hidden',
|
|
61
|
+
display: 'inline-block',
|
|
62
|
+
'@keyframes scroll': {
|
|
63
|
+
'0%': {
|
|
64
|
+
ml: 0,
|
|
65
|
+
transform: 'translateX(0)',
|
|
66
|
+
},
|
|
67
|
+
'100%': {
|
|
68
|
+
ml: '100%',
|
|
69
|
+
transform: 'translateX(-100%)',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}, children: _jsx(Box, { sx: {
|
|
73
|
+
animation: 'scroll linear 5s alternate infinite',
|
|
74
|
+
float: 'left',
|
|
75
|
+
}, children: name }) })] }) }));
|
|
76
|
+
};
|
|
77
|
+
export const getAbsoluteUrl = ({ fileUrl, name }) => {
|
|
78
|
+
if (!fileUrl || typeof fileUrl !== 'string') {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
if (/^https?:\/\//.test(fileUrl)) {
|
|
82
|
+
return withQuery(fileUrl, {
|
|
83
|
+
filename: name,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
const { componentMountPoints = [] } = window.blocklet;
|
|
88
|
+
const mediaKit = componentMountPoints.find((x) => x.did === 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9');
|
|
89
|
+
const realUrl = mediaKit ? joinURL(window.location.origin, mediaKit.mountPoint || '/', '/uploads', fileUrl) : fileUrl;
|
|
90
|
+
return withQuery(realUrl, {
|
|
91
|
+
filename: name,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
function convertFileElement(domNode) {
|
|
95
|
+
if (domNode instanceof HTMLObjectElement) {
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
const { data, width, height, type, alt } = domNode;
|
|
98
|
+
const node = $createFileNode({ height, width, src: data, mimetype: type, name: alt });
|
|
99
|
+
return { node };
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
export class FileNode extends DecoratorNode {
|
|
104
|
+
__src;
|
|
105
|
+
__name;
|
|
106
|
+
__mimetype;
|
|
107
|
+
__width;
|
|
108
|
+
__height;
|
|
109
|
+
static getType() {
|
|
110
|
+
return 'file';
|
|
111
|
+
}
|
|
112
|
+
static clone(node) {
|
|
113
|
+
return new FileNode(node.__src, node.__name, node.__mimetype, node.__width, node.__height, node.__key);
|
|
114
|
+
}
|
|
115
|
+
static importJSON(serializedNode) {
|
|
116
|
+
const { height, width, src, mimetype, name } = serializedNode;
|
|
117
|
+
const node = $createFileNode({
|
|
118
|
+
height,
|
|
119
|
+
src,
|
|
120
|
+
width,
|
|
121
|
+
mimetype,
|
|
122
|
+
name,
|
|
123
|
+
});
|
|
124
|
+
return node;
|
|
125
|
+
}
|
|
126
|
+
exportDOM() {
|
|
127
|
+
const element = document.createElement('object');
|
|
128
|
+
element.setAttribute('data', this.__src);
|
|
129
|
+
element.setAttribute('width', this.__width.toString());
|
|
130
|
+
element.setAttribute('height', this.__height.toString());
|
|
131
|
+
element.setAttribute('type', this.__mimetype || 'image/png');
|
|
132
|
+
element.setAttribute('loading', 'lazy');
|
|
133
|
+
element.setAttribute('alt', this.__name || '');
|
|
134
|
+
return { element };
|
|
135
|
+
}
|
|
136
|
+
static importDOM() {
|
|
137
|
+
return {
|
|
138
|
+
object: (node) => ({
|
|
139
|
+
conversion: convertFileElement,
|
|
140
|
+
priority: 0,
|
|
141
|
+
}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
constructor(src, name, mimetype, width, height, key) {
|
|
145
|
+
super(key);
|
|
146
|
+
this.__src = src;
|
|
147
|
+
this.__name = name;
|
|
148
|
+
this.__mimetype = mimetype;
|
|
149
|
+
this.__width = width || 'inherit';
|
|
150
|
+
this.__height = height || 'inherit';
|
|
151
|
+
}
|
|
152
|
+
exportJSON() {
|
|
153
|
+
return {
|
|
154
|
+
height: this.__height === 'inherit' ? 0 : this.__height,
|
|
155
|
+
src: this.getSrc(),
|
|
156
|
+
type: 'file',
|
|
157
|
+
version: 1,
|
|
158
|
+
width: this.__width === 'inherit' ? 0 : this.__width,
|
|
159
|
+
name: this.__name,
|
|
160
|
+
mimetype: this.__mimetype,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
setWidthAndHeight(width, height) {
|
|
164
|
+
const writable = this.getWritable();
|
|
165
|
+
writable.__width = width;
|
|
166
|
+
writable.__height = height;
|
|
167
|
+
}
|
|
168
|
+
createDOM(config) {
|
|
169
|
+
const span = document.createElement('span');
|
|
170
|
+
return span;
|
|
171
|
+
}
|
|
172
|
+
updateDOM() {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
getSrc() {
|
|
176
|
+
return this.__src;
|
|
177
|
+
}
|
|
178
|
+
setSrc(src) {
|
|
179
|
+
const writable = this.getWritable();
|
|
180
|
+
writable.__src = src;
|
|
181
|
+
}
|
|
182
|
+
decorate() {
|
|
183
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(FileComponent, { src: getAbsoluteUrl({
|
|
184
|
+
fileUrl: this.__src,
|
|
185
|
+
name: this.__name,
|
|
186
|
+
}), mimetype: this.__mimetype, name: this.__name }) }));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export function $createFileNode({ height, src, name, mimetype, width, key }) {
|
|
190
|
+
return $applyNodeReplacement(new FileNode(src, name, mimetype, width, height, key));
|
|
191
|
+
}
|
|
192
|
+
export function $isFileNode(node) {
|
|
193
|
+
return node instanceof FileNode;
|
|
194
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { LexicalCommand } from 'lexical';
|
|
3
|
+
import { FilePayload } from './FileNode';
|
|
4
|
+
export type InsertFilePayload = Readonly<FilePayload>;
|
|
5
|
+
export declare const INSERT_FILE_COMMAND: LexicalCommand<InsertFilePayload>;
|
|
6
|
+
export declare function FilePlugin(): JSX.Element | null;
|
|
7
|
+
export declare const isFile: (filename: string) => string | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
2
|
+
import { $wrapNodeInElement, mergeRegister } from '@lexical/utils';
|
|
3
|
+
import { $createParagraphNode, $insertNodes, $isRootOrShadowRoot, COMMAND_PRIORITY_EDITOR, createCommand, } from 'lexical';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { $createFileNode, FileNode } from './FileNode';
|
|
6
|
+
export const INSERT_FILE_COMMAND = createCommand('INSERT_FILE_COMMAND');
|
|
7
|
+
export function FilePlugin() {
|
|
8
|
+
const [editor] = useLexicalComposerContext();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!editor.hasNodes([FileNode])) {
|
|
11
|
+
throw new Error('FilePlugin: FileNode not registered on editor');
|
|
12
|
+
}
|
|
13
|
+
return mergeRegister(editor.registerCommand(INSERT_FILE_COMMAND, (payload) => {
|
|
14
|
+
const fileNode = $createFileNode(payload);
|
|
15
|
+
$insertNodes([fileNode]);
|
|
16
|
+
if ($isRootOrShadowRoot(fileNode.getParentOrThrow())) {
|
|
17
|
+
$wrapNodeInElement(fileNode, $createParagraphNode).selectEnd();
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
21
|
+
}, [editor]);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export const isFile = (filename) => {
|
|
25
|
+
const ext = filename.split('.').pop();
|
|
26
|
+
return ext;
|
|
27
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { DOMConversionMap, DOMExportOutput, EditorConfig, ElementFormatType, LexicalEditor, LexicalNode, NodeKey, Spread } from 'lexical';
|
|
3
|
+
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
|
|
4
|
+
import type { Properties } from './utils';
|
|
5
|
+
export interface ComponentData {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
properties?: Properties;
|
|
9
|
+
}
|
|
10
|
+
export type SerializedPagesKitComponentNode = Spread<{
|
|
11
|
+
data: ComponentData;
|
|
12
|
+
type: 'pages-kit-component';
|
|
13
|
+
version: 1;
|
|
14
|
+
}, SerializedDecoratorBlockNode>;
|
|
15
|
+
export declare class PagesKitComponentNode extends DecoratorBlockNode {
|
|
16
|
+
__data: ComponentData;
|
|
17
|
+
static getType(): string;
|
|
18
|
+
static clone(node: PagesKitComponentNode): PagesKitComponentNode;
|
|
19
|
+
static importJSON(serializedNode: SerializedPagesKitComponentNode): PagesKitComponentNode;
|
|
20
|
+
exportJSON(): SerializedPagesKitComponentNode;
|
|
21
|
+
constructor(data: ComponentData, format?: ElementFormatType, key?: NodeKey);
|
|
22
|
+
exportDOM(): DOMExportOutput;
|
|
23
|
+
static importDOM(): DOMConversionMap | null;
|
|
24
|
+
updateDOM(): false;
|
|
25
|
+
getId(): string;
|
|
26
|
+
setProperties(properties: any): void;
|
|
27
|
+
decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element;
|
|
28
|
+
isInline(): false;
|
|
29
|
+
}
|
|
30
|
+
export declare function $createPagesKitComponentNode(data: ComponentData): PagesKitComponentNode;
|
|
31
|
+
export declare function $isPagesKitComponentNode(node: PagesKitComponentNode | LexicalNode | null | undefined): node is PagesKitComponentNode;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents';
|
|
3
|
+
import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
|
|
4
|
+
import { PagesKitComponentRenderer } from './PagesKitComponentRenderer';
|
|
5
|
+
function PagesKitComponent({ className, format, nodeKey, data, onPropertiesChange }) {
|
|
6
|
+
return (_jsx(BlockWithAlignableContents, { className: className, format: format, nodeKey: nodeKey, children: _jsx(PagesKitComponentRenderer, { id: data.id, name: data.name, properties: data.properties, onPropertiesChange: onPropertiesChange }) }));
|
|
7
|
+
}
|
|
8
|
+
function convertPagesKitComponentElement(domNode) {
|
|
9
|
+
const pagesKitComponentStr = domNode.getAttribute('data-lexical-pages-kit-component');
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(pagesKitComponentStr);
|
|
12
|
+
if (parsed) {
|
|
13
|
+
const node = $createPagesKitComponentNode(parsed);
|
|
14
|
+
return { node };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
console.warn(`Failed to parse: ${pagesKitComponentStr}`, e);
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export class PagesKitComponentNode extends DecoratorBlockNode {
|
|
23
|
+
__data;
|
|
24
|
+
static getType() {
|
|
25
|
+
return 'pages-kit-component';
|
|
26
|
+
}
|
|
27
|
+
static clone(node) {
|
|
28
|
+
return new PagesKitComponentNode({ ...node.__data }, node.__format, node.__key);
|
|
29
|
+
}
|
|
30
|
+
static importJSON(serializedNode) {
|
|
31
|
+
const node = $createPagesKitComponentNode(serializedNode.data);
|
|
32
|
+
node.setFormat(serializedNode.format);
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
exportJSON() {
|
|
36
|
+
return {
|
|
37
|
+
...super.exportJSON(),
|
|
38
|
+
type: 'pages-kit-component',
|
|
39
|
+
version: 1,
|
|
40
|
+
data: this.__data,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
constructor(data, format, key) {
|
|
44
|
+
super(format, key);
|
|
45
|
+
this.__data = data;
|
|
46
|
+
}
|
|
47
|
+
exportDOM() {
|
|
48
|
+
const element = document.createElement('div');
|
|
49
|
+
element.setAttribute('data-lexical-pages-kit-component', JSON.stringify(this.__data));
|
|
50
|
+
return { element };
|
|
51
|
+
}
|
|
52
|
+
static importDOM() {
|
|
53
|
+
return {
|
|
54
|
+
div: (domNode) => {
|
|
55
|
+
if (!domNode.hasAttribute('data-lexical-pages-kit-component')) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
conversion: convertPagesKitComponentElement,
|
|
60
|
+
priority: 1,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
updateDOM() {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
getId() {
|
|
69
|
+
return this.__data.id;
|
|
70
|
+
}
|
|
71
|
+
setProperties(properties) {
|
|
72
|
+
const writable = this.getWritable();
|
|
73
|
+
writable.__data = { ...writable.__data, properties };
|
|
74
|
+
}
|
|
75
|
+
decorate(editor, config) {
|
|
76
|
+
const embedBlockTheme = config.theme.embedBlock || {};
|
|
77
|
+
const className = {
|
|
78
|
+
base: embedBlockTheme.base || '',
|
|
79
|
+
focus: embedBlockTheme.focus || '',
|
|
80
|
+
};
|
|
81
|
+
return (_jsx(PagesKitComponent, { className: className, format: this.__format, nodeKey: this.getKey(), data: this.__data, onPropertiesChange: (props) => {
|
|
82
|
+
editor.update(() => {
|
|
83
|
+
this.setProperties(props);
|
|
84
|
+
});
|
|
85
|
+
} }));
|
|
86
|
+
}
|
|
87
|
+
isInline() {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function $createPagesKitComponentNode(data) {
|
|
92
|
+
return new PagesKitComponentNode(data);
|
|
93
|
+
}
|
|
94
|
+
export function $isPagesKitComponentNode(node) {
|
|
95
|
+
return node instanceof PagesKitComponentNode;
|
|
96
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { LexicalCommand } from 'lexical';
|
|
3
|
+
type CommandPayload = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const INSERT_PAGES_KIT_COMPONENT_COMMAND: LexicalCommand<CommandPayload>;
|
|
8
|
+
export declare function PagesKitComponentPlugin(): JSX.Element | null;
|
|
9
|
+
export declare function usePagesKitComponents(): {
|
|
10
|
+
pagesKitComponents: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
properties?: {
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
} | undefined;
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
2
|
+
import { $insertNodeToNearestRoot } from '@lexical/utils';
|
|
3
|
+
import { COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { useRequest } from 'ahooks';
|
|
6
|
+
import { joinURL } from 'ufo';
|
|
7
|
+
import { $createPagesKitComponentNode, PagesKitComponentNode } from './PagesKitComponentNode';
|
|
8
|
+
import { getBlockletMountPointInfo, isBlockletRunning } from '../utils';
|
|
9
|
+
import { mode } from './utils';
|
|
10
|
+
export const INSERT_PAGES_KIT_COMPONENT_COMMAND = createCommand('INSERT_PAGES_KIT_COMPONENT_COMMAND');
|
|
11
|
+
export function PagesKitComponentPlugin() {
|
|
12
|
+
const [editor] = useLexicalComposerContext();
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!editor.hasNodes([PagesKitComponentNode])) {
|
|
15
|
+
throw new Error('PagesKitComponentPlugin: PagesKitComponentNode not registered on editor');
|
|
16
|
+
}
|
|
17
|
+
return editor.registerCommand(INSERT_PAGES_KIT_COMPONENT_COMMAND, (payload) => {
|
|
18
|
+
const node = $createPagesKitComponentNode(payload);
|
|
19
|
+
$insertNodeToNearestRoot(node);
|
|
20
|
+
return true;
|
|
21
|
+
}, COMMAND_PRIORITY_EDITOR);
|
|
22
|
+
}, [editor]);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
export function usePagesKitComponents() {
|
|
26
|
+
const { data: pagesKitComponents = [] } = useRequest(() => {
|
|
27
|
+
if (!isBlockletRunning('pages-kit')) {
|
|
28
|
+
return Promise.resolve([]);
|
|
29
|
+
}
|
|
30
|
+
return fetch(joinURL(getBlockletMountPointInfo('pages-kit').mountPoint || '/', `/api/components?mode=${mode}`))
|
|
31
|
+
.then((res) => res.json())
|
|
32
|
+
.then((res) => res?.components || [])
|
|
33
|
+
.catch((res) => []);
|
|
34
|
+
}, {
|
|
35
|
+
cacheKey: 'editor-pages-kit-components',
|
|
36
|
+
});
|
|
37
|
+
return { pagesKitComponents };
|
|
38
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Properties } from './utils';
|
|
2
|
+
type PagesKitComponentProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
properties?: Properties;
|
|
6
|
+
onPropertiesChange: (properties: Properties) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function PagesKitComponentRenderer({ id, name, properties, onPropertiesChange }: PagesKitComponentProps): import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
export {};
|