@squiz/formatted-text-editor 1.21.1-alpha.34 → 1.21.1-alpha.39
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/demo/App.tsx +9 -2
- package/lib/Editor/EditorContext.d.ts +6 -1
- package/lib/Editor/EditorContext.js +1 -1
- package/lib/EditorToolbar/FloatingToolbar.js +1 -1
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.d.ts +9 -8
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.js +71 -43
- package/lib/EditorToolbar/Tools/Image/ImageButton.js +13 -15
- package/lib/EditorToolbar/Tools/Image/ImageModal.js +7 -1
- package/lib/EditorToolbar/Tools/Link/Form/LinkForm.js +1 -1
- package/lib/Extensions/Extensions.d.ts +5 -0
- package/lib/Extensions/Extensions.js +12 -1
- package/lib/Extensions/ImageExtension/AssetImageExtension.d.ts +17 -0
- package/lib/Extensions/ImageExtension/AssetImageExtension.js +92 -0
- package/lib/Extensions/ImageExtension/ImageExtension.d.ts +1 -4
- package/lib/Extensions/ImageExtension/ImageExtension.js +4 -78
- package/lib/Extensions/LinkExtension/AssetLinkExtension.js +3 -3
- package/lib/Extensions/LinkExtension/LinkExtension.d.ts +1 -3
- package/lib/Extensions/LinkExtension/LinkExtension.js +1 -9
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +12 -3
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +14 -5
- package/lib/utils/resolveMatrixAssetUrl.d.ts +1 -0
- package/lib/utils/resolveMatrixAssetUrl.js +10 -0
- package/package.json +3 -3
- package/src/Editor/EditorContext.spec.tsx +3 -3
- package/src/Editor/EditorContext.ts +9 -2
- package/src/EditorToolbar/FloatingToolbar.spec.tsx +24 -4
- package/src/EditorToolbar/FloatingToolbar.tsx +1 -1
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +26 -5
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx +145 -96
- package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +128 -7
- package/src/EditorToolbar/Tools/Image/ImageButton.tsx +15 -17
- package/src/EditorToolbar/Tools/Image/ImageModal.tsx +7 -1
- package/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx +1 -1
- package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +17 -5
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +1 -0
- package/src/Extensions/Extensions.ts +11 -0
- package/src/Extensions/ImageExtension/AssetImageExtension.spec.ts +76 -0
- package/src/Extensions/ImageExtension/AssetImageExtension.ts +111 -0
- package/src/Extensions/ImageExtension/ImageExtension.ts +6 -99
- package/src/Extensions/LinkExtension/AssetLinkExtension.spec.ts +1 -1
- package/src/Extensions/LinkExtension/AssetLinkExtension.ts +3 -3
- package/src/Extensions/LinkExtension/LinkExtension.ts +2 -22
- package/src/utils/converters/mocks/squizNodeJson.mock.ts +19 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +13 -3
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +13 -1
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +13 -5
- package/src/utils/resolveMatrixAssetUrl.spec.ts +26 -0
- package/src/utils/resolveMatrixAssetUrl.ts +7 -0
package/demo/App.tsx
CHANGED
@@ -40,10 +40,17 @@ function App() {
|
|
40
40
|
matrix: {
|
41
41
|
matrixDomain: 'https://matrix-domain.squiz.net',
|
42
42
|
matrixIdentifier: 'matrix-api-identifier',
|
43
|
-
|
43
|
+
resolveMatrixAsset: (assetId: string) => {
|
44
44
|
return new Promise((resolve) => {
|
45
45
|
setTimeout(() => {
|
46
|
-
|
46
|
+
if (assetId.match(/invalid/i)) {
|
47
|
+
resolve(null);
|
48
|
+
} else {
|
49
|
+
resolve({
|
50
|
+
id: assetId,
|
51
|
+
type: assetId.match(/(image)/i)?.[0] || 'unknown',
|
52
|
+
});
|
53
|
+
}
|
47
54
|
}, 200);
|
48
55
|
});
|
49
56
|
},
|
@@ -1,9 +1,14 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
export type MatrixAsset = {
|
3
|
+
id: string;
|
4
|
+
type: string | 'image';
|
5
|
+
};
|
6
|
+
export type MatrixAssetResolver = (assetId: string) => Promise<MatrixAsset | null>;
|
2
7
|
export type EditorContextOptions = {
|
3
8
|
matrix: {
|
4
9
|
matrixIdentifier: string;
|
5
10
|
matrixDomain: string;
|
6
|
-
|
11
|
+
resolveMatrixAsset: MatrixAssetResolver;
|
7
12
|
};
|
8
13
|
};
|
9
14
|
export declare const defaultEditorContext: EditorContextOptions;
|
@@ -9,7 +9,7 @@ exports.defaultEditorContext = {
|
|
9
9
|
matrix: {
|
10
10
|
matrixIdentifier: '',
|
11
11
|
matrixDomain: '',
|
12
|
-
|
12
|
+
resolveMatrixAsset: () => Promise.resolve(null),
|
13
13
|
},
|
14
14
|
};
|
15
15
|
exports.EditorContext = react_1.default.createContext(exports.defaultEditorContext);
|
@@ -50,7 +50,7 @@ const FloatingToolbar = () => {
|
|
50
50
|
extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, { key: "italic" }),
|
51
51
|
extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, { key: "underline" }),
|
52
52
|
];
|
53
|
-
if (active.image()) {
|
53
|
+
if (active.image() || active.assetImage()) {
|
54
54
|
buttons = [react_1.default.createElement(ImageButton_1.default, { key: "add-image", inPopover: true })];
|
55
55
|
}
|
56
56
|
else if (marks?.[Extensions_1.MarkName.Link].isExclusivelyActive || marks?.[Extensions_1.MarkName.AssetLink].isExclusivelyActive) {
|
@@ -1,17 +1,18 @@
|
|
1
1
|
import { ReactElement } from 'react';
|
2
2
|
import { SubmitHandler } from 'react-hook-form';
|
3
3
|
import { ImageAttributes } from '@remirror/extension-image/dist-types/image-extension';
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
import { NodeName } from '../../../../Extensions/Extensions';
|
5
|
+
import { AssetImageAttributes } from '../../../../Extensions/ImageExtension/AssetImageExtension';
|
6
|
+
import { DeepPartial } from '../../../../types';
|
7
|
+
export type ImageFormData = {
|
8
|
+
imageType: NodeName;
|
9
|
+
image: Pick<ImageAttributes, 'src' | 'alt' | 'width' | 'height'>;
|
10
|
+
assetImage: AssetImageAttributes;
|
9
11
|
};
|
10
|
-
export type ImageFormData = Pick<UpdateImageOptions, 'src' | 'alt' | 'width' | 'height'>;
|
11
12
|
export type FormProps = {
|
12
|
-
data:
|
13
|
+
data: DeepPartial<ImageFormData>;
|
13
14
|
onSubmit: SubmitHandler<ImageFormData>;
|
14
15
|
};
|
15
|
-
export type Dimensions = 'width' | 'height';
|
16
|
+
export type Dimensions = 'image.width' | 'image.height';
|
16
17
|
declare const ImageForm: ({ data, onSubmit }: FormProps) => ReactElement;
|
17
18
|
export default ImageForm;
|
@@ -34,23 +34,35 @@ const Button_1 = __importDefault(require("../../../../ui/Button/Button"));
|
|
34
34
|
const LinkOff_1 = __importDefault(require("@mui/icons-material/LinkOff"));
|
35
35
|
const InsertLinkRounded_1 = __importDefault(require("@mui/icons-material/InsertLinkRounded"));
|
36
36
|
const clsx_1 = __importDefault(require("clsx"));
|
37
|
+
const Extensions_1 = require("../../../../Extensions/Extensions");
|
38
|
+
const Select_1 = require("../../../../ui/Fields/Select/Select");
|
39
|
+
const EditorContext_1 = require("../../../../Editor/EditorContext");
|
40
|
+
const imageTypeOptions = {
|
41
|
+
[Extensions_1.NodeName.Image]: { label: 'External image' },
|
42
|
+
[Extensions_1.NodeName.AssetImage]: { label: 'Asset image' },
|
43
|
+
};
|
37
44
|
const regexDataURI = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)$/i;
|
38
45
|
const ImageForm = ({ data, onSubmit }) => {
|
39
|
-
const { register, handleSubmit, setValue, formState: { errors }, } = (0, react_hook_form_1.useForm)({
|
46
|
+
const { register, handleSubmit, setValue, watch, formState: { errors }, } = (0, react_hook_form_1.useForm)({
|
40
47
|
defaultValues: data,
|
41
48
|
});
|
49
|
+
const imageType = watch('imageType') || Extensions_1.NodeName.Image;
|
50
|
+
const context = (0, react_1.useContext)(EditorContext_1.EditorContext);
|
42
51
|
const [aspectRatioFromWidth, setAspectRatioFromWidth] = (0, react_1.useState)(9 / 16);
|
43
52
|
const [aspectRatioFromHeight, setAspectRatioFromHeight] = (0, react_1.useState)(16 / 9);
|
44
53
|
const [aspectRatioLocked, setAspectRatioLocked] = (0, react_1.useState)(true);
|
45
|
-
const setDimensionsFromURL = (e) => {
|
46
|
-
|
47
|
-
|
48
|
-
setValue('width', width);
|
49
|
-
setValue('height', height);
|
54
|
+
const setDimensionsFromURL = async (e) => {
|
55
|
+
try {
|
56
|
+
const { width, height } = await (0, react_image_size_1.getImageSize)(e.target.value);
|
57
|
+
setValue('image.width', width);
|
58
|
+
setValue('image.height', height);
|
50
59
|
setAspectRatioFromWidth(height / width);
|
51
60
|
setAspectRatioFromHeight(width / height);
|
52
|
-
}
|
53
|
-
|
61
|
+
}
|
62
|
+
catch (error) {
|
63
|
+
// swallow the error for fetching the image size, will occur if the URL does not point to an image.
|
64
|
+
// will be handled by validation when attempting to add the image.
|
65
|
+
}
|
54
66
|
};
|
55
67
|
const validateIsNotImage = async (src) => {
|
56
68
|
try {
|
@@ -61,13 +73,13 @@ const ImageForm = ({ data, onSubmit }) => {
|
|
61
73
|
return true;
|
62
74
|
}
|
63
75
|
};
|
64
|
-
const calculateDimensions = () => {
|
76
|
+
const calculateDimensions = (event) => {
|
65
77
|
if (aspectRatioLocked) {
|
66
78
|
const currentTarget = event?.target;
|
67
79
|
const type = currentTarget.name;
|
68
80
|
const currentValue = currentTarget.value;
|
69
|
-
const otherValue = type === 'width' ? 'height' : 'width';
|
70
|
-
const aspectRatio = type === 'width' ? aspectRatioFromWidth : aspectRatioFromHeight;
|
81
|
+
const otherValue = type === 'image.width' ? 'image.height' : 'image.width';
|
82
|
+
const aspectRatio = type === 'image.width' ? aspectRatioFromWidth : aspectRatioFromHeight;
|
71
83
|
const newValue = Math.round(aspectRatio * Number(currentValue) * 100) / 100;
|
72
84
|
setValue(otherValue, newValue);
|
73
85
|
}
|
@@ -77,48 +89,64 @@ const ImageForm = ({ data, onSubmit }) => {
|
|
77
89
|
};
|
78
90
|
return (react_1.default.createElement("form", { className: "squiz-fte-form", onSubmit: handleSubmit(onSubmit) },
|
79
91
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
80
|
-
react_1.default.createElement(
|
81
|
-
|
82
|
-
required: 'Source is required',
|
83
|
-
validate: {
|
84
|
-
isValidImage: async (value) => {
|
85
|
-
if (value && regexDataURI.test(value)) {
|
86
|
-
return 'Must not be a data URI';
|
87
|
-
}
|
88
|
-
if (value && (await validateIsNotImage(value))) {
|
89
|
-
return 'Must be a valid image URL';
|
90
|
-
}
|
91
|
-
},
|
92
|
-
},
|
93
|
-
}) })),
|
94
|
-
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
95
|
-
react_1.default.createElement(Input_1.Input, { label: "Alternative description", required: true, error: errors?.alt?.message, ...register('alt', { required: 'Alternative description is required' }) })),
|
96
|
-
react_1.default.createElement("div", { className: "flex flex-row" },
|
92
|
+
react_1.default.createElement(Select_1.Select, { name: "imageType", label: "Type", value: imageType, options: imageTypeOptions, onChange: (value) => setValue('imageType', value) })),
|
93
|
+
imageType === Extensions_1.NodeName.Image && (react_1.default.createElement(react_1.default.Fragment, null,
|
97
94
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
98
|
-
react_1.default.createElement(Input_1.Input, { label: "
|
99
|
-
onChange:
|
100
|
-
required: '
|
95
|
+
react_1.default.createElement(Input_1.Input, { label: "Source", required: true, error: errors?.image?.src?.message, ...register('image.src', {
|
96
|
+
onChange: setDimensionsFromURL,
|
97
|
+
required: 'Source is required',
|
101
98
|
validate: {
|
102
|
-
|
103
|
-
if (value &&
|
104
|
-
return 'Must be
|
99
|
+
isValidImage: async (value) => {
|
100
|
+
if (value && regexDataURI.test(value)) {
|
101
|
+
return 'Must not be a data URI';
|
102
|
+
}
|
103
|
+
if (value && (await validateIsNotImage(value))) {
|
104
|
+
return 'Must be a valid image URL';
|
105
105
|
}
|
106
106
|
},
|
107
107
|
},
|
108
108
|
}) })),
|
109
|
-
react_1.default.createElement("div", { className: "flex mx-1 mb-2" },
|
110
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: toggleAspectRatio, isActive: false, icon: aspectRatioLocked ? react_1.default.createElement(InsertLinkRounded_1.default, null) : react_1.default.createElement(LinkOff_1.default, null), label: "Constrain properties", isDisabled: false, className: (0, clsx_1.default)('my-auto', !errors?.height && !errors?.width && 'mb-0') })),
|
111
109
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
112
|
-
react_1.default.createElement(Input_1.Input, { label: "
|
113
|
-
|
114
|
-
|
110
|
+
react_1.default.createElement(Input_1.Input, { label: "Alternative description", required: true, error: errors?.image?.alt?.message, ...register('image.alt', { required: 'Alternative description is required' }) })),
|
111
|
+
react_1.default.createElement("div", { className: "flex flex-row" },
|
112
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
113
|
+
react_1.default.createElement(Input_1.Input, { label: "Width", type: "number", required: true, error: errors?.image?.width?.message, ...register('image.width', {
|
114
|
+
onChange: calculateDimensions,
|
115
|
+
required: 'Width is required',
|
116
|
+
validate: {
|
117
|
+
isValidWidth: (value) => {
|
118
|
+
if (value && !(value > 0)) {
|
119
|
+
return 'Must be higher than 0';
|
120
|
+
}
|
121
|
+
},
|
122
|
+
},
|
123
|
+
}) })),
|
124
|
+
react_1.default.createElement("div", { className: "flex mx-1 mb-2" },
|
125
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: toggleAspectRatio, isActive: false, icon: aspectRatioLocked ? react_1.default.createElement(InsertLinkRounded_1.default, null) : react_1.default.createElement(LinkOff_1.default, null), label: "Constrain properties", isDisabled: false, className: (0, clsx_1.default)('my-auto', !errors?.image?.height && !errors?.image?.width && 'mb-0') })),
|
126
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
127
|
+
react_1.default.createElement(Input_1.Input, { label: "Height", type: "number", required: true, error: errors?.image?.height?.message, ...register('image.height', {
|
128
|
+
onChange: calculateDimensions,
|
129
|
+
required: 'Height is required',
|
130
|
+
validate: {
|
131
|
+
isValidHeight: (value) => {
|
132
|
+
if (value && !(value > 0)) {
|
133
|
+
return 'Must be higher than 0';
|
134
|
+
}
|
135
|
+
},
|
136
|
+
},
|
137
|
+
}) }))))),
|
138
|
+
imageType === Extensions_1.NodeName.AssetImage && (react_1.default.createElement(react_1.default.Fragment, null,
|
139
|
+
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
140
|
+
react_1.default.createElement(Input_1.Input, { label: "Asset ID", required: true, error: errors?.assetImage?.matrixAssetId?.message, ...register('assetImage.matrixAssetId', {
|
141
|
+
required: 'Asset ID is required',
|
115
142
|
validate: {
|
116
|
-
|
117
|
-
|
118
|
-
|
143
|
+
isImage: async (assetId) => {
|
144
|
+
const asset = await context.matrix.resolveMatrixAsset(assetId);
|
145
|
+
if (asset?.type !== 'image') {
|
146
|
+
return 'Asset ID is invalid or not an image';
|
119
147
|
}
|
120
148
|
},
|
121
149
|
},
|
122
|
-
}) })))));
|
150
|
+
}) }))))));
|
123
151
|
};
|
124
152
|
exports.default = ImageForm;
|
@@ -27,32 +27,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
27
|
};
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
29
29
|
const react_1 = __importStar(require("react"));
|
30
|
+
const react_2 = require("@remirror/react");
|
30
31
|
const ImageRounded_1 = __importDefault(require("@mui/icons-material/ImageRounded"));
|
31
32
|
const ImageModal_1 = __importDefault(require("./ImageModal"));
|
32
33
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
33
|
-
const
|
34
|
-
const createToolbarPositioner_1 = require("../../../utils/createToolbarPositioner");
|
34
|
+
const Extensions_1 = require("../../../Extensions/Extensions");
|
35
35
|
const ImageButton = ({ inPopover = false }) => {
|
36
36
|
const [showModal, setShowModal] = (0, react_1.useState)(false);
|
37
|
-
const { insertImage } = (0, react_2.useCommands)();
|
37
|
+
const { insertImage, insertAssetImage } = (0, react_2.useCommands)();
|
38
38
|
const active = (0, react_2.useActive)();
|
39
|
-
const
|
40
|
-
const { data } = (0, react_2.usePositioner)(positioner, []);
|
39
|
+
const selection = (0, react_2.useCurrentSelection)();
|
41
40
|
// if the active selection is not an image, disable the button as it means it will be text
|
42
|
-
const disabled =
|
41
|
+
const disabled = !selection.empty && !active.image() && !active.assetImage();
|
43
42
|
const handleClick = () => {
|
44
43
|
if (!showModal) {
|
45
|
-
|
46
|
-
// update the selected text in state before showing the modal.
|
47
|
-
requestAnimationFrame(() => {
|
48
|
-
setShowModal(true);
|
49
|
-
});
|
44
|
+
setShowModal(true);
|
50
45
|
}
|
51
46
|
};
|
52
47
|
const insertImageFromData = (data) => {
|
53
|
-
const {
|
54
|
-
if (
|
55
|
-
insertImage(
|
48
|
+
const { imageType, image, assetImage } = data;
|
49
|
+
if (imageType === Extensions_1.NodeName.Image) {
|
50
|
+
insertImage(image);
|
51
|
+
}
|
52
|
+
else {
|
53
|
+
insertAssetImage(assetImage);
|
56
54
|
}
|
57
55
|
};
|
58
56
|
const handleSubmit = (data) => {
|
@@ -71,7 +69,7 @@ const ImageButton = ({ inPopover = false }) => {
|
|
71
69
|
(0, react_2.useKeymap)('Mod-l', disabled ? () => false : handleShortcut);
|
72
70
|
}
|
73
71
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
74
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.image(), icon: react_1.default.createElement(ImageRounded_1.default, null), label: "Image (cmd+L)", isDisabled: disabled }),
|
72
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.image() || active.assetImage(), icon: react_1.default.createElement(ImageRounded_1.default, null), label: "Image (cmd+L)", isDisabled: disabled }),
|
75
73
|
showModal && react_1.default.createElement(ImageModal_1.default, { onCancel: () => setShowModal(false), onSubmit: handleSubmit })));
|
76
74
|
};
|
77
75
|
exports.default = ImageButton;
|
@@ -7,10 +7,16 @@ const ImageForm_1 = __importDefault(require("./Form/ImageForm"));
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
8
8
|
const react_2 = require("@remirror/react");
|
9
9
|
const FormModal_1 = __importDefault(require("../../../ui/Modal/FormModal"));
|
10
|
+
const Extensions_1 = require("../../../Extensions/Extensions");
|
10
11
|
const ImageModal = ({ onCancel, onSubmit }) => {
|
11
12
|
const selection = (0, react_2.useCurrentSelection)();
|
12
13
|
const currentImage = selection?.node;
|
14
|
+
const formData = {
|
15
|
+
imageType: currentImage?.type.name === Extensions_1.NodeName.AssetImage ? Extensions_1.NodeName.AssetImage : Extensions_1.NodeName.Image,
|
16
|
+
image: currentImage?.type?.name === Extensions_1.NodeName.Image ? currentImage?.attrs : {},
|
17
|
+
assetImage: currentImage?.type?.name === Extensions_1.NodeName.AssetImage ? currentImage?.attrs : {},
|
18
|
+
};
|
13
19
|
return (react_1.default.createElement(FormModal_1.default, { title: "Image", onCancel: onCancel },
|
14
|
-
react_1.default.createElement(ImageForm_1.default, { data:
|
20
|
+
react_1.default.createElement(ImageForm_1.default, { data: formData, onSubmit: onSubmit })));
|
15
21
|
};
|
16
22
|
exports.default = ImageModal;
|
@@ -66,7 +66,7 @@ const LinkForm = ({ data, onSubmit }) => {
|
|
66
66
|
react_1.default.createElement(Input_1.Input, { label: "Asset ID", error: errors?.assetLink?.matrixAssetId?.message, ...register('assetLink.matrixAssetId', {
|
67
67
|
validate: {
|
68
68
|
isValidAsset: async (assetId) => {
|
69
|
-
if (assetId && !(await context.matrix.
|
69
|
+
if (assetId && !(await context.matrix.resolveMatrixAsset(assetId))) {
|
70
70
|
return 'Invalid asset ID';
|
71
71
|
}
|
72
72
|
},
|
@@ -1,5 +1,10 @@
|
|
1
1
|
import { Extension } from '@remirror/core';
|
2
2
|
import { EditorContextOptions } from '../Editor/EditorContext';
|
3
|
+
export declare enum NodeName {
|
4
|
+
Image = "image",
|
5
|
+
AssetImage = "assetImage",
|
6
|
+
Text = "text"
|
7
|
+
}
|
3
8
|
export declare enum MarkName {
|
4
9
|
Link = "link",
|
5
10
|
AssetLink = "assetLink"
|
@@ -1,12 +1,19 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.createExtensions = exports.MarkName = void 0;
|
3
|
+
exports.createExtensions = exports.MarkName = exports.NodeName = void 0;
|
4
4
|
const extensions_1 = require("remirror/extensions");
|
5
5
|
const PreformattedExtension_1 = require("./PreformattedExtension/PreformattedExtension");
|
6
6
|
const AssetLinkExtension_1 = require("./LinkExtension/AssetLinkExtension");
|
7
7
|
const LinkExtension_1 = require("./LinkExtension/LinkExtension");
|
8
8
|
const ImageExtension_1 = require("./ImageExtension/ImageExtension");
|
9
9
|
const CommandsExtension_1 = require("./CommandsExtension/CommandsExtension");
|
10
|
+
const AssetImageExtension_1 = require("./ImageExtension/AssetImageExtension");
|
11
|
+
var NodeName;
|
12
|
+
(function (NodeName) {
|
13
|
+
NodeName["Image"] = "image";
|
14
|
+
NodeName["AssetImage"] = "assetImage";
|
15
|
+
NodeName["Text"] = "text";
|
16
|
+
})(NodeName = exports.NodeName || (exports.NodeName = {}));
|
10
17
|
var MarkName;
|
11
18
|
(function (MarkName) {
|
12
19
|
MarkName["Link"] = "link";
|
@@ -26,6 +33,10 @@ const createExtensions = (context) => {
|
|
26
33
|
new extensions_1.HistoryExtension(),
|
27
34
|
new ImageExtension_1.ImageExtension(),
|
28
35
|
new ImageExtension_1.ImageExtension({ preferPastedTextContent: false }),
|
36
|
+
new AssetImageExtension_1.AssetImageExtension({
|
37
|
+
matrixIdentifier: context.matrix.matrixIdentifier,
|
38
|
+
matrixDomain: context.matrix.matrixDomain,
|
39
|
+
}),
|
29
40
|
new LinkExtension_1.LinkExtension(),
|
30
41
|
new AssetLinkExtension_1.AssetLinkExtension({
|
31
42
|
matrixIdentifier: context.matrix.matrixIdentifier,
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { ApplySchemaAttributes, NodeExtension, NodeExtensionSpec, NodeSpecOverride, CommandFunction } from '@remirror/core';
|
2
|
+
import { NodeName } from '../Extensions';
|
3
|
+
export type AssetImageOptions = {
|
4
|
+
matrixIdentifier?: string;
|
5
|
+
matrixDomain?: string;
|
6
|
+
};
|
7
|
+
export type AssetImageAttributes = {
|
8
|
+
matrixAssetId: string;
|
9
|
+
matrixIdentifier: string;
|
10
|
+
matrixDomain: string;
|
11
|
+
};
|
12
|
+
export declare class AssetImageExtension extends NodeExtension<AssetImageOptions> {
|
13
|
+
get name(): NodeName.AssetImage;
|
14
|
+
createTags(): ("inline" | "media")[];
|
15
|
+
createNodeSpec(extra: ApplySchemaAttributes, override: NodeSpecOverride): NodeExtensionSpec;
|
16
|
+
insertAssetImage(attrs: AssetImageAttributes): CommandFunction;
|
17
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
7
|
+
};
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.AssetImageExtension = void 0;
|
10
|
+
const core_1 = require("@remirror/core");
|
11
|
+
const remirror_1 = require("remirror");
|
12
|
+
const resolveMatrixAssetUrl_1 = require("../../utils/resolveMatrixAssetUrl");
|
13
|
+
const Extensions_1 = require("../Extensions");
|
14
|
+
let AssetImageExtension = class AssetImageExtension extends core_1.NodeExtension {
|
15
|
+
get name() {
|
16
|
+
return Extensions_1.NodeName.AssetImage;
|
17
|
+
}
|
18
|
+
createTags() {
|
19
|
+
return [core_1.ExtensionTag.InlineNode, core_1.ExtensionTag.Media];
|
20
|
+
}
|
21
|
+
createNodeSpec(extra, override) {
|
22
|
+
return {
|
23
|
+
inline: true,
|
24
|
+
draggable: true,
|
25
|
+
selectable: true,
|
26
|
+
...override,
|
27
|
+
attrs: {
|
28
|
+
...extra.defaults(),
|
29
|
+
matrixAssetId: {},
|
30
|
+
matrixIdentifier: { default: this.options.matrixIdentifier },
|
31
|
+
matrixDomain: { default: this.options.matrixDomain },
|
32
|
+
},
|
33
|
+
parseDOM: [
|
34
|
+
{
|
35
|
+
tag: 'img[data-matrix-asset-id]',
|
36
|
+
getAttrs: (node) => {
|
37
|
+
if (!(0, core_1.isElementDomNode)(node)) {
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
const matrixAssetId = node.getAttribute('data-matrix-asset-id');
|
41
|
+
const matrixIdentifier = node.getAttribute('data-matrix-identifier');
|
42
|
+
const matrixDomain = node.getAttribute('data-matrix-domain');
|
43
|
+
if (!matrixAssetId || !matrixIdentifier || !matrixDomain) {
|
44
|
+
return false;
|
45
|
+
}
|
46
|
+
return {
|
47
|
+
...extra.parse(node),
|
48
|
+
matrixAssetId,
|
49
|
+
matrixIdentifier,
|
50
|
+
matrixDomain,
|
51
|
+
};
|
52
|
+
},
|
53
|
+
},
|
54
|
+
],
|
55
|
+
toDOM: (node) => {
|
56
|
+
const { matrixAssetId, matrixIdentifier, matrixDomain, ...rest } = (0, core_1.omitExtraAttributes)(node.attrs, extra);
|
57
|
+
return [
|
58
|
+
'img',
|
59
|
+
{
|
60
|
+
...extra.dom(node),
|
61
|
+
...rest,
|
62
|
+
src: (0, resolveMatrixAssetUrl_1.resolveMatrixAssetUrl)(String(matrixAssetId), String(matrixDomain)),
|
63
|
+
'data-matrix-asset-id': matrixAssetId,
|
64
|
+
'data-matrix-identifier': matrixIdentifier,
|
65
|
+
'data-matrix-domain': matrixDomain,
|
66
|
+
},
|
67
|
+
];
|
68
|
+
},
|
69
|
+
};
|
70
|
+
}
|
71
|
+
insertAssetImage(attrs) {
|
72
|
+
return ({ tr, dispatch }) => {
|
73
|
+
const { from, to } = (0, remirror_1.getTextSelection)(tr.selection, tr.doc);
|
74
|
+
const node = this.type.create(attrs);
|
75
|
+
dispatch?.(tr.replaceRangeWith(from, to, node));
|
76
|
+
return true;
|
77
|
+
};
|
78
|
+
}
|
79
|
+
};
|
80
|
+
__decorate([
|
81
|
+
(0, core_1.command)()
|
82
|
+
], AssetImageExtension.prototype, "insertAssetImage", null);
|
83
|
+
AssetImageExtension = __decorate([
|
84
|
+
(0, core_1.extension)({
|
85
|
+
defaultOptions: {
|
86
|
+
matrixIdentifier: '',
|
87
|
+
matrixDomain: '',
|
88
|
+
},
|
89
|
+
defaultPriority: core_1.ExtensionPriority.High,
|
90
|
+
})
|
91
|
+
], AssetImageExtension);
|
92
|
+
exports.AssetImageExtension = AssetImageExtension;
|
@@ -1,10 +1,7 @@
|
|
1
1
|
import { ImageExtension as RemirrorImageExtension } from 'remirror/extensions';
|
2
2
|
import { PasteRule } from 'prosemirror-paste-rules';
|
3
|
-
import { ApplySchemaAttributes, NodeSpecOverride, NodeExtensionSpec
|
4
|
-
import { ImageAttributes } from '@remirror/extension-image/dist-types/image-extension';
|
5
|
-
import { CommandFunction } from '@remirror/pm';
|
3
|
+
import { ApplySchemaAttributes, NodeSpecOverride, NodeExtensionSpec } from '@remirror/core';
|
6
4
|
export declare class ImageExtension extends RemirrorImageExtension {
|
7
5
|
createPasteRules(): PasteRule[];
|
8
6
|
createNodeSpec(extra: ApplySchemaAttributes, override: NodeSpecOverride): NodeExtensionSpec;
|
9
|
-
insertImage(attributes: ImageAttributes, selection?: PrimitiveSelection): CommandFunction;
|
10
7
|
}
|
@@ -2,90 +2,16 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.ImageExtension = void 0;
|
4
4
|
const extensions_1 = require("remirror/extensions");
|
5
|
-
const core_1 = require("@remirror/core");
|
6
|
-
/**
|
7
|
-
* Get the width and the height of the image.
|
8
|
-
*/
|
9
|
-
function getDimensions(element) {
|
10
|
-
let { width, height } = element.style;
|
11
|
-
width = width || element.getAttribute('width') || '';
|
12
|
-
height = height || element.getAttribute('height') || '';
|
13
|
-
return { width, height };
|
14
|
-
}
|
15
|
-
/**
|
16
|
-
* Retrieve attributes from the dom for the image extension.
|
17
|
-
*/
|
18
|
-
function getImageAttributes({ element, parse }) {
|
19
|
-
const { width, height } = getDimensions(element);
|
20
|
-
return {
|
21
|
-
...parse(element),
|
22
|
-
alt: element.getAttribute('alt') ?? '',
|
23
|
-
height: Number.parseInt(height || '0', 10) || null,
|
24
|
-
src: element.getAttribute('src') ?? null,
|
25
|
-
title: element.getAttribute('title') ?? '',
|
26
|
-
width: Number.parseInt(width || '0', 10) || null,
|
27
|
-
fileName: element.getAttribute('data-file-name') ?? null,
|
28
|
-
};
|
29
|
-
}
|
30
5
|
class ImageExtension extends extensions_1.ImageExtension {
|
31
6
|
createPasteRules() {
|
32
|
-
|
33
|
-
|
34
|
-
type: 'file',
|
35
|
-
regexp: /image/i,
|
36
|
-
fileHandler: () => {
|
37
|
-
return false;
|
38
|
-
},
|
39
|
-
},
|
40
|
-
];
|
7
|
+
// override super behaviour of handling file uploads.
|
8
|
+
return [];
|
41
9
|
}
|
42
10
|
createNodeSpec(extra, override) {
|
43
|
-
const
|
11
|
+
const spec = super.createNodeSpec(extra, override);
|
44
12
|
return {
|
45
|
-
|
46
|
-
draggable: true,
|
13
|
+
...spec,
|
47
14
|
selectable: true,
|
48
|
-
...override,
|
49
|
-
attrs: {
|
50
|
-
...extra.defaults(),
|
51
|
-
alt: { default: '' },
|
52
|
-
crop: { default: null },
|
53
|
-
height: { default: null },
|
54
|
-
width: { default: null },
|
55
|
-
rotate: { default: null },
|
56
|
-
src: { default: null },
|
57
|
-
title: { default: '' },
|
58
|
-
fileName: { default: null },
|
59
|
-
resizable: { default: false },
|
60
|
-
},
|
61
|
-
parseDOM: [
|
62
|
-
{
|
63
|
-
tag: 'img[src]',
|
64
|
-
getAttrs: (element) => {
|
65
|
-
if ((0, core_1.isElementDomNode)(element)) {
|
66
|
-
const attrs = getImageAttributes({ element, parse: extra.parse });
|
67
|
-
if (preferPastedTextContent && attrs.src?.startsWith('file:///')) {
|
68
|
-
return false;
|
69
|
-
}
|
70
|
-
return attrs;
|
71
|
-
}
|
72
|
-
return {};
|
73
|
-
},
|
74
|
-
},
|
75
|
-
...(override.parseDOM ?? []),
|
76
|
-
],
|
77
|
-
toDOM: (node) => {
|
78
|
-
const attrs = (0, core_1.omitExtraAttributes)(node.attrs, extra);
|
79
|
-
return ['img', { ...extra.dom(node), ...attrs }];
|
80
|
-
},
|
81
|
-
};
|
82
|
-
}
|
83
|
-
insertImage(attributes, selection) {
|
84
|
-
return ({ tr, dispatch }) => {
|
85
|
-
const { from, to } = (0, core_1.getTextSelection)(selection ?? tr.selection, tr.doc);
|
86
|
-
const node = this.type.create(attributes);
|
87
|
-
dispatch?.(tr.replaceRangeWith(from, to, node));
|
88
|
-
return true;
|
89
15
|
};
|
90
16
|
}
|
91
17
|
}
|
@@ -12,6 +12,7 @@ const core_1 = require("@remirror/core");
|
|
12
12
|
const common_1 = require("./common");
|
13
13
|
const CommandsExtension_1 = require("../CommandsExtension/CommandsExtension");
|
14
14
|
const Extensions_1 = require("../Extensions");
|
15
|
+
const resolveMatrixAssetUrl_1 = require("../../utils/resolveMatrixAssetUrl");
|
15
16
|
let AssetLinkExtension = class AssetLinkExtension extends core_1.MarkExtension {
|
16
17
|
get name() {
|
17
18
|
return Extensions_1.MarkName.AssetLink;
|
@@ -19,7 +20,7 @@ let AssetLinkExtension = class AssetLinkExtension extends core_1.MarkExtension {
|
|
19
20
|
createMarkSpec(extra, override) {
|
20
21
|
return {
|
21
22
|
inclusive: false,
|
22
|
-
excludes:
|
23
|
+
excludes: Extensions_1.MarkName.Link,
|
23
24
|
...override,
|
24
25
|
attrs: {
|
25
26
|
...extra.defaults(),
|
@@ -58,8 +59,7 @@ let AssetLinkExtension = class AssetLinkExtension extends core_1.MarkExtension {
|
|
58
59
|
...extra.dom(node),
|
59
60
|
...rest,
|
60
61
|
rel,
|
61
|
-
|
62
|
-
href: `/?a=${matrixAssetId}`,
|
62
|
+
href: (0, resolveMatrixAssetUrl_1.resolveMatrixAssetUrl)(String(matrixAssetId), String(matrixDomain)),
|
63
63
|
target: (0, common_1.validateTarget)(target, this.options.supportedTargets, this.options.defaultTarget),
|
64
64
|
'data-matrix-asset-id': matrixAssetId,
|
65
65
|
'data-matrix-identifier': matrixIdentifier,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ApplySchemaAttributes, FromToProps, MarkExtensionSpec, MarkSpecOverride } from 'remirror';
|
2
|
-
import { CommandFunction,
|
2
|
+
import { CommandFunction, MarkExtension } from '@remirror/core';
|
3
3
|
import { LinkTarget } from './common';
|
4
4
|
export type LinkAttributes = {
|
5
5
|
href: string;
|
@@ -9,7 +9,6 @@ export type LinkAttributes = {
|
|
9
9
|
export type LinkOptions = {
|
10
10
|
defaultTarget?: LinkTarget;
|
11
11
|
supportedTargets?: LinkTarget[];
|
12
|
-
onShortcut?: Handler<() => void>;
|
13
12
|
};
|
14
13
|
export type UpdateLinkProps = {
|
15
14
|
text: string;
|
@@ -19,7 +18,6 @@ export type UpdateLinkProps = {
|
|
19
18
|
export declare class LinkExtension extends MarkExtension<LinkOptions> {
|
20
19
|
get name(): string;
|
21
20
|
createMarkSpec(extra: ApplySchemaAttributes, override: MarkSpecOverride): MarkExtensionSpec;
|
22
|
-
shortcut(_: KeyBindingProps): boolean;
|
23
21
|
updateLink({ text, attrs, range }: UpdateLinkProps): CommandFunction;
|
24
22
|
removeLink(): CommandFunction;
|
25
23
|
}
|