@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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
+
import { Box, Button, Divider, IconButton, Popover, useMediaQuery } from '@mui/material';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
|
|
6
|
+
import { CustomComponentRenderer, customComponentStates } from '@blocklet/pages-kit/components';
|
|
7
|
+
import { isBlockletRunning } from '../utils';
|
|
8
|
+
import { mode, isValidPropertyType, getLocalizedValue, getPropertyLabel } from './utils';
|
|
9
|
+
import { PropertyField } from './PropertyField';
|
|
10
|
+
import { useContentLocale } from '../ContentLocale';
|
|
11
|
+
export function PagesKitComponentRenderer({ id, name, properties, onPropertiesChange }) {
|
|
12
|
+
const locale = useContentLocale();
|
|
13
|
+
const [editor] = useLexicalComposerContext();
|
|
14
|
+
const isEditable = editor.isEditable();
|
|
15
|
+
const [loading, setLoading] = useState(true);
|
|
16
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
17
|
+
const open = Boolean(anchorEl);
|
|
18
|
+
const upMd = useMediaQuery((theme) => theme.breakpoints.up('md'));
|
|
19
|
+
const [pendingProperties, setPendingProperties] = useState(properties || {});
|
|
20
|
+
const handleClick = (event) => {
|
|
21
|
+
setAnchorEl(event.currentTarget);
|
|
22
|
+
};
|
|
23
|
+
const handleCancel = () => {
|
|
24
|
+
setAnchorEl(null);
|
|
25
|
+
setPendingProperties(properties || {});
|
|
26
|
+
};
|
|
27
|
+
const handleSave = () => {
|
|
28
|
+
onPropertiesChange(pendingProperties);
|
|
29
|
+
setAnchorEl(null);
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const load = async () => {
|
|
33
|
+
const state = customComponentStates().getState();
|
|
34
|
+
await state.loadComponents({
|
|
35
|
+
mode,
|
|
36
|
+
locale,
|
|
37
|
+
instances: [{ id, component: { name } }],
|
|
38
|
+
});
|
|
39
|
+
setLoading(false);
|
|
40
|
+
};
|
|
41
|
+
if (isBlockletRunning('pages-kit')) {
|
|
42
|
+
load();
|
|
43
|
+
}
|
|
44
|
+
}, [id, name]);
|
|
45
|
+
const component = customComponentStates().getState()?.state?.components?.[id]?.component;
|
|
46
|
+
if (loading || !component) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
// 暂时仅支持基础类型的组件参数配置
|
|
50
|
+
const propDefinitions = Object.values(component.properties || {})
|
|
51
|
+
.map((x) => x.data)
|
|
52
|
+
.filter((x) => !!x.key && isValidPropertyType(x.type));
|
|
53
|
+
return (_jsxs(Box, { sx: { position: 'relative', ...(isEditable && { mt: 2 }) }, children: [_jsx(CustomComponentRenderer, { componentId: id, locale: locale, dev: { mode }, props: properties?.[locale] }), isEditable && !!propDefinitions.length && upMd && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
|
|
54
|
+
vertical: 'bottom',
|
|
55
|
+
horizontal: 'right',
|
|
56
|
+
}, transformOrigin: {
|
|
57
|
+
vertical: 'top',
|
|
58
|
+
horizontal: 'right',
|
|
59
|
+
}, children: _jsxs(Box, { sx: { width: 440, maxHeight: 500, p: 2 }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center' }, children: _jsxs(Box, { sx: {
|
|
60
|
+
display: 'flex',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
gap: 0.5,
|
|
63
|
+
color: 'text.secondary',
|
|
64
|
+
fontSize: 14,
|
|
65
|
+
fontWeight: 600,
|
|
66
|
+
}, children: [_jsx(Box, { component: "span", className: "iconify", "data-icon": "tabler:settings-2", sx: { fontSize: 20 } }), _jsx("span", { children: "Properties" })] }) }), _jsx(Divider, { sx: { mt: 0.75, mb: 2 } }), propDefinitions.map((x) => {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
const multiline = !!x.multiline;
|
|
69
|
+
return (_jsx(Box, { sx: { mt: 2 }, children: _jsx(PropertyField, { label: getPropertyLabel({ locale, locales: x.locales }), type: x.type, value: pendingProperties[locale]?.[x.key] ||
|
|
70
|
+
getLocalizedValue({ key: 'defaultValue', locale, data: x.locales }), onChange: (v) => setPendingProperties({
|
|
71
|
+
...pendingProperties,
|
|
72
|
+
[locale]: { ...pendingProperties[locale], [x.key]: v },
|
|
73
|
+
}), multiline: multiline }) }, x.key));
|
|
74
|
+
}), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end', gap: 1, py: 2 }, children: [_jsx(Button, { color: "inherit", onClick: handleCancel, children: "Cancel" }), _jsx(Button, { color: "primary", variant: "contained", onClick: handleSave, children: "Save" })] })] }) })] }))] }));
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type PropertyType } from './utils';
|
|
2
|
+
interface PropertyFieldProps {
|
|
3
|
+
type?: PropertyType;
|
|
4
|
+
multiline?: boolean;
|
|
5
|
+
label: string;
|
|
6
|
+
value: any;
|
|
7
|
+
onChange: (value: any) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function UploaderButton({ onChange }: {
|
|
10
|
+
onChange?: Function;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function PropertyField({ type, multiline, label, value, onChange, ...rest }: PropertyFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FormControlLabel, IconButton, InputAdornment, Switch, TextField } from '@mui/material';
|
|
3
|
+
import UploadIcon from '@mui/icons-material/Upload';
|
|
4
|
+
function getVideoSize(url) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const video = document.createElement('video');
|
|
7
|
+
video.src = url;
|
|
8
|
+
video.onloadedmetadata = () => {
|
|
9
|
+
const { videoWidth: naturalWidth, videoHeight: naturalHeight } = video;
|
|
10
|
+
resolve({ naturalWidth, naturalHeight });
|
|
11
|
+
};
|
|
12
|
+
video.onerror = (e) => reject(e);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function getImageSize(url) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const img = new Image();
|
|
18
|
+
img.src = url;
|
|
19
|
+
img.onload = () => {
|
|
20
|
+
const { naturalWidth, naturalHeight } = img;
|
|
21
|
+
resolve({ naturalWidth, naturalHeight });
|
|
22
|
+
};
|
|
23
|
+
img.onerror = (e) => reject(e);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function UploaderButton({ onChange }) {
|
|
27
|
+
const handleOpen = () => {
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
const uploader = uploaderRef?.current?.getUploader();
|
|
30
|
+
uploader?.open();
|
|
31
|
+
if (onChange) {
|
|
32
|
+
// rewrite default emitter
|
|
33
|
+
uploader.onceUploadSuccess((...args) => {
|
|
34
|
+
onChange(...args);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return (_jsx(IconButton, { size: "small", onClick: handleOpen, children: _jsx(UploadIcon, {}) }, "uploader-trigger"));
|
|
39
|
+
}
|
|
40
|
+
export function PropertyField({ type, multiline, label, value, onChange, ...rest }) {
|
|
41
|
+
if (type === 'boolean') {
|
|
42
|
+
return (_jsx(FormControlLabel, { sx: { display: 'inline-flex', m: 0 }, label: label, labelPlacement: "start", control: _jsx(Switch, { checked: value === true, onChange: (_, checked) => onChange?.(checked) }) }));
|
|
43
|
+
}
|
|
44
|
+
if (type === 'number') {
|
|
45
|
+
return (_jsx(TextField, { type: "number", size: "small", sx: { width: 1 }, label: label, value: value, onChange: (e) => onChange(e.target.value), ...rest }));
|
|
46
|
+
}
|
|
47
|
+
if (type === 'url') {
|
|
48
|
+
return (_jsx(TextField, { sx: { width: 1 }, size: "small", label: label, value: value.url, onChange: (e) => onChange(e.target.value), InputProps: {
|
|
49
|
+
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(UploaderButton, { onChange: async ({ response }) => {
|
|
50
|
+
const url = response?.data?.url || response?.data?.fileUrl;
|
|
51
|
+
let size;
|
|
52
|
+
if (url) {
|
|
53
|
+
size = await getImageSize(url)
|
|
54
|
+
.catch(() => getVideoSize(url))
|
|
55
|
+
.catch(() => undefined);
|
|
56
|
+
}
|
|
57
|
+
onChange?.({ url, width: size?.naturalWidth, height: size?.naturalHeight });
|
|
58
|
+
} }) })),
|
|
59
|
+
}, ...rest }));
|
|
60
|
+
}
|
|
61
|
+
return (_jsx(TextField, { sx: { width: 1 }, size: "small", label: label, value: value, onChange: (e) => onChange(e.target.value), ...((multiline || type === 'json') && {
|
|
62
|
+
multiline: true,
|
|
63
|
+
rows: 2,
|
|
64
|
+
}), ...rest }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const mode: string;
|
|
2
|
+
export declare const propertyTypes: readonly ["string", "number", "boolean", "json", "url"];
|
|
3
|
+
export type PropertyType = typeof propertyTypes[number];
|
|
4
|
+
export type Properties = {
|
|
5
|
+
[locale: string]: {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare const isValidPropertyType: (type: string | null | undefined) => boolean;
|
|
10
|
+
type Locales = {
|
|
11
|
+
[locale: string]: {
|
|
12
|
+
name?: string;
|
|
13
|
+
defaultValue?: any;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare const getLocalizedValue: ({ key, locale, data }: {
|
|
17
|
+
key: string;
|
|
18
|
+
locale: string;
|
|
19
|
+
data?: Properties | undefined;
|
|
20
|
+
}) => any;
|
|
21
|
+
export declare const getPropertyLabel: ({ locale, locales }: {
|
|
22
|
+
locale: string;
|
|
23
|
+
locales?: Locales | undefined;
|
|
24
|
+
}) => string;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const mode = process.env.NODE_ENV === 'development' ? 'draft' : 'production';
|
|
2
|
+
export const propertyTypes = ['string', 'number', 'boolean', 'json', 'url'];
|
|
3
|
+
export const isValidPropertyType = (type) => {
|
|
4
|
+
return !type || propertyTypes.includes(type);
|
|
5
|
+
};
|
|
6
|
+
// 依次尝试取值
|
|
7
|
+
// - `data[locale][key]`
|
|
8
|
+
// - `data.en[key]`
|
|
9
|
+
// - 遍历所有 locale 取第一个有值的 `data[locale][key]`
|
|
10
|
+
export const getLocalizedValue = ({ key, locale, data }) => {
|
|
11
|
+
data = data || {};
|
|
12
|
+
return data[locale]?.[key] || data.en?.[key] || Object.values(data).find((x) => x?.[key])?.[key] || '';
|
|
13
|
+
};
|
|
14
|
+
export const getPropertyLabel = ({ locale, locales }) => {
|
|
15
|
+
return getLocalizedValue({ key: 'name', locale, data: locales });
|
|
16
|
+
};
|
|
@@ -5,24 +5,24 @@ export declare const getAbsoluteUrl: (imageUrl: string) => any;
|
|
|
5
5
|
export interface PdfPayload {
|
|
6
6
|
key?: NodeKey;
|
|
7
7
|
src: string;
|
|
8
|
-
width?: number;
|
|
9
|
-
height?: number;
|
|
8
|
+
width?: number | string;
|
|
9
|
+
height?: number | string;
|
|
10
10
|
}
|
|
11
11
|
export type SerializedPdfNode = Spread<{
|
|
12
|
-
height?: number;
|
|
12
|
+
height?: number | string;
|
|
13
13
|
src: string;
|
|
14
|
-
width?: number;
|
|
14
|
+
width?: number | string;
|
|
15
15
|
}, SerializedLexicalNode>;
|
|
16
16
|
export declare class PdfNode extends DecoratorNode<JSX.Element> {
|
|
17
17
|
__src: string;
|
|
18
|
-
__width: 'inherit' | number;
|
|
19
|
-
__height: 'inherit' | number;
|
|
18
|
+
__width: 'inherit' | number | string;
|
|
19
|
+
__height: 'inherit' | number | string;
|
|
20
20
|
static getType(): string;
|
|
21
21
|
static clone(node: PdfNode): PdfNode;
|
|
22
22
|
static importJSON(serializedNode: SerializedPdfNode): PdfNode;
|
|
23
23
|
exportDOM(): DOMExportOutput;
|
|
24
24
|
static importDOM(): DOMConversionMap | null;
|
|
25
|
-
constructor(src: string, width?: 'inherit' | number, height?: 'inherit' | number, key?: NodeKey);
|
|
25
|
+
constructor(src: string, width?: 'inherit' | number | string, height?: 'inherit' | number | string, key?: NodeKey);
|
|
26
26
|
exportJSON(): SerializedPdfNode;
|
|
27
27
|
setWidthAndHeight(width: 'inherit' | number, height: 'inherit' | number): void;
|
|
28
28
|
createDOM(config: EditorConfig): HTMLElement;
|
|
@@ -23,9 +23,9 @@ export const getAbsoluteUrl = (imageUrl) => {
|
|
|
23
23
|
return discussKit ? joinURL(window.location.origin, discussKit.mountPoint || '/', '/uploads', imageUrl) : imageUrl;
|
|
24
24
|
};
|
|
25
25
|
function convertPdfElement(domNode) {
|
|
26
|
-
if (domNode instanceof
|
|
27
|
-
const {
|
|
28
|
-
const node = $createPdfNode({ height, src, width });
|
|
26
|
+
if (domNode instanceof HTMLObjectElement) {
|
|
27
|
+
const { data, width, height } = domNode;
|
|
28
|
+
const node = $createPdfNode({ height, src: data, width });
|
|
29
29
|
return { node };
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
@@ -50,15 +50,17 @@ export class PdfNode extends DecoratorNode {
|
|
|
50
50
|
return node;
|
|
51
51
|
}
|
|
52
52
|
exportDOM() {
|
|
53
|
-
const element = document.createElement('
|
|
54
|
-
element.setAttribute('
|
|
53
|
+
const element = document.createElement('object');
|
|
54
|
+
element.setAttribute('data', this.__src);
|
|
55
55
|
element.setAttribute('width', this.__width.toString());
|
|
56
56
|
element.setAttribute('height', this.__height.toString());
|
|
57
|
+
// set pdf content-type
|
|
58
|
+
element.setAttribute('type', 'application/pdf');
|
|
57
59
|
return { element };
|
|
58
60
|
}
|
|
59
61
|
static importDOM() {
|
|
60
62
|
return {
|
|
61
|
-
|
|
63
|
+
object: (node) => ({
|
|
62
64
|
conversion: convertPdfElement,
|
|
63
65
|
priority: 0,
|
|
64
66
|
}),
|
|
@@ -50,7 +50,7 @@ export default function SubpageListingComponent({ docPostId, mode, onModeChange,
|
|
|
50
50
|
if (loading) {
|
|
51
51
|
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', alignItems: 'center', height: 200 }, children: _jsx(CircularProgress, {}) }));
|
|
52
52
|
}
|
|
53
|
-
return (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', position: 'relative', mx: -1 }, children: [isEmpty && (_jsx(Box, { sx: { width: 1, px: 1 }, children: _jsx(Alert, { severity: "info", sx: {}, children: "No subpages" }) })), isEditable && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
|
|
53
|
+
return (_jsxs(Box, { sx: { display: 'flex', flexWrap: 'wrap', position: 'relative', mx: -1, ...(isEditable && { mt: 2 }) }, children: [isEmpty && (_jsx(Box, { sx: { width: 1, px: 1 }, children: _jsx(Alert, { severity: "info", sx: {}, children: "No subpages" }) })), isEditable && (_jsxs(_Fragment, { children: [_jsx(IconButton, { sx: { position: 'absolute', right: 0, top: -32 }, onClick: handleClick, children: _jsx(SettingsOutlinedIcon, {}) }), _jsx(Popover, { open: open, anchorEl: anchorEl, onClose: () => setAnchorEl(null), anchorOrigin: {
|
|
54
54
|
vertical: 'bottom',
|
|
55
55
|
horizontal: 'right',
|
|
56
56
|
}, transformOrigin: {
|
|
@@ -4,4 +4,4 @@ import { VideoPayload } from './VideoNode';
|
|
|
4
4
|
export type InsertVideoPayload = Readonly<VideoPayload>;
|
|
5
5
|
export declare const INSERT_VIDEO_COMMAND: LexicalCommand<InsertVideoPayload>;
|
|
6
6
|
export declare function VideoPlugin(): JSX.Element | null;
|
|
7
|
-
export declare const isVideo: (
|
|
7
|
+
export declare const isVideo: (src: string) => boolean;
|
|
@@ -21,7 +21,6 @@ export function VideoPlugin() {
|
|
|
21
21
|
}, [editor]);
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
|
-
export const isVideo = (
|
|
25
|
-
|
|
26
|
-
return ext && ['mp4', 'webm'].includes(ext);
|
|
24
|
+
export const isVideo = (src) => {
|
|
25
|
+
return /\.(mp4|avi|mov|wmv|flv|mkv|mpeg|mpg|webm|ogg|ogv|3gp)$/i.test(src);
|
|
27
26
|
};
|
package/lib/ext/utils.d.ts
CHANGED
|
@@ -17,3 +17,4 @@ export declare const sleep: (ms: number) => Promise<unknown>;
|
|
|
17
17
|
export declare const minDelay: <T>(promise: Promise<T>, ms: number) => Promise<T>;
|
|
18
18
|
export declare function exhaustiveGuard(_value: never): never;
|
|
19
19
|
export declare const isSameHost: (url: string | URL) => boolean;
|
|
20
|
+
export declare const isBlockletRunning: (name: string) => boolean;
|
package/lib/ext/utils.js
CHANGED
package/lib/main/editor.js
CHANGED
|
@@ -79,9 +79,11 @@ import { VideoPlugin } from '../ext/VideoPlugin';
|
|
|
79
79
|
import { TemplatePlugin } from '../ext/TemplatePlugin';
|
|
80
80
|
import { CustomOnChangePlugin } from '../ext/CustomOnChangePlugin';
|
|
81
81
|
import { PdfPlugin } from '../ext/PdfPlugin';
|
|
82
|
+
import { FilePlugin } from '../ext/FilePlugin';
|
|
82
83
|
import { BlurTextPlugin } from '../ext/BlurTextPlugin';
|
|
83
84
|
import { useResponsiveTable } from './hooks/useResponsiveTable';
|
|
84
85
|
import { useTranslationListener } from './hooks/useTranslationListener';
|
|
86
|
+
import { PagesKitComponentPlugin } from '../ext/PagesKitComponent/PagesKitComponentPlugin';
|
|
85
87
|
export default function Editor({ children, placeholder, onChange, autoFocus = true, showToolbar = true, editorRef, onReady, enableHeadingsIdPlugin, }) {
|
|
86
88
|
const [editor] = useLexicalComposerContext();
|
|
87
89
|
const [editable, setEditable] = useState(false);
|
|
@@ -109,7 +111,7 @@ export default function Editor({ children, placeholder, onChange, autoFocus = tr
|
|
|
109
111
|
console.warn('You need to configure uploader to use ImagesPlugin');
|
|
110
112
|
}
|
|
111
113
|
}, [hasNodes('image'), hasUploader]);
|
|
112
|
-
return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), hasNodes('keyword') && _jsx(KeywordsPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('equation') && _jsx(EquationsPlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
|
|
114
|
+
return (_jsxs(_Fragment, { children: [isRichText && editable && showToolbar && _jsx(ToolbarPlugin, {}), isMaxLength && _jsx(MaxLengthPlugin, { maxLength: 30 }), hasNodes('image') && hasUploader && _jsx(DragDropPaste, {}), autoFocus && _jsx(AutoFocusPlugin, { defaultSelection: "rootEnd" }), _jsx(ClearEditorPlugin, {}), !!editable && _jsx(ComponentPickerPlugin, {}), _jsx(EmojiPickerPlugin, {}), hasNodes('link') && _jsx(AutoEmbedPlugin, {}), !!editable && _jsx(MentionsPlugin, {}), hasNodes('emoji') && _jsx(EmojisPlugin, {}), hasNodes('hashtag') && _jsx(HashtagPlugin, {}), hasNodes('keyword') && _jsx(KeywordsPlugin, {}), _jsx(SpeechToTextPlugin, {}), hasNodes('autolink') && _jsx(AutoLinkPlugin, {}), isRichText ? (_jsxs(_Fragment, { children: [_jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(EditorContent, { ref: onRef, className: cx('be-content', editable && 'editable'), children: _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cx('be-editable', 'notranslate') }), placeholder: _jsx(Placeholder, { className: "be-placeholder", children: placeholder }), ErrorBoundary: LexicalErrorBoundary }) }), _jsx(MarkdownShortcutPlugin, {}), _jsx(TabIndentationPlugin, {}), hasNodes('code', 'code-highlight') && _jsx(CodeHighlightPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListPlugin, {}), hasNodes('list', 'listitem') && _jsx(CheckListPlugin, {}), hasNodes('list', 'listitem') && _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), hasNodes('table', 'tablerow', 'tablecell') && editable && (_jsx(TablePlugin, { hasCellMerge: true, hasCellBackgroundColor: true })), hasNodes('table', 'tablerow', 'tablecell') && _jsx(TableCellResizer, {}), hasNodes('image') && hasUploader && _jsx(ImagesPlugin, {}), hasNodes('video') && _jsx(VideoPlugin, {}), hasNodes('link') && _jsx(LinkPlugin, {}), hasNodes('tweet') && _jsx(TwitterPlugin, {}), hasNodes('youtube') && _jsx(YouTubePlugin, {}), hasNodes('figma') && _jsx(FigmaPlugin, {}), _jsx(BilibiliPlugin, {}), _jsx(BlockletEmbedPlugin, {}), _jsx(PostLinkEmbedPlugin, {}), _jsx(BookmarkPlugin, {}), _jsx(AidePlugin, {}), editable && _jsx(CustomOnChangePlugin, { placeholder: placeholder }), editable && _jsx(TemplatePlugin, {}), !editable && _jsx(BlurTextPlugin, {}), hasNodes('pdf') && _jsx(PdfPlugin, {}), hasNodes('file') && _jsx(FilePlugin, {}), hasNodes('link') && _jsx(ClickableLinkPlugin, {}), hasNodes('horizontalrule') && _jsx(HorizontalRulePlugin, {}), hasNodes('equation') && _jsx(EquationsPlugin, {}), hasNodes('excalidraw') && _jsx(ExcalidrawPlugin, {}), hasNodes('alert') && _jsx(AlertPlugin, {}), hasNodes('pages-kit-component') && _jsx(PagesKitComponentPlugin, {}), _jsx(TabFocusPlugin, {}), hasNodes('collapsible-container', 'collapsible-content', 'collapsible-title') && _jsx(CollapsiblePlugin, {}), onChange && _jsx(OnChangePlugin, { onChange: onChange }), floatingAnchorElem && editable && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), hasNodes('code') && _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem }), hasNodes('link') && _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem }), hasNodes('table') && _jsx(TableCellActionMenuPlugin, { anchorElem: floatingAnchorElem, cellMerge: true }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem })] })), enableHeadingsIdPlugin && _jsx(HeadingsIdPlugin, {})] })) : (_jsxs(_Fragment, { children: [_jsx(PlainTextPlugin, { contentEditable: _jsx(ContentEditable, {}), placeholder: _jsx(Placeholder, { children: "placeholder" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, { externalHistoryState: historyState })] })), (isCharLimit || isCharLimitUtf8) && (_jsx(CharacterLimitPlugin, { charset: isCharLimit ? 'UTF-16' : 'UTF-8', maxLength: 5 })), isAutocomplete && hasNodes('autocomplete') && _jsx(AutocompletePlugin, {}), _jsx("div", { children: showTableOfContents && _jsx(TableOfContentsPlugin, {}) }), _jsx(SelectBlockPlugin, {}), _jsx(RemoveListPlugin, {}), _jsx(MarkdownHeadTextPlugin, {}), _jsx(AiImagePlugin, {}), editorRef && _jsx(EditorRefPlugin, { editorRef: editorRef }), onReady && _jsx(EditorReadyPlugin, { onReady: onReady }), children] }));
|
|
113
115
|
}
|
|
114
116
|
const EditorContent = styled.div `
|
|
115
117
|
position: relative;
|
|
@@ -159,11 +159,11 @@ export default function ImageComponent({ file, src, altText, nodeKey, width, hei
|
|
|
159
159
|
return false;
|
|
160
160
|
}, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW));
|
|
161
161
|
}, [clearSelection, editor, isResizing, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
|
|
162
|
-
const setShowCaption = () => {
|
|
162
|
+
const setShowCaption = (value) => {
|
|
163
163
|
editor.update(() => {
|
|
164
164
|
const node = $getNodeByKey(nodeKey);
|
|
165
165
|
if ($isImageNode(node)) {
|
|
166
|
-
node.setShowCaption(
|
|
166
|
+
node.setShowCaption(value);
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
};
|
|
@@ -227,7 +227,7 @@ export default function ImageComponent({ file, src, altText, nodeKey, width, hei
|
|
|
227
227
|
}
|
|
228
228
|
}, [src, file, confirmText]);
|
|
229
229
|
const placeholder = objectUrl ? (_jsx(ImageContainer, { draggable: draggable, success: !!src, loading: loading, error: error, confirmText: confirmText, uploadImage: uploadImage, placeholder: objectUrl, placeholderProps: { style: { width, height, maxWidth } } })) : null;
|
|
230
|
-
return (_jsx(Suspense, { fallback: placeholder, children: _jsxs(_Fragment, { children: [src ? (_jsx(ImageContainer, { draggable: draggable, children: _jsx(LazyImage, { className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth }) })) : (placeholder), showCaption && (_jsx("div", { className: "image-caption-container", children: _jsxs(LexicalNestedComposer, { initialEditor: caption, children: [_jsx(AutoFocusPlugin, {}), _jsx(MentionsPlugin, {}), _jsx(LinkPlugin, {}), _jsx(EmojisPlugin, {}), _jsx(HashtagPlugin, {}), _jsx(KeywordsPlugin, {}), _jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "ImageNode__contentEditable" }), placeholder: _jsx(Placeholder, { className: "ImageNode__placeholder", children: "Enter a caption..." }), ErrorBoundary: LexicalErrorBoundary })] }) })), resizable && $isNodeSelection(selection) && isFocused && (_jsx(ImageResizer, { showCaption: showCaption, setShowCaption: setShowCaption, editor: editor, buttonRef: buttonRef, imageRef: imageRef,
|
|
230
|
+
return (_jsx(Suspense, { fallback: placeholder, children: _jsxs(_Fragment, { children: [src ? (_jsx(ImageContainer, { draggable: draggable, children: _jsx(LazyImage, { className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth }) })) : (placeholder), showCaption && (_jsx("div", { className: "image-caption-container", children: _jsxs(LexicalNestedComposer, { initialEditor: caption, children: [_jsx(AutoFocusPlugin, {}), _jsx(MentionsPlugin, {}), _jsx(LinkPlugin, {}), _jsx(EmojisPlugin, {}), _jsx(HashtagPlugin, {}), _jsx(KeywordsPlugin, {}), _jsx(HistoryPlugin, { externalHistoryState: historyState }), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "ImageNode__contentEditable" }), placeholder: editor.isEditable() ? (_jsx(Placeholder, { className: "ImageNode__placeholder", children: "Enter a caption..." })) : null, ErrorBoundary: LexicalErrorBoundary })] }) })), resizable && $isNodeSelection(selection) && isFocused && (_jsx(ImageResizer, { showCaption: showCaption, setShowCaption: setShowCaption, editor: editor, buttonRef: buttonRef, imageRef: imageRef,
|
|
231
231
|
// maxWidth={maxWidth}
|
|
232
232
|
onResizeStart: onResizeStart, onResizeEnd: onResizeEnd, captionsEnabled: captionsEnabled }))] }) }));
|
|
233
233
|
}
|
|
@@ -19,10 +19,13 @@
|
|
|
19
19
|
outline: 0px;
|
|
20
20
|
padding: 10px;
|
|
21
21
|
user-select: text;
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
width: calc(100% - 20px);
|
|
24
24
|
white-space: pre-wrap;
|
|
25
25
|
word-break: break-word;
|
|
26
|
+
p {
|
|
27
|
+
font-size: 12px;
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
.ImageNode__placeholder {
|
|
@@ -31,12 +34,16 @@
|
|
|
31
34
|
overflow: hidden;
|
|
32
35
|
position: absolute;
|
|
33
36
|
text-overflow: ellipsis;
|
|
34
|
-
top:
|
|
35
|
-
|
|
37
|
+
top: 0;
|
|
38
|
+
bottom: 0;
|
|
39
|
+
margin: auto;
|
|
40
|
+
left: 0;
|
|
36
41
|
user-select: none;
|
|
37
42
|
white-space: nowrap;
|
|
38
43
|
display: inline-block;
|
|
39
44
|
pointer-events: none;
|
|
45
|
+
padding: 10px;
|
|
46
|
+
line-height: inherit;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
.image-control-wrapper--resizing {
|
|
@@ -36,7 +36,9 @@ import { GithubNode } from '../../ext/nodes/GithubNode';
|
|
|
36
36
|
import { AlertNode } from '../../ext/Alert/AlertNode';
|
|
37
37
|
import { VideoNode } from '../../ext/VideoPlugin';
|
|
38
38
|
import { PdfNode } from '../../ext/PdfPlugin';
|
|
39
|
+
import { FileNode } from '../../ext/FilePlugin';
|
|
39
40
|
import { SubpageListingNode } from '../../ext/SubpageListingPlugin/SubpageListingNode';
|
|
41
|
+
import { PagesKitComponentNode } from '../../ext/PagesKitComponent/PagesKitComponentNode';
|
|
40
42
|
const PlaygroundNodes = [
|
|
41
43
|
HeadingNode,
|
|
42
44
|
ListNode,
|
|
@@ -75,6 +77,8 @@ const PlaygroundNodes = [
|
|
|
75
77
|
GithubNode,
|
|
76
78
|
AlertNode,
|
|
77
79
|
PdfNode,
|
|
80
|
+
FileNode,
|
|
78
81
|
SubpageListingNode,
|
|
82
|
+
PagesKitComponentNode,
|
|
79
83
|
];
|
|
80
84
|
export default PlaygroundNodes;
|
|
@@ -18,6 +18,7 @@ import { INSERT_TABLE_COMMAND } from '@lexical/table';
|
|
|
18
18
|
import { $createParagraphNode, $getSelection, $isRangeSelection, FORMAT_ELEMENT_COMMAND, createCommand, $insertNodes, $createTextNode, COMMAND_PRIORITY_EDITOR, } from 'lexical';
|
|
19
19
|
import { mergeRegister } from '@lexical/utils';
|
|
20
20
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
21
|
+
import { Box } from '@mui/material';
|
|
21
22
|
import { LexicalTypeaheadMenuPlugin, TypeaheadOption, useBasicTypeaheadTriggerMatch, } from '../LexicalTypeaheadMenuPlugin';
|
|
22
23
|
import useHasNodes from '../../hooks/useHasNodes';
|
|
23
24
|
import useModal from '../../hooks/useModal';
|
|
@@ -25,7 +26,7 @@ import { EmbedConfigs } from '../AutoEmbedPlugin';
|
|
|
25
26
|
import { INSERT_COLLAPSIBLE_COMMAND } from '../CollapsiblePlugin';
|
|
26
27
|
import { InsertEquationDialog } from '../EquationsPlugin';
|
|
27
28
|
import { INSERT_EXCALIDRAW_COMMAND } from '../ExcalidrawPlugin';
|
|
28
|
-
import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
|
|
29
|
+
import { INSERT_IMAGE_COMMAND, isImage } from '../ImagesPlugin';
|
|
29
30
|
import { InsertTableDialog } from '../TablePlugin';
|
|
30
31
|
import { useEditorConfig } from '../../../config';
|
|
31
32
|
import createPortal from '../../../components/createPortal';
|
|
@@ -34,7 +35,9 @@ import { $insertAlert } from '../../../ext/Alert/AlertPlugin';
|
|
|
34
35
|
import { INSERT_VIDEO_COMMAND, isVideo } from '../../../ext/VideoPlugin';
|
|
35
36
|
import { INSERT_TEMPLATE_COMMAND } from '../../../ext/TemplatePlugin';
|
|
36
37
|
import { INSERT_PDF_COMMAND, isPdf } from '../../../ext/PdfPlugin';
|
|
38
|
+
import { INSERT_FILE_COMMAND } from '../../../ext/FilePlugin';
|
|
37
39
|
import { $insertSubpageListing } from '../../../ext/SubpageListingPlugin/SubpageListingPlugin';
|
|
40
|
+
import { INSERT_PAGES_KIT_COMPONENT_COMMAND, usePagesKitComponents, } from '../../../ext/PagesKitComponent/PagesKitComponentPlugin';
|
|
38
41
|
class ComponentPickerOption extends TypeaheadOption {
|
|
39
42
|
// What shows up in the editor
|
|
40
43
|
title;
|
|
@@ -68,6 +71,7 @@ export default function ComponentPickerMenuPlugin() {
|
|
|
68
71
|
const [modal, showModal] = useModal();
|
|
69
72
|
const [queryString, setQueryString] = useState(null);
|
|
70
73
|
const config = useEditorConfig();
|
|
74
|
+
const { pagesKitComponents } = usePagesKitComponents();
|
|
71
75
|
const hasUploader = !!config.uploader;
|
|
72
76
|
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('/', {
|
|
73
77
|
minLength: 0,
|
|
@@ -290,6 +294,13 @@ export default function ComponentPickerMenuPlugin() {
|
|
|
290
294
|
keywords: ['subpage', 'doc', 'list'],
|
|
291
295
|
onSelect: () => $insertSubpageListing(editor, 'children'),
|
|
292
296
|
}),
|
|
297
|
+
...pagesKitComponents
|
|
298
|
+
.filter((x) => !!x.name)
|
|
299
|
+
.map((x) => new ComponentPickerOption(`${x.name}`, {
|
|
300
|
+
icon: (_jsx(Box, { component: "span", sx: { display: 'inline-flex', width: 20, px: 0.25, mr: '1px', fontSize: 13 }, title: "Pages kit custom component", children: "\uD83E\uDDE9" })),
|
|
301
|
+
keywords: [x.name],
|
|
302
|
+
onSelect: () => editor.dispatchCommand(INSERT_PAGES_KIT_COMPONENT_COMMAND, { id: x.id, name: x.name }),
|
|
303
|
+
})),
|
|
293
304
|
].filter(Boolean);
|
|
294
305
|
const dynamicOptions = getDynamicOptions();
|
|
295
306
|
return queryString
|
|
@@ -365,20 +376,27 @@ export const uploadFile = (editor) => {
|
|
|
365
376
|
if (file.source !== 'function-upload-file') {
|
|
366
377
|
const { data } = response;
|
|
367
378
|
if (data?.filename) {
|
|
368
|
-
if (isVideo(data.filename)) {
|
|
379
|
+
if (isVideo(data.filename) || isVideo(data.originalname)) {
|
|
369
380
|
editor.dispatchCommand(INSERT_VIDEO_COMMAND, {
|
|
370
381
|
src: `/${data.filename}`,
|
|
371
382
|
});
|
|
372
383
|
}
|
|
373
|
-
else if (isPdf(data.filename)) {
|
|
384
|
+
else if (isPdf(data.filename) || isPdf(data.originalname)) {
|
|
374
385
|
editor.dispatchCommand(INSERT_PDF_COMMAND, {
|
|
375
386
|
src: `/${data.filename}`,
|
|
376
387
|
});
|
|
377
388
|
}
|
|
378
|
-
else {
|
|
389
|
+
else if (isImage(data.filename) || isImage(data.originalname)) {
|
|
379
390
|
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
|
|
380
391
|
src: `/${data.filename}`,
|
|
381
|
-
altText:
|
|
392
|
+
altText: data.originalname,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
editor.dispatchCommand(INSERT_FILE_COMMAND, {
|
|
397
|
+
src: `/${data.filename}`,
|
|
398
|
+
name: data.originalname,
|
|
399
|
+
mimetype: data.mimetype,
|
|
382
400
|
});
|
|
383
401
|
}
|
|
384
402
|
}
|
|
@@ -31,3 +31,6 @@ export default function ImagesPlugin({ captionsEnabled }) {
|
|
|
31
31
|
const TRANSPARENT_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
|
32
32
|
const img = document.createElement('img');
|
|
33
33
|
img.src = TRANSPARENT_IMAGE;
|
|
34
|
+
export const isImage = (src) => {
|
|
35
|
+
return /\.(jpg|jpeg|png|gif|bmp|svg|webp|ico|tiff|tif|raw)$/i.test(src);
|
|
36
|
+
};
|
|
@@ -25,8 +25,6 @@ import { sanitizeUrl } from '../../utils/sanitizeUrl';
|
|
|
25
25
|
// import { InsertEquationDialog } from "../EquationsPlugin";
|
|
26
26
|
// import { INSERT_EXCALIDRAW_COMMAND } from "../ExcalidrawPlugin";
|
|
27
27
|
import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
|
|
28
|
-
import { AideToolbarButton } from '../../../ext/AIPlugin';
|
|
29
|
-
import { AITranslateToolbarButton } from '../../../ext/AITranslationPlugin';
|
|
30
28
|
import { uploadFile, INSERT_COMPONENT_COMMAND } from '../ComponentPickerPlugin';
|
|
31
29
|
// import { InsertNewTableDialog, InsertTableDialog } from "../TablePlugin";
|
|
32
30
|
const blockTypeToBlockName = {
|
|
@@ -390,7 +388,7 @@ export default function ToolbarPlugin() {
|
|
|
390
388
|
if (data?.filename) {
|
|
391
389
|
editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
|
|
392
390
|
src: `/${data?.filename}`,
|
|
393
|
-
altText:
|
|
391
|
+
altText: data?.originalname,
|
|
394
392
|
});
|
|
395
393
|
}
|
|
396
394
|
}
|
|
@@ -463,8 +461,6 @@ export default function ToolbarPlugin() {
|
|
|
463
461
|
if (menus.length === 0) {
|
|
464
462
|
return null;
|
|
465
463
|
}
|
|
466
|
-
menus.push(_jsx(AideToolbarButton, {}, "aide"));
|
|
467
|
-
menus.push(_jsx(AITranslateToolbarButton, {}, "ai-translate"));
|
|
468
464
|
return (_jsxs(Toolbar, { className: "toolbar", children: [menus, modal] }));
|
|
469
465
|
}
|
|
470
466
|
const Toolbar = styled.div `
|
|
@@ -535,12 +535,12 @@ const image = css `
|
|
|
535
535
|
.image-caption-container {
|
|
536
536
|
display: block;
|
|
537
537
|
position: absolute;
|
|
538
|
-
bottom:
|
|
538
|
+
bottom: 8px;
|
|
539
539
|
left: 0;
|
|
540
540
|
right: 0;
|
|
541
541
|
padding: 0;
|
|
542
542
|
margin: 0;
|
|
543
|
-
|
|
543
|
+
|
|
544
544
|
background-color: rgba(255, 255, 255, 0.9);
|
|
545
545
|
min-width: 100px;
|
|
546
546
|
color: #000;
|
|
@@ -560,13 +560,15 @@ const image = css `
|
|
|
560
560
|
.image-caption-button {
|
|
561
561
|
display: block;
|
|
562
562
|
position: absolute;
|
|
563
|
-
|
|
563
|
+
height: 40px;
|
|
564
|
+
bottom: 0;
|
|
565
|
+
top: 0;
|
|
564
566
|
left: 0;
|
|
565
567
|
right: 0;
|
|
566
568
|
width: 30%;
|
|
567
569
|
padding: 10px;
|
|
568
|
-
margin:
|
|
569
|
-
border:
|
|
570
|
+
margin: auto;
|
|
571
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
570
572
|
border-radius: 5px;
|
|
571
573
|
background-color: rgba(0, 0, 0, 0.5);
|
|
572
574
|
min-width: 100px;
|
|
@@ -136,9 +136,9 @@ export default function ImageResizer({ onResizeStart, onResizeEnd, buttonRef, im
|
|
|
136
136
|
document.removeEventListener('pointerup', handlePointerUp);
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
|
-
return (_jsxs("div", { ref: controlWrapperRef, children: [
|
|
139
|
+
return (_jsxs("div", { ref: controlWrapperRef, children: [captionsEnabled && (_jsx("button", { className: "image-caption-button", ref: buttonRef, onClick: () => {
|
|
140
140
|
setShowCaption(!showCaption);
|
|
141
|
-
}, children:
|
|
141
|
+
}, children: showCaption ? 'Remove Caption' : 'Add Caption' })), _jsx("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
|
|
142
142
|
handlePointerDown(event, Direction.north);
|
|
143
143
|
} }), _jsx("div", { className: "image-resizer image-resizer-ne", onPointerDown: (event) => {
|
|
144
144
|
handlePointerDown(event, Direction.north | Direction.east);
|