@selfcommunity/react-ui 0.7.0-alpha.313 → 0.7.0-alpha.315
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/cjs/components/Editor/nodes/ImageNode.d.ts +8 -11
- package/lib/cjs/components/Editor/nodes/ImageNode.js +41 -316
- package/lib/cjs/components/Editor/plugins/ImagePlugin.js +9 -4
- package/lib/cjs/components/Feed/Feed.js +3 -2
- package/lib/esm/components/Editor/nodes/ImageNode.d.ts +8 -11
- package/lib/esm/components/Editor/nodes/ImageNode.js +43 -318
- package/lib/esm/components/Editor/plugins/ImagePlugin.js +9 -4
- package/lib/esm/components/Feed/Feed.js +4 -3
- package/lib/umd/react-ui.js +1 -1
- package/package.json +5 -5
|
@@ -1,251 +1,10 @@
|
|
|
1
|
-
import React, { Suspense, useCallback, useEffect, useRef
|
|
2
|
-
import { $getNodeByKey, $getSelection, $isNodeSelection, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, DecoratorNode,
|
|
1
|
+
import React, { Suspense, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import { $getNodeByKey, $getSelection, $isNodeSelection, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, DecoratorNode, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND, TextNode, } from 'lexical';
|
|
3
3
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
4
4
|
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
|
|
5
5
|
import { mergeRegister } from '@lexical/utils';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { Box, Button, InputAdornment, Paper, TextField } from '@mui/material';
|
|
9
|
-
const PREFIX = 'SCEditorImagePluginImageEdit';
|
|
10
|
-
const classes = {
|
|
11
|
-
root: `${PREFIX}-root`
|
|
12
|
-
};
|
|
13
|
-
const Root = styled(Box, {
|
|
14
|
-
name: PREFIX,
|
|
15
|
-
slot: 'Root',
|
|
16
|
-
overridesResolver: (props, styles) => styles.root
|
|
17
|
-
})(({ theme }) => ({
|
|
18
|
-
position: 'absolute',
|
|
19
|
-
top: 0,
|
|
20
|
-
left: 0,
|
|
21
|
-
zIndex: 2000,
|
|
22
|
-
outline: `2px solid ${theme.palette.primary.main}`,
|
|
23
|
-
userSelect: 'none',
|
|
24
|
-
display: 'flex',
|
|
25
|
-
justifyContent: 'center',
|
|
26
|
-
alignItems: 'center',
|
|
27
|
-
'& .MuiPaper-root': {
|
|
28
|
-
padding: theme.spacing(1),
|
|
29
|
-
display: 'flex',
|
|
30
|
-
flexDirection: 'column',
|
|
31
|
-
justifyContent: 'center',
|
|
32
|
-
alignItems: 'center',
|
|
33
|
-
width: '50%',
|
|
34
|
-
backgroundColor: theme.palette.background.paper,
|
|
35
|
-
'& > *': {
|
|
36
|
-
margin: theme.spacing(1)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}));
|
|
40
|
-
const ImageEdit = ({ onResize, onDelete, imageRef }) => {
|
|
41
|
-
// STATE
|
|
42
|
-
const [width, setWidth] = useState(imageRef.current.width);
|
|
43
|
-
const [height, setHeight] = useState(imageRef.current.height);
|
|
44
|
-
const [openResizePanel, setOpenResizePanel] = useState(false);
|
|
45
|
-
// HANDLERS
|
|
46
|
-
const handleStopPropagation = (event) => {
|
|
47
|
-
event.preventDefault();
|
|
48
|
-
event.stopPropagation();
|
|
49
|
-
return false;
|
|
50
|
-
};
|
|
51
|
-
const handleOpenResizePanel = (event) => {
|
|
52
|
-
event.preventDefault();
|
|
53
|
-
event.stopPropagation();
|
|
54
|
-
setOpenResizePanel(true);
|
|
55
|
-
return false;
|
|
56
|
-
};
|
|
57
|
-
const handleChangeWidth = (event) => {
|
|
58
|
-
setWidth(event.target.value);
|
|
59
|
-
};
|
|
60
|
-
const handleChangeHeight = (event) => {
|
|
61
|
-
setHeight(event.target.value);
|
|
62
|
-
};
|
|
63
|
-
const handleResize = (event) => {
|
|
64
|
-
onResize(width, height);
|
|
65
|
-
setOpenResizePanel(false);
|
|
66
|
-
};
|
|
67
|
-
// RENDER
|
|
68
|
-
console.log(width);
|
|
69
|
-
return (React.createElement(Root, { className: classes.root, style: { width: imageRef.current.width, height: imageRef.current.height }, contentEditable: false, onClick: handleStopPropagation },
|
|
70
|
-
React.createElement(Paper, null, openResizePanel ? (React.createElement(React.Fragment, null,
|
|
71
|
-
React.createElement(TextField, { value: width, type: "number", onChange: handleChangeWidth, label: React.createElement(FormattedMessage, { id: "ui.editor.imagePluginEdit.width", defaultMessage: "ui.editor.imagePluginEdit.width" }), InputProps: {
|
|
72
|
-
endAdornment: React.createElement(InputAdornment, { position: "start" }, "px")
|
|
73
|
-
} }),
|
|
74
|
-
React.createElement(TextField, { value: height, type: "number", onChange: handleChangeHeight, label: React.createElement(FormattedMessage, { id: "ui.editor.imagePluginEdit.height", defaultMessage: "ui.editor.imagePluginEdit.height" }), InputProps: {
|
|
75
|
-
endAdornment: React.createElement(InputAdornment, { position: "start" }, "px")
|
|
76
|
-
} }),
|
|
77
|
-
React.createElement(Button, { variant: "contained", color: "primary", disabled: width === imageRef.current.width && height === imageRef.current.height, onClick: handleResize },
|
|
78
|
-
React.createElement(FormattedMessage, { id: "ui.editor.imagePluginEdit.resize", defaultMessage: "ui.editor.imagePluginEdit.resize" })))) : (React.createElement(React.Fragment, null,
|
|
79
|
-
React.createElement(Button, { variant: "contained", color: "primary", onClick: handleOpenResizePanel },
|
|
80
|
-
React.createElement(FormattedMessage, { id: "ui.editor.imagePluginEdit.resize", defaultMessage: "ui.editor.imagePluginEdit.resize" })),
|
|
81
|
-
React.createElement(Button, { variant: "contained", color: "secondary", onClick: onDelete },
|
|
82
|
-
React.createElement(FormattedMessage, { id: "ui.editor.imagePluginEdit.delete", defaultMessage: "ui.editor.imagePluginEdit.delete" })))))));
|
|
83
|
-
};
|
|
84
|
-
function clamp(value, min, max) {
|
|
85
|
-
return Math.min(Math.max(value, min), Number(max));
|
|
86
|
-
}
|
|
87
|
-
const Direction = {
|
|
88
|
-
east: 1 << 0,
|
|
89
|
-
north: 1 << 3,
|
|
90
|
-
south: 1 << 1,
|
|
91
|
-
west: 1 << 2
|
|
92
|
-
};
|
|
93
|
-
function ImageResizer({ onResizeStart, onResizeEnd, imageRef, maxWidth, editor }) {
|
|
94
|
-
const controlWrapperRef = useRef(null);
|
|
95
|
-
const userSelect = useRef({
|
|
96
|
-
priority: '',
|
|
97
|
-
value: 'default'
|
|
98
|
-
});
|
|
99
|
-
const positioningRef = useRef({
|
|
100
|
-
currentHeight: 0,
|
|
101
|
-
currentWidth: 0,
|
|
102
|
-
direction: 0,
|
|
103
|
-
isResizing: false,
|
|
104
|
-
ratio: 0,
|
|
105
|
-
startHeight: 0,
|
|
106
|
-
startWidth: 0,
|
|
107
|
-
startX: 0,
|
|
108
|
-
startY: 0
|
|
109
|
-
});
|
|
110
|
-
const editorRootElement = editor.getRootElement();
|
|
111
|
-
// Find max width, accounting for editor padding.
|
|
112
|
-
const maxWidthContainer = maxWidth ? maxWidth : editorRootElement !== null ? editorRootElement.getBoundingClientRect().width - 20 : 100;
|
|
113
|
-
const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 : 100;
|
|
114
|
-
const minWidth = 100;
|
|
115
|
-
const minHeight = 100;
|
|
116
|
-
const setStartCursor = (direction) => {
|
|
117
|
-
const ew = direction === Direction.east || direction === Direction.west;
|
|
118
|
-
const ns = direction === Direction.north || direction === Direction.south;
|
|
119
|
-
const nwse = (direction & Direction.north && direction & Direction.west) || (direction & Direction.south && direction & Direction.east);
|
|
120
|
-
const cursorDir = ew ? 'ew' : ns ? 'ns' : nwse ? 'nwse' : 'nesw';
|
|
121
|
-
if (editorRootElement !== null) {
|
|
122
|
-
editorRootElement.style.setProperty('cursor', `${cursorDir}-resize`, 'important');
|
|
123
|
-
}
|
|
124
|
-
if (document.body !== null) {
|
|
125
|
-
document.body.style.setProperty('cursor', `${cursorDir}-resize`, 'important');
|
|
126
|
-
userSelect.current.value = document.body.style.getPropertyValue('-webkit-user-select');
|
|
127
|
-
userSelect.current.priority = document.body.style.getPropertyPriority('-webkit-user-select');
|
|
128
|
-
document.body.style.setProperty('-webkit-user-select', `none`, 'important');
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
const setEndCursor = () => {
|
|
132
|
-
if (editorRootElement !== null) {
|
|
133
|
-
editorRootElement.style.setProperty('cursor', 'text');
|
|
134
|
-
}
|
|
135
|
-
if (document.body !== null) {
|
|
136
|
-
document.body.style.setProperty('cursor', 'default');
|
|
137
|
-
document.body.style.setProperty('-webkit-user-select', userSelect.current.value, userSelect.current.priority);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const handlePointerDown = (event, direction) => {
|
|
141
|
-
if (!editor.isEditable()) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const image = imageRef.current;
|
|
145
|
-
const controlWrapper = controlWrapperRef.current;
|
|
146
|
-
if (image !== null && controlWrapper !== null) {
|
|
147
|
-
const { width, height } = image.getBoundingClientRect();
|
|
148
|
-
const positioning = positioningRef.current;
|
|
149
|
-
positioning.startWidth = width;
|
|
150
|
-
positioning.startHeight = height;
|
|
151
|
-
positioning.ratio = width / height;
|
|
152
|
-
positioning.currentWidth = width;
|
|
153
|
-
positioning.currentHeight = height;
|
|
154
|
-
positioning.startX = event.clientX;
|
|
155
|
-
positioning.startY = event.clientY;
|
|
156
|
-
positioning.isResizing = true;
|
|
157
|
-
positioning.direction = direction;
|
|
158
|
-
setStartCursor(direction);
|
|
159
|
-
onResizeStart();
|
|
160
|
-
controlWrapper.classList.add('image-control-wrapper--resizing');
|
|
161
|
-
image.style.height = `${height}px`;
|
|
162
|
-
image.style.width = `${width}px`;
|
|
163
|
-
document.addEventListener('pointermove', handlePointerMove);
|
|
164
|
-
document.addEventListener('pointerup', handlePointerUp);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
const handlePointerMove = (event) => {
|
|
168
|
-
const image = imageRef.current;
|
|
169
|
-
const positioning = positioningRef.current;
|
|
170
|
-
const isHorizontal = positioning.direction & (Direction.east | Direction.west);
|
|
171
|
-
const isVertical = positioning.direction & (Direction.south | Direction.north);
|
|
172
|
-
if (image !== null && positioning.isResizing) {
|
|
173
|
-
// Corner cursor
|
|
174
|
-
if (isHorizontal && isVertical) {
|
|
175
|
-
let diff = Math.floor(positioning.startX - event.clientX);
|
|
176
|
-
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
177
|
-
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
|
|
178
|
-
const height = width / positioning.ratio;
|
|
179
|
-
image.style.width = `${width}px`;
|
|
180
|
-
image.style.height = `${height}px`;
|
|
181
|
-
positioning.currentHeight = height;
|
|
182
|
-
positioning.currentWidth = width;
|
|
183
|
-
}
|
|
184
|
-
else if (isVertical) {
|
|
185
|
-
let diff = Math.floor(positioning.startY - event.clientY);
|
|
186
|
-
diff = positioning.direction & Direction.south ? -diff : diff;
|
|
187
|
-
const height = clamp(positioning.startHeight + diff, minHeight, maxHeightContainer);
|
|
188
|
-
image.style.height = `${height}px`;
|
|
189
|
-
positioning.currentHeight = height;
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
let diff = Math.floor(positioning.startX - event.clientX);
|
|
193
|
-
diff = positioning.direction & Direction.east ? -diff : diff;
|
|
194
|
-
const width = clamp(positioning.startWidth + diff, minWidth, maxWidthContainer);
|
|
195
|
-
image.style.width = `${width}px`;
|
|
196
|
-
positioning.currentWidth = width;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
const handlePointerUp = () => {
|
|
201
|
-
const image = imageRef.current;
|
|
202
|
-
const positioning = positioningRef.current;
|
|
203
|
-
const controlWrapper = controlWrapperRef.current;
|
|
204
|
-
if (image !== null && controlWrapper !== null && positioning.isResizing) {
|
|
205
|
-
const width = positioning.currentWidth;
|
|
206
|
-
const height = positioning.currentHeight;
|
|
207
|
-
positioning.startWidth = 0;
|
|
208
|
-
positioning.startHeight = 0;
|
|
209
|
-
positioning.ratio = 0;
|
|
210
|
-
positioning.startX = 0;
|
|
211
|
-
positioning.startY = 0;
|
|
212
|
-
positioning.currentWidth = 0;
|
|
213
|
-
positioning.currentHeight = 0;
|
|
214
|
-
positioning.isResizing = false;
|
|
215
|
-
controlWrapper.classList.remove('image-control-wrapper--resizing');
|
|
216
|
-
setEndCursor();
|
|
217
|
-
console.log(width);
|
|
218
|
-
console.log(height);
|
|
219
|
-
onResizeEnd(width, height);
|
|
220
|
-
document.removeEventListener('pointermove', handlePointerMove);
|
|
221
|
-
document.removeEventListener('pointerup', handlePointerUp);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
return (React.createElement("div", { ref: controlWrapperRef },
|
|
225
|
-
React.createElement("div", { className: "image-resizer image-resizer-n", onPointerDown: (event) => {
|
|
226
|
-
handlePointerDown(event, Direction.north);
|
|
227
|
-
} }),
|
|
228
|
-
React.createElement("div", { className: "image-resizer image-resizer-ne", onPointerDown: (event) => {
|
|
229
|
-
handlePointerDown(event, Direction.north | Direction.east);
|
|
230
|
-
} }),
|
|
231
|
-
React.createElement("div", { className: "image-resizer image-resizer-e", onPointerDown: (event) => {
|
|
232
|
-
handlePointerDown(event, Direction.east);
|
|
233
|
-
} }),
|
|
234
|
-
React.createElement("div", { className: "image-resizer image-resizer-se", onPointerDown: (event) => {
|
|
235
|
-
handlePointerDown(event, Direction.south | Direction.east);
|
|
236
|
-
} }),
|
|
237
|
-
React.createElement("div", { className: "image-resizer image-resizer-s", onPointerDown: (event) => {
|
|
238
|
-
handlePointerDown(event, Direction.south);
|
|
239
|
-
} }),
|
|
240
|
-
React.createElement("div", { className: "image-resizer image-resizer-sw", onPointerDown: (event) => {
|
|
241
|
-
handlePointerDown(event, Direction.south | Direction.west);
|
|
242
|
-
} }),
|
|
243
|
-
React.createElement("div", { className: "image-resizer image-resizer-w", onPointerDown: (event) => {
|
|
244
|
-
handlePointerDown(event, Direction.west);
|
|
245
|
-
} }),
|
|
246
|
-
React.createElement("div", { className: "image-resizer image-resizer-nw", onPointerDown: (event) => {
|
|
247
|
-
handlePointerDown(event, Direction.north | Direction.west);
|
|
248
|
-
} })));
|
|
6
|
+
function getAspectRatio(width, height) {
|
|
7
|
+
return width / height;
|
|
249
8
|
}
|
|
250
9
|
const imageCache = new Set();
|
|
251
10
|
function useSuspenseImage(src) {
|
|
@@ -260,21 +19,21 @@ function useSuspenseImage(src) {
|
|
|
260
19
|
});
|
|
261
20
|
}
|
|
262
21
|
}
|
|
263
|
-
function LazyImage({ altText, className, imageRef, src, width, height
|
|
22
|
+
function LazyImage({ altText, className, imageRef, src, width, height }) {
|
|
264
23
|
useSuspenseImage(src);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
24
|
+
const aspectRatio = getAspectRatio(width, height);
|
|
25
|
+
return (React.createElement("div", { draggable: false, className: className, style: { position: 'relative', paddingBottom: `${100 / aspectRatio}%` } },
|
|
26
|
+
React.createElement("img", { src: src, alt: altText, ref: imageRef, style: {
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
height: `100%`,
|
|
29
|
+
width: `100%`
|
|
30
|
+
} })));
|
|
270
31
|
}
|
|
271
|
-
function ImageComponent({ src, altText, nodeKey, width, height
|
|
32
|
+
function ImageComponent({ src, altText, nodeKey, width, height }) {
|
|
272
33
|
const imageRef = useRef(null);
|
|
273
34
|
const buttonRef = useRef(null);
|
|
274
35
|
const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
|
|
275
|
-
const [isResizing, setIsResizing] = useState(false);
|
|
276
36
|
const [editor] = useLexicalComposerContext();
|
|
277
|
-
const [selection, setSelection] = useState(null);
|
|
278
37
|
const activeEditorRef = useRef(null);
|
|
279
38
|
const onDelete = useCallback((payload) => {
|
|
280
39
|
if (isSelected && $isNodeSelection($getSelection())) {
|
|
@@ -315,19 +74,11 @@ function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, resiza
|
|
|
315
74
|
return false;
|
|
316
75
|
}, [editor, setSelected]);
|
|
317
76
|
useEffect(() => {
|
|
318
|
-
|
|
319
|
-
const unregister = mergeRegister(editor.registerUpdateListener(({ editorState }) => {
|
|
320
|
-
if (isMounted) {
|
|
321
|
-
setSelection(editorState.read(() => $getSelection()));
|
|
322
|
-
}
|
|
323
|
-
}), editor.registerCommand(SELECTION_CHANGE_COMMAND, (_, activeEditor) => {
|
|
77
|
+
const unregister = mergeRegister(editor.registerCommand(SELECTION_CHANGE_COMMAND, (_, activeEditor) => {
|
|
324
78
|
activeEditorRef.current = activeEditor;
|
|
325
79
|
return false;
|
|
326
80
|
}, COMMAND_PRIORITY_LOW), editor.registerCommand(CLICK_COMMAND, (payload) => {
|
|
327
81
|
const event = payload;
|
|
328
|
-
if (isResizing) {
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
82
|
if (event.target === imageRef.current) {
|
|
332
83
|
if (event.shiftKey) {
|
|
333
84
|
setSelected(!isSelected);
|
|
@@ -339,63 +90,36 @@ function ImageComponent({ src, altText, nodeKey, width, height, maxWidth, resiza
|
|
|
339
90
|
return true;
|
|
340
91
|
}
|
|
341
92
|
return false;
|
|
342
|
-
}, COMMAND_PRIORITY_LOW), editor.registerCommand(DRAGSTART_COMMAND, (event) => {
|
|
343
|
-
if (event.target === imageRef.current) {
|
|
344
|
-
// TODO This is just a temporary workaround for FF to behave like other browsers.
|
|
345
|
-
// Ideally, this handles drag & drop too (and all browsers).
|
|
346
|
-
event.preventDefault();
|
|
347
|
-
return true;
|
|
348
|
-
}
|
|
349
|
-
return false;
|
|
350
93
|
}, 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));
|
|
351
94
|
return () => {
|
|
352
|
-
isMounted = false;
|
|
353
95
|
unregister();
|
|
354
96
|
};
|
|
355
|
-
}, [clearSelection, editor,
|
|
356
|
-
const
|
|
357
|
-
// Delay hiding the resize bars for click case
|
|
358
|
-
setTimeout(() => {
|
|
359
|
-
setIsResizing(false);
|
|
360
|
-
}, 200);
|
|
361
|
-
editor.update(() => {
|
|
362
|
-
const node = $getNodeByKey(nodeKey);
|
|
363
|
-
if ($isImageNode(node)) {
|
|
364
|
-
node.setWidthAndHeight(nextWidth, nextHeight);
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
};
|
|
368
|
-
const onResizeStart = () => {
|
|
369
|
-
setIsResizing(true);
|
|
370
|
-
};
|
|
371
|
-
const draggable = isSelected && $isNodeSelection(selection) && !isResizing;
|
|
372
|
-
const isFocused = isSelected || isResizing;
|
|
97
|
+
}, [clearSelection, editor, isSelected, nodeKey, onDelete, onEnter, onEscape, setSelected]);
|
|
98
|
+
const isFocused = isSelected;
|
|
373
99
|
return (React.createElement(Suspense, { fallback: null },
|
|
374
|
-
React.createElement(
|
|
375
|
-
React.createElement("div", { draggable: draggable },
|
|
376
|
-
React.createElement(LazyImage, { className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height, maxWidth: maxWidth })),
|
|
377
|
-
resizable && $isNodeSelection(selection) && isFocused && (React.createElement(ImageResizer, { editor: editor, imageRef: imageRef, maxWidth: typeof maxWidth === 'number' ? maxWidth : null, onResizeStart: onResizeStart, onResizeEnd: onResizeEnd })))));
|
|
100
|
+
React.createElement(LazyImage, { className: isFocused ? `focused` : null, src: src, altText: altText, imageRef: imageRef, width: width, height: height })));
|
|
378
101
|
}
|
|
379
102
|
function convertImageElement(domNode) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
node
|
|
383
|
-
|
|
103
|
+
if (domNode instanceof HTMLImageElement) {
|
|
104
|
+
const { alt: altText, src, dataset: { width, height } } = domNode;
|
|
105
|
+
const node = $createImageNode({ altText, height: Number(height), src, width: Number(width) });
|
|
106
|
+
return { node };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
384
109
|
}
|
|
385
110
|
export class ImageNode extends DecoratorNode {
|
|
386
|
-
constructor(src, altText,
|
|
111
|
+
constructor(src, altText, width, height, key) {
|
|
387
112
|
super(key);
|
|
388
113
|
this.__src = src;
|
|
389
114
|
this.__altText = altText;
|
|
390
|
-
this.
|
|
391
|
-
this.
|
|
392
|
-
this.__height = height || 'inherit';
|
|
115
|
+
this.__width = width;
|
|
116
|
+
this.__height = height;
|
|
393
117
|
}
|
|
394
118
|
static getType() {
|
|
395
119
|
return 'image';
|
|
396
120
|
}
|
|
397
121
|
static clone(node) {
|
|
398
|
-
return new ImageNode(node.__src, node.__altText, node.
|
|
122
|
+
return new ImageNode(node.__src, node.__altText, node.__width, node.__height, node.__key);
|
|
399
123
|
}
|
|
400
124
|
setWidthAndHeight(width, height) {
|
|
401
125
|
const writable = this.getWritable();
|
|
@@ -429,24 +153,26 @@ export class ImageNode extends DecoratorNode {
|
|
|
429
153
|
}) }, dom);
|
|
430
154
|
}
|
|
431
155
|
exportDOM() {
|
|
432
|
-
const
|
|
433
|
-
element.
|
|
434
|
-
element.setAttribute('
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
156
|
+
const aspectRatio = getAspectRatio(this.__width, this.__height);
|
|
157
|
+
const element = document.createElement('div');
|
|
158
|
+
element.setAttribute('style', `position: relative;padding-bottom:${100 / aspectRatio}%`);
|
|
159
|
+
const image = document.createElement('img');
|
|
160
|
+
image.setAttribute('src', this.__src);
|
|
161
|
+
image.setAttribute('alt', this.__altText);
|
|
162
|
+
image.setAttribute('style', `position: absolute;width:100%;height:100%;`);
|
|
163
|
+
image.setAttribute('data-width', `${this.__width}`);
|
|
164
|
+
image.setAttribute('data-height', `${this.__height}`);
|
|
165
|
+
element.appendChild(image);
|
|
438
166
|
return { element };
|
|
439
167
|
}
|
|
440
168
|
decorate() {
|
|
441
|
-
|
|
442
|
-
return (React.createElement(ImageComponent, { src: this.__src, altText: this.__altText, width: this.__width, height: this.__height, maxWidth: this.__maxWidth, nodeKey: this.getKey(), resizable: true }));
|
|
169
|
+
return (React.createElement(ImageComponent, { src: this.__src, altText: this.__altText, width: this.__width, height: this.__height, nodeKey: this.getKey() }));
|
|
443
170
|
}
|
|
444
171
|
static importJSON(serializedNode) {
|
|
445
|
-
const { altText, height, width,
|
|
172
|
+
const { altText, height, width, src } = serializedNode;
|
|
446
173
|
const node = $createImageNode({
|
|
447
174
|
altText,
|
|
448
175
|
height,
|
|
449
|
-
maxWidth,
|
|
450
176
|
src,
|
|
451
177
|
width
|
|
452
178
|
});
|
|
@@ -455,17 +181,16 @@ export class ImageNode extends DecoratorNode {
|
|
|
455
181
|
exportJSON() {
|
|
456
182
|
return {
|
|
457
183
|
altText: this.getAltText(),
|
|
458
|
-
height: this.__height
|
|
459
|
-
maxWidth: this.__maxWidth,
|
|
184
|
+
height: this.__height,
|
|
460
185
|
src: this.getSrc(),
|
|
461
186
|
type: 'image',
|
|
462
187
|
version: 1,
|
|
463
|
-
width: this.__width
|
|
188
|
+
width: this.__width
|
|
464
189
|
};
|
|
465
190
|
}
|
|
466
191
|
}
|
|
467
|
-
export function $createImageNode({ src, altText,
|
|
468
|
-
return new ImageNode(src, altText,
|
|
192
|
+
export function $createImageNode({ src, altText, width, height }) {
|
|
193
|
+
return new ImageNode(src, altText, width, height);
|
|
469
194
|
}
|
|
470
195
|
export function $isImageNode(node) {
|
|
471
196
|
return node.getType() === 'image';
|
|
@@ -29,12 +29,17 @@ function Image({ editor, className = '' }) {
|
|
|
29
29
|
return file.type.startsWith('image/');
|
|
30
30
|
};
|
|
31
31
|
const handleUploadSuccess = (media) => {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
src: media.image,
|
|
32
|
+
const image = media.image_thumbnail ? media.image_thumbnail : {
|
|
33
|
+
url: media.image,
|
|
35
34
|
width: media.image_width,
|
|
36
35
|
height: media.image_height
|
|
37
36
|
};
|
|
37
|
+
const data = {
|
|
38
|
+
altText: media.title,
|
|
39
|
+
src: image.url,
|
|
40
|
+
width: image.width,
|
|
41
|
+
height: image.height
|
|
42
|
+
};
|
|
38
43
|
editor.focus();
|
|
39
44
|
editor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
|
|
40
45
|
editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);
|
|
@@ -86,7 +91,7 @@ export default function ImagePlugin() {
|
|
|
86
91
|
if ($isRootNode(selection.anchor.getNode())) {
|
|
87
92
|
selection.insertParagraph();
|
|
88
93
|
}
|
|
89
|
-
const imageNode = $createImageNode({ src: payload.src, altText: payload.altText,
|
|
94
|
+
const imageNode = $createImageNode({ src: payload.src, altText: payload.altText, width: payload.width, height: payload.height });
|
|
90
95
|
selection.insertNodes([imageNode]);
|
|
91
96
|
}
|
|
92
97
|
return true;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Link, SCCache, SCPreferences, SCPreferencesContext, SCUserContext, useSCFetchFeed, usePreviousValue, useIsComponentMountedRef } from '@selfcommunity/react-core';
|
|
4
4
|
import { styled, useTheme } from '@mui/material/styles';
|
|
5
|
-
import { Box, CardContent, Grid, Hidden, useMediaQuery } from
|
|
5
|
+
import { Box, CardContent, Grid, Hidden, useMediaQuery } from '@mui/material';
|
|
6
6
|
import { FormattedMessage } from 'react-intl';
|
|
7
7
|
import { GenericSkeleton } from '../Skeleton';
|
|
8
8
|
import CustomAdv from '../CustomAdv';
|
|
@@ -25,6 +25,7 @@ const PREFIX = 'SCFeed';
|
|
|
25
25
|
const classes = {
|
|
26
26
|
root: `${PREFIX}-root`,
|
|
27
27
|
left: `${PREFIX}-left`,
|
|
28
|
+
leftItems: `${PREFIX}-left-items`,
|
|
28
29
|
start: `${PREFIX}-start`,
|
|
29
30
|
end: `${PREFIX}-end`,
|
|
30
31
|
endMessage: `${PREFIX}-end-message`,
|
|
@@ -411,14 +412,14 @@ const Feed = (inProps, ref) => {
|
|
|
411
412
|
React.createElement(CustomAdv, Object.assign({ position: SCCustomAdvPosition.POSITION_BELOW_TOPBAR }, CustomAdvProps)))) : null,
|
|
412
413
|
React.createElement(Grid, { item: true, xs: 12, md: 7 },
|
|
413
414
|
React.createElement("div", { style: { overflow: 'visible' } },
|
|
414
|
-
React.createElement(InfiniteScroll, { className: classes.left, dataLength: feedDataLeft.length, next: getNextPage, previous: getPreviousPage, hasMoreNext: Boolean(feedDataObject.next), hasMorePrevious: Boolean(feedDataObject.previous), header: PreviousPageLink, footer: NextPageLink, loaderNext: React.createElement(ItemSkeleton, Object.assign({}, ItemSkeletonProps)), loaderPrevious: React.createElement(ItemSkeleton, Object.assign({}, ItemSkeletonProps)), scrollThreshold:
|
|
415
|
+
React.createElement(InfiniteScroll, { className: classes.left, dataLength: feedDataLeft.length, next: getNextPage, previous: getPreviousPage, hasMoreNext: Boolean(feedDataObject.next), hasMorePrevious: Boolean(feedDataObject.previous), header: PreviousPageLink, footer: NextPageLink, loaderNext: React.createElement(ItemSkeleton, Object.assign({}, ItemSkeletonProps)), loaderPrevious: React.createElement(ItemSkeleton, Object.assign({}, ItemSkeletonProps)), scrollThreshold: '80%', endMessage: React.createElement(Box, { className: classes.end },
|
|
415
416
|
React.createElement(Widget, { className: classes.endMessage },
|
|
416
417
|
React.createElement(CardContent, null, endMessage)),
|
|
417
418
|
advEnabled && !hideAdvs && enabledCustomAdvPositions.includes(SCCustomAdvPosition.POSITION_ABOVE_FOOTER_BAR) ? (React.createElement(CustomAdv, Object.assign({ position: SCCustomAdvPosition.POSITION_ABOVE_FOOTER_BAR }, CustomAdvProps))) : null,
|
|
418
419
|
FooterComponent ? React.createElement(FooterComponent, Object.assign({}, FooterComponentProps)) : null), refreshFunction: refresh, pullDownToRefresh: true, pullDownToRefreshThreshold: 1000, pullDownToRefreshContent: null, releaseToRefreshContent: React.createElement(Widget, { variant: "outlined", className: classes.refresh },
|
|
419
420
|
React.createElement(CardContent, null, refreshMessage)), style: { overflow: 'visible' } },
|
|
420
421
|
renderHeaderComponent(),
|
|
421
|
-
React.createElement(VirtualizedScroller, Object.assign({ items: feedDataLeft, itemComponent: InnerItem, onMount: onScrollerMount, onScrollerStateChange: onScrollerStateChange, getItemId: getScrollItemId, preserveScrollPosition: true, preserveScrollPositionOnPrependItems: true, cacheScrollStateKey: SCCache.getVirtualizedScrollStateCacheKey(id), cacheScrollerPositionKey: SCCache.getFeedSPCacheKey(id), cacheStrategy: cacheStrategy }, VirtualizedScrollerProps))))),
|
|
422
|
+
React.createElement(VirtualizedScroller, Object.assign({ className: classes.leftItems, items: feedDataLeft, itemComponent: InnerItem, onMount: onScrollerMount, onScrollerStateChange: onScrollerStateChange, getItemId: getScrollItemId, preserveScrollPosition: true, preserveScrollPositionOnPrependItems: true, cacheScrollStateKey: SCCache.getVirtualizedScrollStateCacheKey(id), cacheScrollerPositionKey: SCCache.getFeedSPCacheKey(id), cacheStrategy: cacheStrategy }, VirtualizedScrollerProps))))),
|
|
422
423
|
feedDataRight.length > 0 && !hideAdvs && (React.createElement(Hidden, { smDown: true },
|
|
423
424
|
React.createElement(Grid, { item: true, xs: 12, md: 5 },
|
|
424
425
|
React.createElement(StickyBoxComp, Object.assign({ className: classes.right }, FeedSidebarProps),
|