@blocklet/editor 1.6.234 → 1.6.237

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.
@@ -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,196 @@
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
+ textAlign: 'center',
59
+ maxWidth: iconSize * 1.5,
60
+ whiteSpace: 'nowrap',
61
+ overflow: 'hidden',
62
+ display: 'inline-block',
63
+ '@keyframes scroll': {
64
+ '0%': {
65
+ ml: 0,
66
+ transform: 'translateX(0)',
67
+ },
68
+ '100%': {
69
+ ml: '100%',
70
+ transform: 'translateX(-100%)',
71
+ },
72
+ },
73
+ }, children: _jsx(Box, { sx: {
74
+ animation: 'scroll linear 5s alternate infinite',
75
+ float: 'left',
76
+ minWidth: iconSize,
77
+ }, children: name }) })] }) }));
78
+ };
79
+ export const getAbsoluteUrl = ({ fileUrl, name }) => {
80
+ if (!fileUrl || typeof fileUrl !== 'string') {
81
+ return '';
82
+ }
83
+ if (/^https?:\/\//.test(fileUrl)) {
84
+ return withQuery(fileUrl, {
85
+ filename: name,
86
+ });
87
+ }
88
+ // @ts-ignore
89
+ const { componentMountPoints = [] } = window.blocklet;
90
+ const mediaKit = componentMountPoints.find((x) => x.did === 'z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9');
91
+ const realUrl = mediaKit ? joinURL(window.location.origin, mediaKit.mountPoint || '/', '/uploads', fileUrl) : fileUrl;
92
+ return withQuery(realUrl, {
93
+ filename: name,
94
+ });
95
+ };
96
+ function convertFileElement(domNode) {
97
+ if (domNode instanceof HTMLObjectElement) {
98
+ // @ts-ignore
99
+ const { data, width, height, type, alt } = domNode;
100
+ const node = $createFileNode({ height, width, src: data, mimetype: type, name: alt });
101
+ return { node };
102
+ }
103
+ return null;
104
+ }
105
+ export class FileNode extends DecoratorNode {
106
+ __src;
107
+ __name;
108
+ __mimetype;
109
+ __width;
110
+ __height;
111
+ static getType() {
112
+ return 'file';
113
+ }
114
+ static clone(node) {
115
+ return new FileNode(node.__src, node.__name, node.__mimetype, node.__width, node.__height, node.__key);
116
+ }
117
+ static importJSON(serializedNode) {
118
+ const { height, width, src, mimetype, name } = serializedNode;
119
+ const node = $createFileNode({
120
+ height,
121
+ src,
122
+ width,
123
+ mimetype,
124
+ name,
125
+ });
126
+ return node;
127
+ }
128
+ exportDOM() {
129
+ const element = document.createElement('object');
130
+ element.setAttribute('data', this.__src);
131
+ element.setAttribute('width', this.__width.toString());
132
+ element.setAttribute('height', this.__height.toString());
133
+ element.setAttribute('type', this.__mimetype || 'image/png');
134
+ element.setAttribute('loading', 'lazy');
135
+ element.setAttribute('alt', this.__name || '');
136
+ return { element };
137
+ }
138
+ static importDOM() {
139
+ return {
140
+ object: (node) => ({
141
+ conversion: convertFileElement,
142
+ priority: 0,
143
+ }),
144
+ };
145
+ }
146
+ constructor(src, name, mimetype, width, height, key) {
147
+ super(key);
148
+ this.__src = src;
149
+ this.__name = name;
150
+ this.__mimetype = mimetype;
151
+ this.__width = width || 'inherit';
152
+ this.__height = height || 'inherit';
153
+ }
154
+ exportJSON() {
155
+ return {
156
+ height: this.__height === 'inherit' ? 0 : this.__height,
157
+ src: this.getSrc(),
158
+ type: 'file',
159
+ version: 1,
160
+ width: this.__width === 'inherit' ? 0 : this.__width,
161
+ name: this.__name,
162
+ mimetype: this.__mimetype,
163
+ };
164
+ }
165
+ setWidthAndHeight(width, height) {
166
+ const writable = this.getWritable();
167
+ writable.__width = width;
168
+ writable.__height = height;
169
+ }
170
+ createDOM(config) {
171
+ const span = document.createElement('span');
172
+ return span;
173
+ }
174
+ updateDOM() {
175
+ return false;
176
+ }
177
+ getSrc() {
178
+ return this.__src;
179
+ }
180
+ setSrc(src) {
181
+ const writable = this.getWritable();
182
+ writable.__src = src;
183
+ }
184
+ decorate() {
185
+ return (_jsx(Suspense, { fallback: null, children: _jsx(FileComponent, { src: getAbsoluteUrl({
186
+ fileUrl: this.__src,
187
+ name: this.__name,
188
+ }), mimetype: this.__mimetype, name: this.__name }) }));
189
+ }
190
+ }
191
+ export function $createFileNode({ height, src, name, mimetype, width, key }) {
192
+ return $applyNodeReplacement(new FileNode(src, name, mimetype, width, height, key));
193
+ }
194
+ export function $isFileNode(node) {
195
+ return node instanceof FileNode;
196
+ }
@@ -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,2 @@
1
+ export * from './FilePlugin';
2
+ export { FileNode } from './FileNode';
@@ -0,0 +1,2 @@
1
+ export * from './FilePlugin';
2
+ export { FileNode } from './FileNode';
@@ -30,7 +30,7 @@ export function PagesKitComponentRenderer({ id, name, properties, onPropertiesCh
30
30
  };
31
31
  useEffect(() => {
32
32
  const load = async () => {
33
- const state = customComponentStates.getState();
33
+ const state = customComponentStates().getState();
34
34
  await state.loadComponents({
35
35
  mode,
36
36
  locale,
@@ -42,7 +42,7 @@ export function PagesKitComponentRenderer({ id, name, properties, onPropertiesCh
42
42
  load();
43
43
  }
44
44
  }, [id, name]);
45
- const component = customComponentStates.getState()?.state?.components?.[id]?.component;
45
+ const component = customComponentStates().getState()?.state?.components?.[id]?.component;
46
46
  if (loading || !component) {
47
47
  return null;
48
48
  }
@@ -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 HTMLImageElement) {
27
- const { src, width, height } = domNode;
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('img');
54
- element.setAttribute('src', this.__src);
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
- img: (node) => ({
63
+ object: (node) => ({
62
64
  conversion: convertPdfElement,
63
65
  priority: 0,
64
66
  }),
@@ -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: (filename: string) => boolean | "" | undefined;
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 = (filename) => {
25
- const ext = filename.split('.').pop();
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
  };
@@ -79,6 +79,7 @@ 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';
@@ -110,7 +111,7 @@ export default function Editor({ children, placeholder, onChange, autoFocus = tr
110
111
  console.warn('You need to configure uploader to use ImagesPlugin');
111
112
  }
112
113
  }, [hasNodes('image'), hasUploader]);
113
- 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, {}), 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] }));
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] }));
114
115
  }
115
116
  const EditorContent = styled.div `
116
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(true);
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
- font-size: 12px;
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: 10px;
35
- left: 10px;
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,6 +36,7 @@ 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';
40
41
  import { PagesKitComponentNode } from '../../ext/PagesKitComponent/PagesKitComponentNode';
41
42
  const PlaygroundNodes = [
@@ -76,6 +77,7 @@ const PlaygroundNodes = [
76
77
  GithubNode,
77
78
  AlertNode,
78
79
  PdfNode,
80
+ FileNode,
79
81
  SubpageListingNode,
80
82
  PagesKitComponentNode,
81
83
  ];
@@ -26,7 +26,7 @@ import { EmbedConfigs } from '../AutoEmbedPlugin';
26
26
  import { INSERT_COLLAPSIBLE_COMMAND } from '../CollapsiblePlugin';
27
27
  import { InsertEquationDialog } from '../EquationsPlugin';
28
28
  import { INSERT_EXCALIDRAW_COMMAND } from '../ExcalidrawPlugin';
29
- import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
29
+ import { INSERT_IMAGE_COMMAND, isImage } from '../ImagesPlugin';
30
30
  import { InsertTableDialog } from '../TablePlugin';
31
31
  import { useEditorConfig } from '../../../config';
32
32
  import createPortal from '../../../components/createPortal';
@@ -35,6 +35,7 @@ import { $insertAlert } from '../../../ext/Alert/AlertPlugin';
35
35
  import { INSERT_VIDEO_COMMAND, isVideo } from '../../../ext/VideoPlugin';
36
36
  import { INSERT_TEMPLATE_COMMAND } from '../../../ext/TemplatePlugin';
37
37
  import { INSERT_PDF_COMMAND, isPdf } from '../../../ext/PdfPlugin';
38
+ import { INSERT_FILE_COMMAND } from '../../../ext/FilePlugin';
38
39
  import { $insertSubpageListing } from '../../../ext/SubpageListingPlugin/SubpageListingPlugin';
39
40
  import { INSERT_PAGES_KIT_COMPONENT_COMMAND, usePagesKitComponents, } from '../../../ext/PagesKitComponent/PagesKitComponentPlugin';
40
41
  class ComponentPickerOption extends TypeaheadOption {
@@ -375,20 +376,27 @@ export const uploadFile = (editor) => {
375
376
  if (file.source !== 'function-upload-file') {
376
377
  const { data } = response;
377
378
  if (data?.filename) {
378
- if (isVideo(data.filename)) {
379
+ if (isVideo(data.filename) || isVideo(data.originalname)) {
379
380
  editor.dispatchCommand(INSERT_VIDEO_COMMAND, {
380
381
  src: `/${data.filename}`,
381
382
  });
382
383
  }
383
- else if (isPdf(data.filename)) {
384
+ else if (isPdf(data.filename) || isPdf(data.originalname)) {
384
385
  editor.dispatchCommand(INSERT_PDF_COMMAND, {
385
386
  src: `/${data.filename}`,
386
387
  });
387
388
  }
388
- else {
389
+ else if (isImage(data.filename) || isImage(data.originalname)) {
389
390
  editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
390
391
  src: `/${data.filename}`,
391
- altText: 'Image',
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,
392
400
  });
393
401
  }
394
402
  }
@@ -12,3 +12,4 @@ declare global {
12
12
  rangeParent?: Node;
13
13
  }
14
14
  }
15
+ export declare const isImage: (src: string) => boolean;
@@ -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
+ };
@@ -388,7 +388,7 @@ export default function ToolbarPlugin() {
388
388
  if (data?.filename) {
389
389
  editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
390
390
  src: `/${data?.filename}`,
391
- altText: 'Image',
391
+ altText: data?.originalname,
392
392
  });
393
393
  }
394
394
  }
@@ -222,7 +222,7 @@ const createCustomTheme = (theme) => {
222
222
  padding: 0;
223
223
  p {
224
224
  color: ${palette.text.secondary};
225
- font-size: 0.875rem;
225
+ font-size: 12px;
226
226
  }
227
227
  }
228
228
  }
@@ -535,12 +535,12 @@ const image = css `
535
535
  .image-caption-container {
536
536
  display: block;
537
537
  position: absolute;
538
- bottom: 4px;
538
+ bottom: 8px;
539
539
  left: 0;
540
540
  right: 0;
541
541
  padding: 0;
542
542
  margin: 0;
543
- border-top: 1px solid #fff;
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
- bottom: 20px;
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: 0 auto;
569
- border: 1px solid rgba(255, 255, 255, 0.3);
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: [!showCaption && captionsEnabled && (_jsx("button", { className: "image-caption-button", ref: buttonRef, onClick: () => {
139
+ return (_jsxs("div", { ref: controlWrapperRef, children: [captionsEnabled && (_jsx("button", { className: "image-caption-button", ref: buttonRef, onClick: () => {
140
140
  setShowCaption(!showCaption);
141
- }, children: "Add Caption" })), _jsx("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/editor",
3
- "version": "1.6.234",
3
+ "version": "1.6.237",
4
4
  "main": "lib/index.js",
5
5
  "scripts": {
6
6
  "dev": "npm run storybook",
@@ -37,12 +37,13 @@
37
37
  ]
38
38
  },
39
39
  "dependencies": {
40
- "@arcblock/ux": "^2.9.75",
40
+ "@arcblock/ux": "^2.9.77",
41
41
  "@blocklet/embed": "^0.1.11",
42
- "@blocklet/pages-kit": "^0.2.283",
43
- "@blocklet/pdf": "1.6.234",
42
+ "@blocklet/pages-kit": "^0.2.302",
43
+ "@blocklet/pdf": "1.6.237",
44
44
  "@excalidraw/excalidraw": "^0.14.2",
45
45
  "@iconify/iconify": "^3.0.1",
46
+ "@iconify/icons-tabler": "^1.2.95",
46
47
  "@lexical/clipboard": "0.13.1",
47
48
  "@lexical/code": "0.13.1",
48
49
  "@lexical/file": "0.13.1",
@@ -109,5 +110,5 @@
109
110
  "react": "*",
110
111
  "react-dom": "*"
111
112
  },
112
- "gitHead": "630c1a20241c7d3d65d3f90f78b7858d2198b25d"
113
+ "gitHead": "252ca705b171a1406071a46b016a0583799b63ee"
113
114
  }