@team-monolith/cds 1.70.2 → 1.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Input.js +10 -2
- package/dist/components/InputBase.js +3 -1
- package/dist/patterns/LexicalEditor/Plugins.js +4 -4
- package/dist/patterns/LexicalEditor/nodes/ImageNode/ImageComponent.d.ts +3 -1
- package/dist/patterns/LexicalEditor/nodes/ImageNode/ImageComponent.js +61 -26
- package/dist/patterns/LexicalEditor/nodes/ImageNode/ImageNode.js +6 -7
- package/dist/patterns/LexicalEditor/nodes/ImageNode/ImageResizer.d.ts +24 -0
- package/dist/patterns/LexicalEditor/nodes/ImageNode/ImageResizer.js +233 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/InputComponent.js +1 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectComponent.js +1 -1
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.js +1 -1
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/InputComponent.js +1 -1
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectComponent/SelectComponent.js +1 -1
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.js +1 -1
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +1 -1
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/FloatingLinkEditor.js +27 -10
- package/dist/patterns/LexicalEditor/plugins/FloatingLinkEditorPlugin/index.js +25 -9
- package/package.json +3 -3
package/dist/components/Input.js
CHANGED
|
@@ -65,12 +65,20 @@ const SIZE_TO_FONT_STYLES = (size) => ({
|
|
|
65
65
|
/**
|
|
66
66
|
* [피그마](https://www.figma.com/file/yhrRFizzmhPoHdw9FbYow2/Codle-PD-Kit---Components?type=design&node-id=26-10284&t=HnIfxpf8uR6WmzMW-0)
|
|
67
67
|
*/
|
|
68
|
+
// eslint-disable-next-line react/display-name, @typescript-eslint/no-explicit-any
|
|
68
69
|
const Input = React.forwardRef((props, ref) => {
|
|
69
|
-
const { label, hintText, hintIcon,
|
|
70
|
+
const { label, hintText, hintIcon,
|
|
71
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
72
|
+
className, color, size, placeholder, disabled, startIcon, startLabel, endLabel, endIcon, inputProps, inputRef, fullWidth, onChange, onClear, defaultValue, value } = props,
|
|
73
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
74
|
+
other = __rest(props, ["label", "hintText", "hintIcon", "className", "color", "size", "placeholder", "disabled", "startIcon", "startLabel", "endLabel", "endIcon", "inputProps", "inputRef", "fullWidth", "onChange", "onClear", "defaultValue", "value"]);
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
70
76
|
const { className: _className } = props, inputBaseProps = __rest(props, ["className"]);
|
|
71
77
|
return (_jsxs(InputWrapper, Object.assign({ className: props.className, size: props.size, fullWidth: props.fullWidth, ref: ref }, other, { children: [label ? (_jsxs(Label, Object.assign({ disabled: props.disabled }, { children: [label, " ", _jsx(InputBase, Object.assign({}, inputBaseProps))] }))) : (_jsx(InputBase, Object.assign({}, inputBaseProps))), hintText && (_jsxs(Hint, Object.assign({ color: props.color, disabled: props.disabled }, { children: [hintIcon, hintText] })))] })));
|
|
72
78
|
});
|
|
73
|
-
const InputWrapper = styled
|
|
79
|
+
const InputWrapper = styled("span", {
|
|
80
|
+
shouldForwardProp: (prop) => prop !== "fullWidth",
|
|
81
|
+
}) `
|
|
74
82
|
display: flex;
|
|
75
83
|
flex-direction: column;
|
|
76
84
|
gap: 8px;
|
|
@@ -99,7 +99,9 @@ export function InputBase(props) {
|
|
|
99
99
|
const { className, color, size, disabled = false, startIcon, startLabel, endLabel, endIcon, fullWidth = false, onClear, } = props;
|
|
100
100
|
return (_jsxs(InputContainer, Object.assign({ className: className, disabled: disabled, color: color, inputSize: size, fullWidth: fullWidth }, { children: [startIcon, startLabel && _jsx("span", { children: startLabel }), _jsx(InputComponent, Object.assign({}, props)), endLabel && _jsx("span", { children: endLabel }), endIcon, onClear && (_jsx(ClearButton, Object.assign({ type: "button", onClick: onClear, disabled: disabled, tabIndex: -1 }, { children: _jsx(CloseCircleFillIcon, {}) })))] })));
|
|
101
101
|
}
|
|
102
|
-
const InputContainer = styled
|
|
102
|
+
const InputContainer = styled("div", {
|
|
103
|
+
shouldForwardProp: (prop) => !["inputSize", "fullWidth"].includes(prop),
|
|
104
|
+
})(({ theme, color, inputSize, disabled, fullWidth }) => css `
|
|
103
105
|
display: flex;
|
|
104
106
|
align-items: center;
|
|
105
107
|
gap: 16px;
|
|
@@ -8,7 +8,7 @@ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
|
8
8
|
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
9
9
|
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
|
|
10
10
|
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
|
|
11
|
-
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
|
|
11
|
+
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
12
12
|
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
|
13
13
|
import { CUSTOM_TRANSFORMERS } from "./plugins/MarkdownTransformers";
|
|
14
14
|
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
|
|
@@ -22,9 +22,9 @@ import ImagesPlugin from "./plugins/ImagesPlugin";
|
|
|
22
22
|
import { TablePlugin } from "@lexical/react/LexicalTablePlugin";
|
|
23
23
|
import hr from "./hr.svg";
|
|
24
24
|
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
|
|
25
|
-
import
|
|
25
|
+
import { ClickableLinkPlugin } from "@lexical/react/LexicalClickableLinkPlugin";
|
|
26
26
|
import FloatingLinkEditorPlugin from "./plugins/FloatingLinkEditorPlugin";
|
|
27
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
27
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
28
28
|
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
|
|
29
29
|
import styled from "@emotion/styled";
|
|
30
30
|
import ProblemInputPlugin from "./plugins/ProblemInputPlugin";
|
|
@@ -47,7 +47,7 @@ export default function Plugins(props) {
|
|
|
47
47
|
onChange === null || onChange === void 0 ? void 0 : onChange(editorState.toJSON());
|
|
48
48
|
},
|
|
49
49
|
// ignore 하지 않으면 Form에서 수정하지 않았는데 Dirty로 처리됨.
|
|
50
|
-
ignoreSelectionChange: true }), _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(MarkdownShortcutPlugin, { transformers: CUSTOM_TRANSFORMERS }), _jsx(HistoryPlugin, {})] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(
|
|
50
|
+
ignoreSelectionChange: true }), _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(MarkdownShortcutPlugin, { transformers: CUSTOM_TRANSFORMERS }), _jsx(HistoryPlugin, {})] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(ClickableLinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(ImagesPlugin, {}), _jsx(TablePlugin, {}), _jsx(LinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 5 }), _jsx(ProblemInputPlugin, {}), _jsx(ProblemSelectPlugin, {}), _jsx(LayoutPlugin, {}), _jsx(SheetSelectPlugin, {}), _jsx(SheetInputPlugin, {}), _jsx(SelfEvaluationPlugin, {})] }));
|
|
51
51
|
}
|
|
52
52
|
const ScrollArea = styled.div `
|
|
53
53
|
min-height: 150px;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
1
2
|
/**
|
|
2
3
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
4
|
*
|
|
@@ -8,11 +9,12 @@
|
|
|
8
9
|
/// <reference types="react" />
|
|
9
10
|
import type { LexicalCommand, NodeKey } from "lexical";
|
|
10
11
|
export declare const RIGHT_CLICK_IMAGE_COMMAND: LexicalCommand<MouseEvent>;
|
|
11
|
-
export default function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, }: {
|
|
12
|
+
export default function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, resizable, }: {
|
|
12
13
|
altText: string;
|
|
13
14
|
height: "inherit" | number;
|
|
14
15
|
maxWidth: number;
|
|
15
16
|
nodeKey: NodeKey;
|
|
17
|
+
resizable: boolean;
|
|
16
18
|
src: string;
|
|
17
19
|
width: "inherit" | number;
|
|
18
20
|
}): JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
2
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
3
3
|
import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
|
|
4
4
|
import { mergeRegister } from "@lexical/utils";
|
|
@@ -9,8 +9,10 @@ import SquareButton from "../../../../components/SquareButton";
|
|
|
9
9
|
import { Settings3FillIcon } from "../../../../icons";
|
|
10
10
|
import styled from "@emotion/styled";
|
|
11
11
|
import { InsertImageDialog } from "../../components/InsertImageDialog";
|
|
12
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
12
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
13
13
|
import { ImageNotAvailable } from "./ImageNotAvailable";
|
|
14
|
+
import ImageResizer from "./ImageResizer";
|
|
15
|
+
import { css } from "@emotion/react";
|
|
14
16
|
const imageCache = new Set();
|
|
15
17
|
export const RIGHT_CLICK_IMAGE_COMMAND = createCommand("RIGHT_CLICK_IMAGE_COMMAND");
|
|
16
18
|
function useSuspenseImage(src) {
|
|
@@ -22,10 +24,15 @@ function useSuspenseImage(src) {
|
|
|
22
24
|
imageCache.add(src);
|
|
23
25
|
resolve(null);
|
|
24
26
|
};
|
|
27
|
+
img.onerror = () => {
|
|
28
|
+
imageCache.add(src);
|
|
29
|
+
};
|
|
25
30
|
});
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
|
-
function LazyImage({ altText, className, imageRef, src, width, height,
|
|
33
|
+
function LazyImage({ altText, className, imageRef, src, width, height,
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
35
|
+
maxWidth, onError, }) {
|
|
29
36
|
useSuspenseImage(src);
|
|
30
37
|
return (_jsx("img", { className: className || undefined, src: src, alt: altText, ref: imageRef, style: {
|
|
31
38
|
height,
|
|
@@ -35,27 +42,33 @@ function LazyImage({ altText, className, imageRef, src, width, height, maxWidth,
|
|
|
35
42
|
// fixme: maxWidth를 수정할 수 있게 되면 원복합니다.
|
|
36
43
|
maxWidth: "100%",
|
|
37
44
|
width,
|
|
38
|
-
}, draggable: "false" }));
|
|
45
|
+
}, onError: onError, draggable: "false" }));
|
|
39
46
|
}
|
|
40
|
-
export default function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, }) {
|
|
47
|
+
export default function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, resizable, }) {
|
|
41
48
|
const imageRef = useRef(null);
|
|
42
49
|
const buttonRef = useRef(null);
|
|
43
50
|
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
|
|
51
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
44
52
|
const [editor] = useLexicalComposerContext();
|
|
45
53
|
const [selection, setSelection] = useState(null);
|
|
46
54
|
const activeEditorRef = useRef(null);
|
|
47
|
-
const
|
|
48
|
-
|
|
55
|
+
const [isLoadError, setIsLoadError] = useState(false);
|
|
56
|
+
const $onDelete = useCallback((payload) => {
|
|
57
|
+
const deleteSelection = $getSelection();
|
|
58
|
+
if (isSelected && $isNodeSelection(deleteSelection)) {
|
|
49
59
|
const event = payload;
|
|
50
60
|
event.preventDefault();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
editor.update(() => {
|
|
62
|
+
deleteSelection.getNodes().forEach((node) => {
|
|
63
|
+
if ($isImageNode(node)) {
|
|
64
|
+
node.remove();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
55
68
|
}
|
|
56
69
|
return false;
|
|
57
|
-
}, [
|
|
58
|
-
const onEnter = useCallback((event) => {
|
|
70
|
+
}, [editor, isSelected]);
|
|
71
|
+
const $onEnter = useCallback((event) => {
|
|
59
72
|
const latestSelection = $getSelection();
|
|
60
73
|
const buttonElem = buttonRef.current;
|
|
61
74
|
if (isSelected &&
|
|
@@ -69,7 +82,7 @@ export default function ImageComponent({ src, altText, nodeKey, width, height, m
|
|
|
69
82
|
}
|
|
70
83
|
return false;
|
|
71
84
|
}, [isSelected]);
|
|
72
|
-
const onEscape = useCallback((event) => {
|
|
85
|
+
const $onEscape = useCallback((event) => {
|
|
73
86
|
if (buttonRef.current === event.target) {
|
|
74
87
|
$setSelection(null);
|
|
75
88
|
editor.update(() => {
|
|
@@ -85,6 +98,9 @@ export default function ImageComponent({ src, altText, nodeKey, width, height, m
|
|
|
85
98
|
}, [editor, setSelected]);
|
|
86
99
|
const onClick = useCallback((payload) => {
|
|
87
100
|
const event = payload;
|
|
101
|
+
if (isResizing) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
88
104
|
if (event.target === imageRef.current) {
|
|
89
105
|
if (event.shiftKey) {
|
|
90
106
|
setSelected(!isSelected);
|
|
@@ -96,7 +112,7 @@ export default function ImageComponent({ src, altText, nodeKey, width, height, m
|
|
|
96
112
|
return true;
|
|
97
113
|
}
|
|
98
114
|
return false;
|
|
99
|
-
}, [isSelected, setSelected, clearSelection]);
|
|
115
|
+
}, [isResizing, isSelected, setSelected, clearSelection]);
|
|
100
116
|
const onRightClick = useCallback((event) => {
|
|
101
117
|
editor.getEditorState().read(() => {
|
|
102
118
|
const latestSelection = $getSelection();
|
|
@@ -126,7 +142,7 @@ export default function ImageComponent({ src, altText, nodeKey, width, height, m
|
|
|
126
142
|
return true;
|
|
127
143
|
}
|
|
128
144
|
return false;
|
|
129
|
-
}, 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));
|
|
145
|
+
}, 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));
|
|
130
146
|
rootElement === null || rootElement === void 0 ? void 0 : rootElement.addEventListener("contextmenu", onRightClick);
|
|
131
147
|
return () => {
|
|
132
148
|
isMounted = false;
|
|
@@ -136,27 +152,46 @@ export default function ImageComponent({ src, altText, nodeKey, width, height, m
|
|
|
136
152
|
}, [
|
|
137
153
|
clearSelection,
|
|
138
154
|
editor,
|
|
155
|
+
isResizing,
|
|
139
156
|
isSelected,
|
|
140
157
|
nodeKey,
|
|
141
|
-
onDelete,
|
|
142
|
-
onEnter,
|
|
143
|
-
onEscape,
|
|
158
|
+
$onDelete,
|
|
159
|
+
$onEnter,
|
|
160
|
+
$onEscape,
|
|
144
161
|
onClick,
|
|
145
162
|
onRightClick,
|
|
146
163
|
setSelected,
|
|
147
164
|
]);
|
|
148
|
-
const
|
|
149
|
-
|
|
165
|
+
const onResizeEnd = (nextWidth, nextHeight) => {
|
|
166
|
+
// Delay hiding the resize bars for click case
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
setIsResizing(false);
|
|
169
|
+
}, 200);
|
|
170
|
+
editor.update(() => {
|
|
171
|
+
const node = $getNodeByKey(nodeKey);
|
|
172
|
+
if ($isImageNode(node)) {
|
|
173
|
+
node.setWidthAndHeight(nextWidth, nextHeight);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
const onResizeStart = () => {
|
|
178
|
+
setIsResizing(true);
|
|
179
|
+
};
|
|
180
|
+
const draggable = isSelected && $isNodeSelection(selection) && !isResizing;
|
|
181
|
+
const isFocused = isSelected || isResizing;
|
|
150
182
|
const [open, setOpen] = useState(false);
|
|
151
183
|
const isEditable = useLexicalEditable();
|
|
152
184
|
if (!isEditable) {
|
|
153
|
-
return (_jsx(Suspense, Object.assign({ fallback:
|
|
185
|
+
return (_jsx(Suspense, Object.assign({ fallback: null }, { children: _jsx("div", Object.assign({ draggable: draggable }, { children: isLoadError ? (_jsx(ImageNotAvailable, {})) : (_jsx(LazyImage, { className: isFocused
|
|
154
186
|
? `focused ${$isNodeSelection(selection) ? "draggable" : ""}`
|
|
155
|
-
: null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth }) })) })));
|
|
187
|
+
: null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth, onError: () => setIsLoadError(true) })) })) })));
|
|
156
188
|
}
|
|
157
|
-
return (_jsxs(_Fragment, { children: [_jsxs(EditContainer, { children: [_jsx(Suspense, Object.assign({ fallback:
|
|
158
|
-
|
|
159
|
-
|
|
189
|
+
return (_jsxs(_Fragment, { children: [_jsxs(EditContainer, { children: [_jsx(Suspense, Object.assign({ fallback: null }, { children: _jsx("div", Object.assign({ draggable: draggable, css: css `
|
|
190
|
+
// ImageResizer를 위한 relative 설정입니다.
|
|
191
|
+
position: relative;
|
|
192
|
+
` }, { children: isLoadError ? (_jsx(ImageNotAvailable, {})) : (_jsxs(_Fragment, { children: [_jsx(LazyImage, { className: isFocused
|
|
193
|
+
? `focused ${$isNodeSelection(selection) ? "draggable" : ""}`
|
|
194
|
+
: null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth, onError: () => setIsLoadError(true) }), resizable && $isNodeSelection(selection) && isFocused && (_jsx(ImageResizer, { showCaption: false, setShowCaption: () => { }, editor: editor, buttonRef: buttonRef, imageRef: imageRef, maxWidth: maxWidth, onResizeStart: onResizeStart, onResizeEnd: onResizeEnd, captionsEnabled: false }))] })) })) })), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
160
195
|
setOpen(true);
|
|
161
196
|
}, buttonProps: {
|
|
162
197
|
"aria-label": "이미지 수정하기",
|
|
@@ -2,10 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { $applyNodeReplacement, createEditor, DecoratorNode } from "lexical";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Suspense } from "react";
|
|
5
|
-
const ImageComponent = React.lazy(
|
|
6
|
-
|
|
7
|
-
() => import("./ImageComponent"));
|
|
8
|
-
function convertImageElement(domNode) {
|
|
5
|
+
const ImageComponent = React.lazy(() => import("./ImageComponent"));
|
|
6
|
+
function $convertImageElement(domNode) {
|
|
9
7
|
if (domNode instanceof HTMLImageElement) {
|
|
10
8
|
const { alt: altText, src, width, height } = domNode;
|
|
11
9
|
const node = $createImageNode({ altText, height, src, width });
|
|
@@ -47,8 +45,9 @@ export class ImageNode extends DecoratorNode {
|
|
|
47
45
|
}
|
|
48
46
|
static importDOM() {
|
|
49
47
|
return {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
50
49
|
img: (node) => ({
|
|
51
|
-
conversion: convertImageElement,
|
|
50
|
+
conversion: $convertImageElement,
|
|
52
51
|
priority: 0,
|
|
53
52
|
}),
|
|
54
53
|
};
|
|
@@ -61,7 +60,7 @@ export class ImageNode extends DecoratorNode {
|
|
|
61
60
|
this.__width = width || "inherit";
|
|
62
61
|
this.__height = height || "inherit";
|
|
63
62
|
this.__showCaption = showCaption || false;
|
|
64
|
-
this.__caption = caption || createEditor();
|
|
63
|
+
this.__caption = caption || createEditor({ nodes: [] });
|
|
65
64
|
this.__captionsEnabled = captionsEnabled || captionsEnabled === undefined;
|
|
66
65
|
}
|
|
67
66
|
exportJSON() {
|
|
@@ -114,7 +113,7 @@ export class ImageNode extends DecoratorNode {
|
|
|
114
113
|
return this.__altText;
|
|
115
114
|
}
|
|
116
115
|
decorate() {
|
|
117
|
-
return (_jsx(Suspense, Object.assign({ fallback: null }, { children: _jsx(ImageComponent, { src: this.__src, altText: this.__altText, width: this.__width, height: this.__height, maxWidth: this.__maxWidth, nodeKey: this.getKey() }) })));
|
|
116
|
+
return (_jsx(Suspense, Object.assign({ fallback: null }, { children: _jsx(ImageComponent, { src: this.__src, altText: this.__altText, width: this.__width, height: this.__height, maxWidth: this.__maxWidth, nodeKey: this.getKey(), resizable: true }) })));
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
export function $createImageNode({ altText, height, maxWidth = 500, captionsEnabled, src, width, showCaption, caption, key, }) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
/// <reference types="react" />
|
|
9
|
+
import type { LexicalEditor } from "lexical";
|
|
10
|
+
export default function ImageResizer({ onResizeStart, onResizeEnd, buttonRef, imageRef, maxWidth, editor, showCaption, setShowCaption, captionsEnabled, }: {
|
|
11
|
+
editor: LexicalEditor;
|
|
12
|
+
buttonRef: {
|
|
13
|
+
current: null | HTMLButtonElement;
|
|
14
|
+
};
|
|
15
|
+
imageRef: {
|
|
16
|
+
current: null | HTMLElement;
|
|
17
|
+
};
|
|
18
|
+
maxWidth?: number;
|
|
19
|
+
onResizeEnd: (width: "inherit" | number, height: "inherit" | number) => void;
|
|
20
|
+
onResizeStart: () => void;
|
|
21
|
+
setShowCaption: (show: boolean) => void;
|
|
22
|
+
showCaption: boolean;
|
|
23
|
+
captionsEnabled: boolean;
|
|
24
|
+
}): JSX.Element;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { calculateZoomLevel } from "@lexical/utils";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import styled from "@emotion/styled";
|
|
5
|
+
function clamp(value, min, max) {
|
|
6
|
+
return Math.min(Math.max(value, min), max);
|
|
7
|
+
}
|
|
8
|
+
const Direction = {
|
|
9
|
+
east: 1 << 0,
|
|
10
|
+
north: 1 << 3,
|
|
11
|
+
south: 1 << 1,
|
|
12
|
+
west: 1 << 2,
|
|
13
|
+
};
|
|
14
|
+
export default function ImageResizer({ onResizeStart, onResizeEnd, buttonRef, imageRef, maxWidth, editor, showCaption, setShowCaption, captionsEnabled, }) {
|
|
15
|
+
const controlWrapperRef = useRef(null);
|
|
16
|
+
const userSelect = useRef({
|
|
17
|
+
priority: "",
|
|
18
|
+
value: "default",
|
|
19
|
+
});
|
|
20
|
+
const positioningRef = useRef({
|
|
21
|
+
currentHeight: 0,
|
|
22
|
+
currentWidth: 0,
|
|
23
|
+
direction: 0,
|
|
24
|
+
isResizing: false,
|
|
25
|
+
ratio: 0,
|
|
26
|
+
startHeight: 0,
|
|
27
|
+
startWidth: 0,
|
|
28
|
+
startX: 0,
|
|
29
|
+
startY: 0,
|
|
30
|
+
});
|
|
31
|
+
const editorRootElement = editor.getRootElement();
|
|
32
|
+
// Find max width, accounting for editor padding.
|
|
33
|
+
const maxWidthContainer = maxWidth
|
|
34
|
+
? maxWidth
|
|
35
|
+
: editorRootElement !== null
|
|
36
|
+
? editorRootElement.getBoundingClientRect().width - 20
|
|
37
|
+
: 100;
|
|
38
|
+
const maxHeightContainer = editorRootElement !== null
|
|
39
|
+
? editorRootElement.getBoundingClientRect().height - 20
|
|
40
|
+
: 100;
|
|
41
|
+
const minWidth = 100;
|
|
42
|
+
const minHeight = 100;
|
|
43
|
+
const setStartCursor = (direction) => {
|
|
44
|
+
const ew = direction === Direction.east || direction === Direction.west;
|
|
45
|
+
const ns = direction === Direction.north || direction === Direction.south;
|
|
46
|
+
const nwse = (direction & Direction.north && direction & Direction.west) ||
|
|
47
|
+
(direction & Direction.south && direction & Direction.east);
|
|
48
|
+
const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw";
|
|
49
|
+
if (editorRootElement !== null) {
|
|
50
|
+
editorRootElement.style.setProperty("cursor", `${cursorDir}-resize`, "important");
|
|
51
|
+
}
|
|
52
|
+
if (document.body !== null) {
|
|
53
|
+
document.body.style.setProperty("cursor", `${cursorDir}-resize`, "important");
|
|
54
|
+
userSelect.current.value = document.body.style.getPropertyValue("-webkit-user-select");
|
|
55
|
+
userSelect.current.priority = document.body.style.getPropertyPriority("-webkit-user-select");
|
|
56
|
+
document.body.style.setProperty("-webkit-user-select", `none`, "important");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const setEndCursor = () => {
|
|
60
|
+
if (editorRootElement !== null) {
|
|
61
|
+
editorRootElement.style.setProperty("cursor", "text");
|
|
62
|
+
}
|
|
63
|
+
if (document.body !== null) {
|
|
64
|
+
document.body.style.setProperty("cursor", "default");
|
|
65
|
+
document.body.style.setProperty("-webkit-user-select", userSelect.current.value, userSelect.current.priority);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const handlePointerDown = (event, direction) => {
|
|
69
|
+
if (!editor.isEditable()) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const image = imageRef.current;
|
|
73
|
+
const controlWrapper = controlWrapperRef.current;
|
|
74
|
+
if (image !== null && controlWrapper !== null) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
const { width, height } = image.getBoundingClientRect();
|
|
77
|
+
const zoom = calculateZoomLevel(image);
|
|
78
|
+
const positioning = positioningRef.current;
|
|
79
|
+
positioning.startWidth = width;
|
|
80
|
+
positioning.startHeight = height;
|
|
81
|
+
positioning.ratio = width / height;
|
|
82
|
+
positioning.currentWidth = width;
|
|
83
|
+
positioning.currentHeight = height;
|
|
84
|
+
positioning.startX = event.clientX / zoom;
|
|
85
|
+
positioning.startY = event.clientY / zoom;
|
|
86
|
+
positioning.isResizing = true;
|
|
87
|
+
positioning.direction = direction;
|
|
88
|
+
setStartCursor(direction);
|
|
89
|
+
onResizeStart();
|
|
90
|
+
controlWrapper.classList.add("image-control-wrapper--resizing");
|
|
91
|
+
image.style.height = `${height}px`;
|
|
92
|
+
image.style.width = `${width}px`;
|
|
93
|
+
document.addEventListener("pointermove", handlePointerMove);
|
|
94
|
+
document.addEventListener("pointerup", handlePointerUp);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const handlePointerMove = (event) => {
|
|
98
|
+
const image = imageRef.current;
|
|
99
|
+
const positioning = positioningRef.current;
|
|
100
|
+
const isHorizontal = positioning.direction & (Direction.east | Direction.west);
|
|
101
|
+
const isVertical = positioning.direction & (Direction.south | Direction.north);
|
|
102
|
+
if (image !== null && positioning.isResizing) {
|
|
103
|
+
const zoom = calculateZoomLevel(image);
|
|
104
|
+
// Corner cursor
|
|
105
|
+
if (isHorizontal && isVertical) {
|
|
106
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
107
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
108
|
+
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
|
|
109
|
+
const height = width / positioning.ratio;
|
|
110
|
+
image.style.width = `${width}px`;
|
|
111
|
+
image.style.height = `${height}px`;
|
|
112
|
+
positioning.currentHeight = height;
|
|
113
|
+
positioning.currentWidth = width;
|
|
114
|
+
}
|
|
115
|
+
else if (isVertical) {
|
|
116
|
+
let diff = Math.floor(positioning.startY - event.clientY / zoom);
|
|
117
|
+
diff = positioning.direction & Direction.south ? -diff : diff;
|
|
118
|
+
const height = clamp(positioning.startHeight + diff, minHeight, maxHeightContainer);
|
|
119
|
+
image.style.height = `${height}px`;
|
|
120
|
+
positioning.currentHeight = height;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
let diff = Math.floor(positioning.startX - event.clientX / zoom);
|
|
124
|
+
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
125
|
+
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
|
|
126
|
+
image.style.width = `${width}px`;
|
|
127
|
+
positioning.currentWidth = width;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const handlePointerUp = () => {
|
|
132
|
+
const image = imageRef.current;
|
|
133
|
+
const positioning = positioningRef.current;
|
|
134
|
+
const controlWrapper = controlWrapperRef.current;
|
|
135
|
+
if (image !== null && controlWrapper !== null && positioning.isResizing) {
|
|
136
|
+
const width = positioning.currentWidth;
|
|
137
|
+
const height = positioning.currentHeight;
|
|
138
|
+
positioning.startWidth = 0;
|
|
139
|
+
positioning.startHeight = 0;
|
|
140
|
+
positioning.ratio = 0;
|
|
141
|
+
positioning.startX = 0;
|
|
142
|
+
positioning.startY = 0;
|
|
143
|
+
positioning.currentWidth = 0;
|
|
144
|
+
positioning.currentHeight = 0;
|
|
145
|
+
positioning.isResizing = false;
|
|
146
|
+
controlWrapper.classList.remove("image-control-wrapper--resizing");
|
|
147
|
+
setEndCursor();
|
|
148
|
+
onResizeEnd(width, height);
|
|
149
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
150
|
+
document.removeEventListener("pointerup", handlePointerUp);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
return (_jsxs(Container, Object.assign({ ref: controlWrapperRef }, { children: [!showCaption && captionsEnabled && (_jsx("button", Object.assign({ className: "image-caption-button", ref: buttonRef, onClick: () => {
|
|
154
|
+
setShowCaption(!showCaption);
|
|
155
|
+
} }, { children: "Add Caption" }))), _jsx("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
|
|
156
|
+
handlePointerDown(event, Direction.north);
|
|
157
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-ne", onPointerDown: (event) => {
|
|
158
|
+
handlePointerDown(event, Direction.north | Direction.east);
|
|
159
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-e", onPointerDown: (event) => {
|
|
160
|
+
handlePointerDown(event, Direction.east);
|
|
161
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-se", onPointerDown: (event) => {
|
|
162
|
+
handlePointerDown(event, Direction.south | Direction.east);
|
|
163
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-s", onPointerDown: (event) => {
|
|
164
|
+
handlePointerDown(event, Direction.south);
|
|
165
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-sw", onPointerDown: (event) => {
|
|
166
|
+
handlePointerDown(event, Direction.south | Direction.west);
|
|
167
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-w", onPointerDown: (event) => {
|
|
168
|
+
handlePointerDown(event, Direction.west);
|
|
169
|
+
} }), _jsx("div", { className: "image-resizer image-resizer-nw", onPointerDown: (event) => {
|
|
170
|
+
handlePointerDown(event, Direction.north | Direction.west);
|
|
171
|
+
} })] })));
|
|
172
|
+
}
|
|
173
|
+
const Container = styled.div `
|
|
174
|
+
&.image-control-wrapper--resizing {
|
|
175
|
+
touch-action: none;
|
|
176
|
+
}
|
|
177
|
+
.image-resizer {
|
|
178
|
+
display: block;
|
|
179
|
+
width: 7px;
|
|
180
|
+
height: 7px;
|
|
181
|
+
position: absolute;
|
|
182
|
+
background-color: rgb(60, 132, 244);
|
|
183
|
+
border: 1px solid #fff;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.image-resizer.image-resizer-n {
|
|
187
|
+
top: -6px;
|
|
188
|
+
left: 48%;
|
|
189
|
+
cursor: n-resize;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.image-resizer.image-resizer-ne {
|
|
193
|
+
top: -6px;
|
|
194
|
+
right: -6px;
|
|
195
|
+
cursor: ne-resize;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.image-resizer.image-resizer-e {
|
|
199
|
+
bottom: 48%;
|
|
200
|
+
right: -6px;
|
|
201
|
+
cursor: e-resize;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.image-resizer.image-resizer-se {
|
|
205
|
+
bottom: -2px;
|
|
206
|
+
right: -6px;
|
|
207
|
+
cursor: nwse-resize;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.image-resizer.image-resizer-s {
|
|
211
|
+
bottom: -2px;
|
|
212
|
+
left: 48%;
|
|
213
|
+
cursor: s-resize;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.image-resizer.image-resizer-sw {
|
|
217
|
+
bottom: -2px;
|
|
218
|
+
left: -6px;
|
|
219
|
+
cursor: sw-resize;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.image-resizer.image-resizer-w {
|
|
223
|
+
bottom: 48%;
|
|
224
|
+
left: -6px;
|
|
225
|
+
cursor: w-resize;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.image-resizer.image-resizer-nw {
|
|
229
|
+
top: -6px;
|
|
230
|
+
left: -6px;
|
|
231
|
+
cursor: nw-resize;
|
|
232
|
+
}
|
|
233
|
+
`;
|
|
@@ -17,7 +17,7 @@ import SquareButton from "../../../../components/SquareButton";
|
|
|
17
17
|
import { Settings3FillIcon } from "../../../../icons";
|
|
18
18
|
import styled from "@emotion/styled";
|
|
19
19
|
import SettingForm from "./SettingForm";
|
|
20
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
20
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
21
21
|
import { $isProblemInputNode } from "./ProblemInputNode";
|
|
22
22
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
23
23
|
import { $getNodeByKey } from "lexical";
|
|
@@ -4,7 +4,7 @@ import { $getNodeByKey } from "lexical";
|
|
|
4
4
|
import { $isProblemSelectNode, } from "./ProblemSelectNode";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { useContext, useState } from "react";
|
|
7
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
7
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
8
8
|
import { SelectBoxEdit, SelectBoxView } from "./SelectBox";
|
|
9
9
|
import { css } from "@emotion/react";
|
|
10
10
|
import SquareButton from "../../../../components/SquareButton";
|
|
@@ -4,7 +4,7 @@ import { $getNodeByKey } from "lexical";
|
|
|
4
4
|
import { $isSelfEvaluationNode, } from "../SelfEvaluationNode";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { useContext, useState } from "react";
|
|
7
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
7
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
8
8
|
import { LexicalCustomConfigContext } from "../../../LexicalCustomConfigContext";
|
|
9
9
|
import { Evaluation, SettingForm } from "./Evaluation";
|
|
10
10
|
import { ICON_DATA } from "../iconData";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
2
|
/** @jsxImportSource @emotion/react */
|
|
3
3
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
4
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
4
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
5
5
|
import { $getNodeByKey } from "lexical";
|
|
6
6
|
import { useContext, useState } from "react";
|
|
7
7
|
import { LexicalCustomConfigContext } from "../../LexicalCustomConfigContext";
|
package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectComponent/SelectComponent.js
CHANGED
|
@@ -4,7 +4,7 @@ import { $getNodeByKey } from "lexical";
|
|
|
4
4
|
import { $isSheetSelectNode } from "../SheetSelectNode";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { useContext, useState } from "react";
|
|
7
|
-
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
7
|
+
import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
|
|
8
8
|
import { LexicalCustomConfigContext } from "../../../LexicalCustomConfigContext";
|
|
9
9
|
import styled from "@emotion/styled";
|
|
10
10
|
import { css } from "@emotion/react";
|
|
@@ -29,7 +29,7 @@ import styled from "@emotion/styled";
|
|
|
29
29
|
import { useContextMenuOptions } from "./useContextMenuOptions";
|
|
30
30
|
import { ZINDEX } from "../../../../utils/zIndex";
|
|
31
31
|
import { useTheme } from "@emotion/react";
|
|
32
|
-
import { InsertImageDialog
|
|
32
|
+
import { InsertImageDialog } from "../../components/InsertImageDialog";
|
|
33
33
|
import { INSERT_IMAGE_COMMAND } from "../ImagesPlugin";
|
|
34
34
|
export const COMPONENT_ADDER_MENU_CLASSNAME = "component-adder-menu";
|
|
35
35
|
function isOnMenu(element) {
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js
CHANGED
|
@@ -29,7 +29,7 @@ import { INSERT_LAYOUT_COMMAND } from "../LayoutPlugin";
|
|
|
29
29
|
import { INSERT_SHEET_SELECT_COMMAND } from "../SheetSelectPlugin";
|
|
30
30
|
import { INSERT_SHEET_INPUT_COMMAND } from "../SheetInputPlugin";
|
|
31
31
|
import { INSERT_SELF_EVALUATION_COMMAND } from "../SelfEvaluationPlugin";
|
|
32
|
-
import { InsertImageDialog
|
|
32
|
+
import { InsertImageDialog } from "../../components/InsertImageDialog";
|
|
33
33
|
import { INSERT_IMAGE_COMMAND } from "../ImagesPlugin";
|
|
34
34
|
// import useModal from "../../hooks/useModal";
|
|
35
35
|
// import catTypingGif from "../../images/cat-typing.gif";
|
|
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { sanitizeUrl } from "../../utils/url";
|
|
4
4
|
import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND, } from "lexical";
|
|
5
5
|
import { getSelectedNode } from "../../utils/getSelectedNode";
|
|
6
|
-
import { $isLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
|
|
6
|
+
import { $createLinkNode, $isAutoLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND, } from "@lexical/link";
|
|
7
7
|
import { setFloatingElemPositionForLinkEditor } from "../../utils/setFloatingElemPositionForLinkEditor";
|
|
8
8
|
import { $findMatchingParent, mergeRegister } from "@lexical/utils";
|
|
9
9
|
import styled from "@emotion/styled";
|
|
@@ -16,7 +16,7 @@ export function FloatingLinkEditor(props) {
|
|
|
16
16
|
const [linkUrl, setLinkUrl] = useState("");
|
|
17
17
|
const [editedLinkUrl, setEditedLinkUrl] = useState("https://");
|
|
18
18
|
const [lastSelection, setLastSelection] = useState(null);
|
|
19
|
-
const updateLinkEditor = useCallback(() => {
|
|
19
|
+
const $updateLinkEditor = useCallback(() => {
|
|
20
20
|
var _a, _b;
|
|
21
21
|
const selection = $getSelection();
|
|
22
22
|
if ($isRangeSelection(selection)) {
|
|
@@ -31,6 +31,9 @@ export function FloatingLinkEditor(props) {
|
|
|
31
31
|
else {
|
|
32
32
|
setLinkUrl("");
|
|
33
33
|
}
|
|
34
|
+
if (isLinkEditMode) {
|
|
35
|
+
setEditedLinkUrl(linkUrl);
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
const editorElem = editorRef.current;
|
|
36
39
|
const nativeSelection = window.getSelection();
|
|
@@ -60,12 +63,12 @@ export function FloatingLinkEditor(props) {
|
|
|
60
63
|
setLinkUrl("");
|
|
61
64
|
}
|
|
62
65
|
return true;
|
|
63
|
-
}, [anchorElem, editor, setIsLinkEditMode]);
|
|
66
|
+
}, [anchorElem, editor, setIsLinkEditMode, isLinkEditMode, linkUrl]);
|
|
64
67
|
useEffect(() => {
|
|
65
68
|
const scrollerElem = anchorElem.parentElement;
|
|
66
69
|
const update = () => {
|
|
67
70
|
editor.getEditorState().read(() => {
|
|
68
|
-
updateLinkEditor();
|
|
71
|
+
$updateLinkEditor();
|
|
69
72
|
});
|
|
70
73
|
};
|
|
71
74
|
window.addEventListener("resize", update);
|
|
@@ -78,14 +81,14 @@ export function FloatingLinkEditor(props) {
|
|
|
78
81
|
scrollerElem.removeEventListener("scroll", update);
|
|
79
82
|
}
|
|
80
83
|
};
|
|
81
|
-
}, [anchorElem.parentElement, editor, updateLinkEditor]);
|
|
84
|
+
}, [anchorElem.parentElement, editor, $updateLinkEditor]);
|
|
82
85
|
useEffect(() => {
|
|
83
86
|
return mergeRegister(editor.registerUpdateListener(({ editorState }) => {
|
|
84
87
|
editorState.read(() => {
|
|
85
|
-
updateLinkEditor();
|
|
88
|
+
$updateLinkEditor();
|
|
86
89
|
});
|
|
87
90
|
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
88
|
-
updateLinkEditor();
|
|
91
|
+
$updateLinkEditor();
|
|
89
92
|
return true;
|
|
90
93
|
}, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, () => {
|
|
91
94
|
if (isLink) {
|
|
@@ -94,12 +97,12 @@ export function FloatingLinkEditor(props) {
|
|
|
94
97
|
}
|
|
95
98
|
return false;
|
|
96
99
|
}, COMMAND_PRIORITY_HIGH));
|
|
97
|
-
}, [editor, updateLinkEditor, setIsLink, isLink]);
|
|
100
|
+
}, [editor, $updateLinkEditor, setIsLink, isLink]);
|
|
98
101
|
useEffect(() => {
|
|
99
102
|
editor.getEditorState().read(() => {
|
|
100
|
-
updateLinkEditor();
|
|
103
|
+
$updateLinkEditor();
|
|
101
104
|
});
|
|
102
|
-
}, [editor, updateLinkEditor]);
|
|
105
|
+
}, [editor, $updateLinkEditor]);
|
|
103
106
|
useEffect(() => {
|
|
104
107
|
if (isLinkEditMode && inputRef.current) {
|
|
105
108
|
inputRef.current.focus();
|
|
@@ -119,6 +122,20 @@ export function FloatingLinkEditor(props) {
|
|
|
119
122
|
if (lastSelection !== null) {
|
|
120
123
|
if (linkUrl !== "") {
|
|
121
124
|
editor.dispatchCommand(TOGGLE_LINK_COMMAND, sanitizeUrl(editedLinkUrl));
|
|
125
|
+
editor.update(() => {
|
|
126
|
+
const selection = $getSelection();
|
|
127
|
+
if ($isRangeSelection(selection)) {
|
|
128
|
+
const parent = getSelectedNode(selection).getParent();
|
|
129
|
+
if ($isAutoLinkNode(parent)) {
|
|
130
|
+
const linkNode = $createLinkNode(parent.getURL(), {
|
|
131
|
+
rel: parent.__rel,
|
|
132
|
+
target: parent.__target,
|
|
133
|
+
title: parent.__title,
|
|
134
|
+
});
|
|
135
|
+
parent.replace(linkNode, true);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
122
139
|
}
|
|
123
140
|
setEditedLinkUrl("https://");
|
|
124
141
|
setIsLinkEditMode(false);
|
|
@@ -9,7 +9,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
9
9
|
import { $isAutoLinkNode, $isLinkNode } from "@lexical/link";
|
|
10
10
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
11
11
|
import { $findMatchingParent, mergeRegister } from "@lexical/utils";
|
|
12
|
-
import { $getSelection, $isRangeSelection, CLICK_COMMAND, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_LOW, SELECTION_CHANGE_COMMAND, } from "lexical";
|
|
12
|
+
import { $getSelection, $isLineBreakNode, $isRangeSelection, CLICK_COMMAND, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_LOW, SELECTION_CHANGE_COMMAND, } from "lexical";
|
|
13
13
|
import { useEffect, useState } from "react";
|
|
14
14
|
import { createPortal } from "react-dom";
|
|
15
15
|
import { getSelectedNode } from "../../utils/getSelectedNode";
|
|
@@ -18,14 +18,30 @@ function useFloatingLinkEditorToolbar(editor, anchorElem, isLinkEditMode, setIsL
|
|
|
18
18
|
const [activeEditor, setActiveEditor] = useState(editor);
|
|
19
19
|
const [isLink, setIsLink] = useState(false);
|
|
20
20
|
useEffect(() => {
|
|
21
|
-
function updateToolbar() {
|
|
21
|
+
function $updateToolbar() {
|
|
22
22
|
const selection = $getSelection();
|
|
23
23
|
if ($isRangeSelection(selection)) {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const focusNode = getSelectedNode(selection);
|
|
25
|
+
const focusLinkNode = $findMatchingParent(focusNode, $isLinkNode);
|
|
26
|
+
const focusAutoLinkNode = $findMatchingParent(focusNode, $isAutoLinkNode);
|
|
27
|
+
if (!(focusLinkNode || focusAutoLinkNode)) {
|
|
28
|
+
setIsLink(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const badNode = selection
|
|
32
|
+
.getNodes()
|
|
33
|
+
.filter((node) => !$isLineBreakNode(node))
|
|
34
|
+
.find((node) => {
|
|
35
|
+
const linkNode = $findMatchingParent(node, $isLinkNode);
|
|
36
|
+
const autoLinkNode = $findMatchingParent(node, $isAutoLinkNode);
|
|
37
|
+
return ((focusLinkNode && !focusLinkNode.is(linkNode)) ||
|
|
38
|
+
(linkNode && !linkNode.is(focusLinkNode)) ||
|
|
39
|
+
(focusAutoLinkNode && !focusAutoLinkNode.is(autoLinkNode)) ||
|
|
40
|
+
(autoLinkNode &&
|
|
41
|
+
(!autoLinkNode.is(focusAutoLinkNode) ||
|
|
42
|
+
autoLinkNode.getIsUnlinked())));
|
|
43
|
+
});
|
|
44
|
+
if (!badNode) {
|
|
29
45
|
setIsLink(true);
|
|
30
46
|
}
|
|
31
47
|
else {
|
|
@@ -35,10 +51,10 @@ function useFloatingLinkEditorToolbar(editor, anchorElem, isLinkEditMode, setIsL
|
|
|
35
51
|
}
|
|
36
52
|
return mergeRegister(editor.registerUpdateListener(({ editorState }) => {
|
|
37
53
|
editorState.read(() => {
|
|
38
|
-
updateToolbar();
|
|
54
|
+
$updateToolbar();
|
|
39
55
|
});
|
|
40
56
|
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, (_payload, newEditor) => {
|
|
41
|
-
updateToolbar();
|
|
57
|
+
$updateToolbar();
|
|
42
58
|
setActiveEditor(newEditor);
|
|
43
59
|
return false;
|
|
44
60
|
}, COMMAND_PRIORITY_CRITICAL), editor.registerCommand(CLICK_COMMAND, (payload) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-monolith/cds",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.71.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"@emotion/css": "^11.11.2",
|
|
9
9
|
"@emotion/react": "^11.8.2",
|
|
10
10
|
"@emotion/styled": "^11.8.1",
|
|
11
|
-
"@lexical/react": "^0.
|
|
11
|
+
"@lexical/react": "^0.17.1",
|
|
12
12
|
"@mui/material": "^5.13.6",
|
|
13
13
|
"@types/node": "^16.11.26",
|
|
14
14
|
"@types/react": "^18.2.28",
|
|
15
15
|
"@types/react-dom": "^18.2.13",
|
|
16
16
|
"framer-motion": "^11.3.19",
|
|
17
17
|
"hex-to-css-filter": "^5.4.0",
|
|
18
|
-
"lexical": "^0.
|
|
18
|
+
"lexical": "^0.17.1",
|
|
19
19
|
"lodash": "^4.17.21",
|
|
20
20
|
"react": "^18.2.0",
|
|
21
21
|
"react-dom": "^18.2.0",
|