@blocklet/editor 1.6.188 → 1.6.191

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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { AI, UserService } from './types';
3
- export type ToolbarConfigItem = 'block' | 'align' | 'font-family' | 'font-size' | 'bold' | 'italic' | 'underline' | 'code' | 'link' | 'image' | 'color' | 'bgcolor' | 'strikethrough' | 'media' | 'alert';
3
+ export type ToolbarConfigItem = 'component' | 'block' | 'align' | 'font-family' | 'font-size' | 'bold' | 'italic' | 'underline' | 'code' | 'link' | 'image' | 'color' | 'bgcolor' | 'strikethrough' | 'media' | 'alert';
4
4
  export interface ToolbarConfig {
5
5
  items?: ToolbarConfigItem[];
6
6
  }
@@ -11,7 +11,7 @@ export declare const MenuWrapper: import("@emotion/styled").StyledComponent<{
11
11
  }, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
12
12
  export declare const GradientButton: import("@emotion/styled").StyledComponent<import("@mui/material").ButtonOwnProps & Omit<import("@mui/material").ButtonBaseOwnProps, "classes"> & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & {
13
13
  ref?: ((instance: HTMLButtonElement | null) => void) | import("react").RefObject<HTMLButtonElement> | null | undefined;
14
- }, "color" | "children" | "style" | "className" | "tabIndex" | "disabled" | "sx" | "href" | "size" | "action" | "classes" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple" | "variant" | "disableElevation" | "endIcon" | "fullWidth" | "startIcon"> & {
14
+ }, "color" | "children" | "style" | "className" | "tabIndex" | "disabled" | "sx" | "href" | "size" | "action" | "classes" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple" | "disableElevation" | "fullWidth" | "startIcon" | "endIcon" | "variant"> & {
15
15
  theme?: import("@emotion/react").Theme | undefined;
16
16
  }, {}, {}>;
17
17
  export declare function NestedMenuItem({ children, subMenu, ...rest }: MenuItemProps & {
@@ -711,8 +711,14 @@ i.prettier-error {
711
711
  margin-left: 5px;
712
712
  border-radius: 15px;
713
713
  color: #222;
714
- display: inline-block;
715
714
  cursor: pointer;
715
+ display: flex;
716
+ }
717
+
718
+ .action-button .MuiLoadingButton-loadingIndicator {
719
+ position: relative;
720
+ left: 0;
721
+ margin-right: 8px;
716
722
  }
717
723
 
718
724
  .action-button:hover {
@@ -724,8 +730,6 @@ i.prettier-error {
724
730
  animation: mic-pulsate-color 3s infinite;
725
731
  }
726
732
  button.action-button:disabled {
727
- opacity: 0.6;
728
- background: #eee;
729
733
  cursor: not-allowed;
730
734
  }
731
735
 
@@ -8,6 +8,7 @@ import ImageResizer from '../../ui/ImageResizer';
8
8
  import { $isExcalidrawNode } from '.';
9
9
  import ExcalidrawImage from './ExcalidrawImage';
10
10
  import ExcalidrawModal from './ExcalidrawModal';
11
+ import { covertImageWithMediaKitPath } from './utils';
11
12
  export default function ExcalidrawComponent({ nodeKey, data }) {
12
13
  const [editor] = useLexicalComposerContext();
13
14
  const [isModalOpen, setModalOpen] = useState(data === '[]' && editor.isEditable());
@@ -21,7 +22,9 @@ export default function ExcalidrawComponent({ nodeKey, data }) {
21
22
  // 兼容 excalidraw 旧版本数据 - 缺少 "elements" key, 并且是一个 array, 新版本数据是 object
22
23
  const parsed = useMemo(() => {
23
24
  try {
24
- return JSON.parse(data);
25
+ const tempData = JSON.parse(data);
26
+ covertImageWithMediaKitPath(tempData.elements, tempData.files);
27
+ return tempData;
25
28
  }
26
29
  catch (e) {
27
30
  console.error(e);
@@ -89,6 +92,8 @@ export default function ExcalidrawComponent({ nodeKey, data }) {
89
92
  const node = $getNodeByKey(nodeKey);
90
93
  if ($isExcalidrawNode(node)) {
91
94
  if (els.length > 0 || Object.keys(fls).length > 0) {
95
+ // covert image with mediaKit path
96
+ covertImageWithMediaKitPath(els, fls);
92
97
  node.setData(JSON.stringify({
93
98
  appState: aps,
94
99
  elements: els,
@@ -35,7 +35,7 @@
35
35
  }
36
36
  .ExcalidrawModal__row {
37
37
  position: relative;
38
- padding: 40px 5px 5px;
38
+ padding: 42px 5px 5px;
39
39
  width: 90vw;
40
40
  height: 90vh;
41
41
  border-radius: 8px;
@@ -46,7 +46,7 @@
46
46
  }
47
47
  .ExcalidrawModal__modal {
48
48
  position: relative;
49
- z-index: 10;
49
+ z-index: 99999;
50
50
  top: 50px;
51
51
  width: auto;
52
52
  left: 0;
@@ -56,6 +56,7 @@
56
56
  border-radius: 8px;
57
57
  background-color: #eee;
58
58
  }
59
+
59
60
  .ExcalidrawModal__discardModal {
60
61
  margin-top: 60px;
61
62
  text-align: center;
@@ -68,3 +69,7 @@
68
69
  width: 100vw;
69
70
  height: 100vh;
70
71
  }
72
+
73
+ body .excalidraw-modal-container {
74
+ z-index: 99999 !important;
75
+ }
@@ -1,4 +1,4 @@
1
- import { AppState, BinaryFiles } from '@excalidraw/excalidraw/types/types';
1
+ import { AppState, BinaryFiles, LibraryItems } from '@excalidraw/excalidraw/types/types';
2
2
  import { ReactPortal } from 'react';
3
3
  export type ExcalidrawElementFragment = {
4
4
  isDeleted?: boolean;
@@ -14,9 +14,13 @@ type Props = {
14
14
  */
15
15
  initialAppState: AppState;
16
16
  /**
17
- * The initial set of elements to draw into the scene
17
+ * The initial set of files to draw into the scene
18
18
  */
19
19
  initialFiles: BinaryFiles;
20
+ /**
21
+ * The initial set of library items to draw into the scene
22
+ */
23
+ initialLibraryItems?: LibraryItems;
20
24
  /**
21
25
  * Controls the visibility of the modal
22
26
  */
@@ -39,5 +43,5 @@ type Props = {
39
43
  * A component which renders a modal with Excalidraw (a painting app)
40
44
  * which can be used to export an editable image
41
45
  */
42
- export default function ExcalidrawModal({ closeOnClickOutside, onSave, initialElements, initialAppState, initialFiles, isShown, onDelete, onClose, }: Props): ReactPortal | null;
46
+ export default function ExcalidrawModal({ closeOnClickOutside, onSave, initialElements, initialAppState, initialFiles, initialLibraryItems, isShown, onDelete, onClose, }: Props): ReactPortal | null;
43
47
  export {};
@@ -6,29 +6,50 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  *
8
8
  */
9
- import { Excalidraw } from '@excalidraw/excalidraw';
9
+ import { Excalidraw, useHandleLibrary, serializeLibraryAsJSON } from '@excalidraw/excalidraw';
10
10
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
11
11
  import { createPortal } from 'react-dom';
12
+ import LoadingButton from '@mui/lab/LoadingButton';
12
13
  import Button from '../../ui/Button';
13
14
  import Modal from '../../ui/Modal';
14
15
  import { useBeforeUnload } from '../../hooks/useBeforeUnload';
16
+ import { uploadImageToMediaKit } from './utils';
17
+ const safeParseJSON = (json) => {
18
+ try {
19
+ return JSON.parse(json);
20
+ }
21
+ catch (error) {
22
+ return null;
23
+ }
24
+ };
25
+ const LIBRARY_KEY = 'excalidraw-library';
15
26
  /**
16
27
  * @explorer-desc
17
28
  * A component which renders a modal with Excalidraw (a painting app)
18
29
  * which can be used to export an editable image
19
30
  */
20
- export default function ExcalidrawModal({ closeOnClickOutside = false, onSave, initialElements, initialAppState, initialFiles, isShown = false, onDelete, onClose, }) {
31
+ export default function ExcalidrawModal({ closeOnClickOutside = false, onSave, initialElements, initialAppState, initialFiles, initialLibraryItems = safeParseJSON(localStorage.getItem(LIBRARY_KEY))?.libraryItems, isShown = false, onDelete, onClose, }) {
21
32
  const excaliDrawModelRef = useRef(null);
22
33
  const excaliDrawSceneRef = useRef(null);
23
34
  const [discardModalOpen, setDiscardModalOpen] = useState(false);
24
35
  const [elements, setElements] = useState(initialElements);
25
36
  const [files, setFiles] = useState(initialFiles);
37
+ const [loading, setLoading] = useState(false);
38
+ const [saveText, setSaveText] = useState('Save');
26
39
  const [fullScreen, setFullScreen] = useState(false);
27
40
  useEffect(() => {
41
+ // @ts-ignore set window.name to add library target
42
+ window.name = window.location.href;
28
43
  if (excaliDrawModelRef.current !== null) {
29
44
  excaliDrawModelRef.current.focus();
30
45
  }
31
46
  }, []);
47
+ useHandleLibrary({
48
+ excalidrawAPI: excaliDrawSceneRef.current,
49
+ getInitialLibraryItems: () => {
50
+ return initialLibraryItems;
51
+ },
52
+ });
32
53
  useBeforeUnload(isShown);
33
54
  useEffect(() => {
34
55
  let modalOverlayElement = null;
@@ -68,30 +89,44 @@ export default function ExcalidrawModal({ closeOnClickOutside = false, onSave, i
68
89
  }
69
90
  };
70
91
  }, [elements, files, onDelete]);
71
- const save = () => {
72
- if (elements.filter((el) => !el.isDeleted).length > 0) {
73
- const appState = excaliDrawSceneRef?.current?.getAppState();
74
- // We only need a subset of the state
75
- const partialState = appState
76
- ? {
77
- exportBackground: appState.exportBackground,
78
- exportScale: appState.exportScale,
79
- exportWithDarkMode: appState.theme === 'dark',
80
- isBindingEnabled: appState.isBindingEnabled,
81
- isLoading: appState.isLoading,
82
- name: appState.name,
83
- theme: appState.theme,
84
- viewBackgroundColor: appState.viewBackgroundColor,
85
- viewModeEnabled: appState.viewModeEnabled,
86
- zenModeEnabled: appState.zenModeEnabled,
87
- zoom: appState.zoom,
88
- }
89
- : {};
90
- onSave(elements, partialState, files);
92
+ const save = async () => {
93
+ setLoading(true);
94
+ setSaveText('Saving...');
95
+ try {
96
+ if (elements.filter((el) => !el.isDeleted).length > 0) {
97
+ const appState = excaliDrawSceneRef?.current?.getAppState();
98
+ // We only need a subset of the state
99
+ const partialState = appState
100
+ ? {
101
+ exportBackground: appState.exportBackground,
102
+ exportScale: appState.exportScale,
103
+ exportWithDarkMode: appState.theme === 'dark',
104
+ isBindingEnabled: appState.isBindingEnabled,
105
+ isLoading: appState.isLoading,
106
+ name: appState.name,
107
+ theme: appState.theme,
108
+ viewBackgroundColor: appState.viewBackgroundColor,
109
+ viewModeEnabled: appState.viewModeEnabled,
110
+ zenModeEnabled: appState.zenModeEnabled,
111
+ zoom: appState.zoom,
112
+ }
113
+ : {};
114
+ setSaveText('Uploading...');
115
+ await uploadImageToMediaKit(elements, files);
116
+ setSaveText('Saving...');
117
+ await onSave(elements, partialState, files);
118
+ }
119
+ else {
120
+ // delete node if the scene is clear
121
+ await onDelete();
122
+ }
91
123
  }
92
- else {
93
- // delete node if the scene is clear
94
- onDelete();
124
+ catch (error) {
125
+ console.error('Error saving Excalidraw', error);
126
+ }
127
+ finally {
128
+ setLoading(false);
129
+ setSaveText('Save');
95
130
  }
96
131
  };
97
132
  const discard = () => {
@@ -122,14 +157,18 @@ export default function ExcalidrawModal({ closeOnClickOutside = false, onSave, i
122
157
  setElements(els);
123
158
  setFiles(fls);
124
159
  };
160
+ const onLibraryChange = (items) => {
161
+ const serializeJSON = serializeLibraryAsJSON(items);
162
+ localStorage.setItem(LIBRARY_KEY, serializeJSON);
163
+ };
125
164
  // This is a hacky work-around for Excalidraw + Vite.
126
165
  // In DEV, Vite pulls this in fine, in prod it doesn't. It seems
127
166
  // like a module resolution issue with ESM vs CJS?
128
167
  // @ts-ignore
129
168
  const _Excalidraw = Excalidraw.$$typeof != null ? Excalidraw : Excalidraw.default;
130
- return createPortal(_jsx("div", { className: `ExcalidrawModal__overlay ${fullScreen ? 'ExcalidrawModal__fullscreen' : ''}`, role: "dialog", children: _jsx("div", { className: "ExcalidrawModal__modal", ref: excaliDrawModelRef, tabIndex: -1, children: _jsxs("div", { className: "ExcalidrawModal__row", children: [discardModalOpen && _jsx(ShowDiscardDialog, {}), _jsx(_Excalidraw, { onChange: onChange, ref: excaliDrawSceneRef, initialData: {
169
+ return createPortal(_jsx("div", { className: `ExcalidrawModal__overlay ${fullScreen ? 'ExcalidrawModal__fullscreen' : ''}`, role: "dialog", children: _jsx("div", { className: "ExcalidrawModal__modal", ref: excaliDrawModelRef, tabIndex: -1, children: _jsxs("div", { className: "ExcalidrawModal__row", children: [discardModalOpen && _jsx(ShowDiscardDialog, {}), _jsx(_Excalidraw, { onChange: onChange, onLibraryChange: onLibraryChange, ref: excaliDrawSceneRef, initialData: {
131
170
  appState: initialAppState || { isLoading: false },
132
171
  elements: initialElements,
133
172
  files: initialFiles,
134
- } }), _jsxs("div", { className: "ExcalidrawModal__actions", children: [!fullScreen && (_jsx("button", { className: "action-button", style: { width: 32, padding: 0 }, onClick: () => setFullScreen(true), children: _jsx("i", { className: "iconify", "data-icon": "material-symbols:fullscreen", "data-height": "18" }) })), fullScreen && (_jsx("button", { className: "action-button", style: { width: 32, padding: 0 }, onClick: () => setFullScreen(false), children: _jsx("i", { className: "iconify", "data-icon": "material-symbols:fullscreen-exit", "data-height": "18" }) })), !initialAppState.viewModeEnabled && (_jsxs(_Fragment, { children: [_jsx("button", { className: "action-button", onClick: discard, children: "Discard" }), _jsx("button", { className: "action-button", onClick: save, children: "Save" })] })), initialAppState.viewModeEnabled && (_jsx("button", { className: "action-button", onClick: () => onClose(), children: "Close" }))] })] }) }) }), document.body);
173
+ } }), _jsxs("div", { className: "ExcalidrawModal__actions", children: [!fullScreen && (_jsx(LoadingButton, { className: "action-button", style: { width: 32, padding: 0, display: 'flex', alignItems: 'center' }, onClick: () => setFullScreen(true), children: _jsx("i", { className: "iconify", "data-icon": "material-symbols:fullscreen", "data-height": "18" }) })), fullScreen && (_jsx(LoadingButton, { className: "action-button", style: { width: 32, padding: 0, display: 'flex', alignItems: 'center' }, onClick: () => setFullScreen(false), children: _jsx("i", { className: "iconify", "data-icon": "material-symbols:fullscreen-exit", "data-height": "18" }) })), !initialAppState.viewModeEnabled && (_jsxs(_Fragment, { children: [_jsx(LoadingButton, { loadingPosition: "start", disabled: loading, className: "action-button", onClick: discard, children: "Discard" }), _jsx(LoadingButton, { loadingPosition: "start", loading: loading, className: "action-button", onClick: save, children: saveText })] })), initialAppState.viewModeEnabled && (_jsx(LoadingButton, { className: "action-button", onClick: () => onClose(), children: "Close" }))] })] }) }) }), document.body);
135
174
  }
@@ -0,0 +1,5 @@
1
+ export declare const getBlockletMountPointInfo: (name: string) => any;
2
+ export declare function isLocalImage(src: string): boolean;
3
+ export declare function joinImageUrl(src: string): string;
4
+ export declare function uploadImageToMediaKit(elements: any, files: any): Promise<void>;
5
+ export declare function covertImageWithMediaKitPath(elements: any, files: any): void;
@@ -0,0 +1,71 @@
1
+ import { joinURL } from 'ufo';
2
+ // chain promise
3
+ const runPromiseInSequence = (list, fn) => {
4
+ return list.reduce((p, item) => {
5
+ return p.then(() => fn(item));
6
+ }, Promise.resolve()); // initial
7
+ };
8
+ export const getBlockletMountPointInfo = (name) => {
9
+ return window.blocklet?.componentMountPoints?.find((x) => x.name === name || x.did === name);
10
+ };
11
+ export function isLocalImage(src) {
12
+ return src.startsWith('data:image') || src.startsWith('blob:');
13
+ }
14
+ export function joinImageUrl(src) {
15
+ if (src?.startsWith('http')) {
16
+ return src;
17
+ }
18
+ // media kit
19
+ // const mountPoint = getBlockletMountPointInfo('z8ia1mAXo8ZE7ytGF36L5uBf9kD2kenhqFGp9');
20
+ // discuss kit
21
+ const mountPoint = getBlockletMountPointInfo('did-comments')?.mountPoint;
22
+ if (mountPoint) {
23
+ const prefix = joinURL(window.location.origin, mountPoint, 'uploads');
24
+ // not add prefix
25
+ if (!src.startsWith(prefix)) {
26
+ return joinURL(prefix, src);
27
+ }
28
+ }
29
+ return src;
30
+ }
31
+ export async function uploadImageToMediaKit(elements, files) {
32
+ const filteredImages = elements?.filter((el) => el.type === 'image');
33
+ if (filteredImages.length > 0) {
34
+ // upload image to Media Kit
35
+ await runPromiseInSequence(filteredImages, async (el) => {
36
+ const fileItem = files[el.fileId];
37
+ // @ts-ignore
38
+ const uploader = window?.uploaderRef?.current?.getUploader?.();
39
+ const prefix = joinImageUrl('/');
40
+ // if not uploaded yet and is has uploader
41
+ if (isLocalImage(fileItem.dataURL) && !!uploader) {
42
+ // try to upload the image
43
+ const blobFile = await fetch(fileItem.dataURL).then((res) => res.blob());
44
+ const { data } = await uploader.uploadFile(blobFile).then((result) => {
45
+ // https://uppy.io/docs/uppy/#options
46
+ return result?.response;
47
+ });
48
+ // only save filename
49
+ files[el.fileId].dataURL = data.filename;
50
+ }
51
+ else if (fileItem.dataURL?.startsWith(prefix)) {
52
+ // remove the prefix
53
+ files[el.fileId].dataURL = fileItem.dataURL.replace(prefix, '');
54
+ }
55
+ });
56
+ // promise all to chain the upload
57
+ }
58
+ }
59
+ export function covertImageWithMediaKitPath(elements, files) {
60
+ const filteredImages = elements?.filter((el) => el.type === 'image') || [];
61
+ if (filteredImages?.length > 0) {
62
+ // upload image to Media Kit
63
+ filteredImages.forEach((el) => {
64
+ const fileItem = files[el.fileId];
65
+ if (!isLocalImage(fileItem.dataURL)) {
66
+ // replace dataURL with Media Kit URL
67
+ files[el.fileId].dataURL = joinImageUrl(fileItem.dataURL);
68
+ }
69
+ });
70
+ }
71
+ }
@@ -7,5 +7,6 @@
7
7
  */
8
8
  /// <reference types="react" />
9
9
  import { LexicalEditor } from 'lexical';
10
+ export declare const INSERT_COMPONENT_COMMAND: import("lexical").LexicalCommand<unknown>;
10
11
  export default function ComponentPickerMenuPlugin(): JSX.Element;
11
12
  export declare const uploadFile: (editor: LexicalEditor) => void;
@@ -15,8 +15,9 @@ import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontal
15
15
  import { $createHeadingNode, $createQuoteNode } from '@lexical/rich-text';
16
16
  import { $wrapNodes } from '@lexical/selection';
17
17
  import { INSERT_TABLE_COMMAND } from '@lexical/table';
18
- import { $createParagraphNode, $getSelection, $isRangeSelection, FORMAT_ELEMENT_COMMAND, } from 'lexical';
19
- import { useCallback, useMemo, useState } from 'react';
18
+ import { $createParagraphNode, $getSelection, $isRangeSelection, FORMAT_ELEMENT_COMMAND, createCommand, $insertNodes, $createTextNode, COMMAND_PRIORITY_EDITOR, } from 'lexical';
19
+ import { mergeRegister } from '@lexical/utils';
20
+ import { useCallback, useEffect, useMemo, useState } from 'react';
20
21
  import { LexicalTypeaheadMenuPlugin, TypeaheadOption, useBasicTypeaheadTriggerMatch, } from '../LexicalTypeaheadMenuPlugin';
21
22
  import useHasNodes from '../../hooks/useHasNodes';
22
23
  import useModal from '../../hooks/useModal';
@@ -61,6 +62,7 @@ function ComponentPickerMenuItem({ index, isSelected, onClick, onMouseEnter, opt
61
62
  }
62
63
  return (_jsxs("li", { tabIndex: -1, className: className, ref: option.setRefElement, role: "option", "aria-selected": isSelected, id: `typeahead-item-${index}`, onMouseEnter: onMouseEnter, onClick: onClick, children: [option.icon, _jsx("span", { className: "text", children: option.title })] }, option.key));
63
64
  }
65
+ export const INSERT_COMPONENT_COMMAND = createCommand('INSERT_COMPONENT_COMMAND');
64
66
  export default function ComponentPickerMenuPlugin() {
65
67
  const [editor] = useLexicalComposerContext();
66
68
  const [modal, showModal] = useModal();
@@ -310,6 +312,30 @@ export default function ComponentPickerMenuPlugin() {
310
312
  closeMenu();
311
313
  });
312
314
  }, [editor]);
315
+ useEffect(() => {
316
+ return mergeRegister(editor.registerCommand(INSERT_COMPONENT_COMMAND, (payload) => {
317
+ editor.update(() => {
318
+ const selection = $getSelection();
319
+ // @ts-ignore
320
+ const anchorNode = selection?.anchor?.getNode();
321
+ const oldText = anchorNode?.getTextContent();
322
+ // check if has payload in current line
323
+ if (oldText?.endsWith(payload)) {
324
+ // set selection to the end of the line
325
+ anchorNode?.selectEnd();
326
+ }
327
+ else {
328
+ // has content should add space before payload
329
+ if (oldText.length > 0 && !oldText?.endsWith(' ')) {
330
+ payload = ` ${payload}`;
331
+ }
332
+ const textNode = $createTextNode(payload);
333
+ $insertNodes([textNode]);
334
+ }
335
+ });
336
+ return true;
337
+ }, COMMAND_PRIORITY_EDITOR));
338
+ }, [editor]);
313
339
  return (_jsxs(_Fragment, { children: [modal, _jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn: checkForTriggerMatch, options: options, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => {
314
340
  return anchorElementRef.current && options.length
315
341
  ? createPortal(_jsx("div", { className: "typeahead-popover component-picker-menu", children: _jsx(List, { children: options.map((option, i) => (_jsx(ComponentPickerMenuItem, { index: i, isSelected: selectedIndex === i, onClick: () => {
@@ -27,7 +27,7 @@ import { sanitizeUrl } from '../../utils/sanitizeUrl';
27
27
  import { INSERT_IMAGE_COMMAND } from '../ImagesPlugin';
28
28
  import { AideToolbarButton } from '../../../ext/AIPlugin';
29
29
  import { AITranslateToolbarButton } from '../../../ext/AITranslationPlugin';
30
- import { uploadFile } from '../ComponentPickerPlugin';
30
+ import { uploadFile, INSERT_COMPONENT_COMMAND } from '../ComponentPickerPlugin';
31
31
  // import { InsertNewTableDialog, InsertTableDialog } from "../TablePlugin";
32
32
  const blockTypeToBlockName = {
33
33
  bullet: 'Bulleted List',
@@ -300,6 +300,7 @@ export default function ToolbarPlugin() {
300
300
  };
301
301
  const config = useEditorConfig().toolbar;
302
302
  const items = config?.items ?? [
303
+ 'component',
303
304
  'block',
304
305
  'align',
305
306
  'bold',
@@ -315,9 +316,18 @@ export default function ToolbarPlugin() {
315
316
  const menus = [];
316
317
  for (const item of items) {
317
318
  switch (item) {
319
+ case 'component': {
320
+ menus.push(_jsx(FormatButton, { disabled: !isEditable, onClick: () => {
321
+ // add '/' to editor
322
+ editor.dispatchCommand(INSERT_COMPONENT_COMMAND, '/');
323
+ }, className: "toolbar-item spaced", title: "Component Picker", "aria-label": "Open component picker", children: _jsx("i", { className: "iconify", "data-icon": "tabler:circle-plus" }) }, item));
324
+ break;
325
+ }
318
326
  case 'block': {
319
327
  if (blockType in blockTypeToBlockName && activeEditor === editor) {
320
- menus.push(_jsx(BlockFormatDropDown, { disabled: !isEditable, blockType: blockType, editor: editor }, "block"), _jsx(Divider, {}, "block-divider"));
328
+ menus.push(_jsx(BlockFormatDropDown, { disabled: !isEditable, blockType: blockType, editor: editor }, "block")
329
+ // <Divider key="block-divider" />
330
+ );
321
331
  }
322
332
  if (blockType === 'code') {
323
333
  menus.push(_jsx(DropDown, { disabled: !isEditable, buttonClassName: "toolbar-item code-language", buttonLabel: getLanguageFriendlyName(codeLanguage), buttonAriaLabel: "Select language", children: CODE_LANGUAGE_OPTIONS.map(([value, name]) => {
@@ -58,5 +58,5 @@
58
58
  background-color: #ddd;
59
59
  }
60
60
  .Modal__content {
61
- padding-top: 20px;
61
+ /* padding-top: 20px; */
62
62
  }
@@ -101,7 +101,7 @@ import { HeadingNode } from '@lexical/rich-text';
101
101
  <EditorConfigProvider
102
102
  value={{
103
103
  toolbar: {
104
- items: ['block', 'font-family', 'font-size', 'italic', 'bold', 'underline'],
104
+ items: ['component','block', 'font-family', 'font-size', 'italic', 'bold', 'underline'],
105
105
  },
106
106
  }}>
107
107
  <BlockletEditor />
@@ -113,7 +113,7 @@ import { HeadingNode } from '@lexical/rich-text';
113
113
  };
114
114
  CustomToolbar.args = {
115
115
  toolbar: {
116
- items: ['block', 'font-family', 'font-size', 'italic', 'bold', 'underline'],
116
+ items: ['component', 'block', 'font-family', 'font-size', 'italic', 'bold', 'underline'],
117
117
  },
118
118
  };
119
119
  export const CustomPlaceholder = (props) => _jsx(BlockletEditor, { ...props });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/editor",
3
- "version": "1.6.188",
3
+ "version": "1.6.191",
4
4
  "main": "lib/index.js",
5
5
  "scripts": {
6
6
  "dev": "npm run storybook",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@arcblock/ux": "^2.9.58",
41
41
  "@blocklet/embed": "^0.1.11",
42
- "@blocklet/pdf": "1.6.188",
42
+ "@blocklet/pdf": "1.6.191",
43
43
  "@excalidraw/excalidraw": "^0.14.2",
44
44
  "@iconify/iconify": "^3.0.1",
45
45
  "@lexical/clipboard": "0.13.1",
@@ -108,5 +108,5 @@
108
108
  "react": "*",
109
109
  "react-dom": "*"
110
110
  },
111
- "gitHead": "7662e0b6e1a16e0129777feba8640acac2032a7b"
111
+ "gitHead": "e0bfe0bdc7ce8d39f4f3684f7170075025bbcb0a"
112
112
  }