@squiz/formatted-text-editor 2.0.1 → 2.2.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/CHANGELOG.md +24 -0
- package/demo/App.tsx +5 -0
- package/demo/AppContext.tsx +111 -51
- package/jest.config.ts +1 -1
- package/jest.setup.ts +16 -0
- package/lib/EditorToolbar/FloatingToolbar.js +1 -1
- package/lib/EditorToolbar/Toolbar.js +3 -1
- package/lib/EditorToolbar/Tools/ContentTools/ContentToolsDropdown.d.ts +3 -0
- package/lib/EditorToolbar/Tools/ContentTools/ContentToolsDropdown.js +35 -0
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.d.ts +2 -1
- package/lib/EditorToolbar/Tools/Image/Form/ImageForm.js +53 -10
- package/lib/EditorToolbar/Tools/Image/ImageButton.js +8 -5
- package/lib/EditorToolbar/Tools/Image/ImageModal.js +3 -1
- package/lib/EditorToolbar/Tools/Table/TableButton.js +1 -3
- package/lib/Extensions/Extensions.d.ts +1 -0
- package/lib/Extensions/Extensions.js +3 -0
- package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +6 -0
- package/lib/Extensions/ImageExtension/DAMImageExtension.d.ts +17 -0
- package/lib/Extensions/ImageExtension/DAMImageExtension.js +97 -0
- package/lib/Icons/AiIcon.d.ts +2 -0
- package/lib/Icons/AiIcon.js +60 -0
- package/lib/index.css +4224 -0
- package/lib/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.d.ts +28 -0
- package/lib/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.js +88 -0
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +9 -0
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +9 -0
- package/package.json +6 -2
- package/src/EditorToolbar/FloatingToolbar.spec.tsx +3 -1
- package/src/EditorToolbar/FloatingToolbar.tsx +1 -1
- package/src/EditorToolbar/Toolbar.tsx +2 -0
- package/src/EditorToolbar/Tools/ContentTools/ContentToolsDropdown.spec.tsx +78 -0
- package/src/EditorToolbar/Tools/ContentTools/ContentToolsDropdown.tsx +46 -0
- package/src/EditorToolbar/Tools/ContentTools/_content-tools.scss +45 -0
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +27 -2
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.tsx +61 -14
- package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +70 -2
- package/src/EditorToolbar/Tools/Image/ImageButton.tsx +12 -6
- package/src/EditorToolbar/Tools/Image/ImageModal.tsx +4 -1
- package/src/EditorToolbar/Tools/Table/TableButton.tsx +0 -2
- package/src/Extensions/Extensions.ts +3 -0
- package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +9 -0
- package/src/Extensions/ImageExtension/DAMImageExtension.spec.ts +87 -0
- package/src/Extensions/ImageExtension/DAMImageExtension.ts +119 -0
- package/src/Icons/AiIcon.tsx +140 -0
- package/src/index.scss +4 -0
- package/src/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.spec.tsx +219 -0
- package/src/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.tsx +109 -0
- package/src/utils/converters/mocks/squizNodeJson.mock.ts +21 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +10 -0
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +8 -0
- package/src/utils/getNodeNamesByGroup.spec.ts +1 -0
- package/tests/index.ts +1 -0
- package/tests/mockResourceBrowser.tsx +46 -0
@@ -1,11 +1,18 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
2
|
import { screen, fireEvent, waitForElementToBeRemoved, act, waitFor } from '@testing-library/react';
|
3
|
+
import { ResourceBrowserResource } from '@squiz/resource-browser';
|
3
4
|
import { NodeSelection } from 'prosemirror-state';
|
4
5
|
import React from 'react';
|
5
|
-
import { renderWithEditor, mockResourceBrowserContext } from '../../../../tests';
|
6
|
+
import { renderWithEditor, mockResourceBrowserContext, mockResourceBrowser } from '../../../../tests';
|
6
7
|
import ImageButton from './ImageButton';
|
7
8
|
import { getImageSize } from 'react-image-size';
|
8
9
|
|
10
|
+
const { setSelectedResource, setShouldUseMockResourceBrowser, mockResourceBrowserImpl } = mockResourceBrowser();
|
11
|
+
jest.mock('@squiz/resource-browser', () => ({
|
12
|
+
...jest.requireActual('@squiz/resource-browser'),
|
13
|
+
ResourceBrowser: (props: any) => mockResourceBrowserImpl(props),
|
14
|
+
}));
|
15
|
+
|
9
16
|
jest.mock('react-image-size');
|
10
17
|
|
11
18
|
describe('ImageButton', () => {
|
@@ -295,7 +302,7 @@ describe('ImageButton', () => {
|
|
295
302
|
});
|
296
303
|
});
|
297
304
|
|
298
|
-
it('Updates the attributes of an existing asset image', async () => {
|
305
|
+
it('assetImage: Updates the attributes of an existing asset image', async () => {
|
299
306
|
const matrixIdentifier = 'matrix-api-identifier';
|
300
307
|
const matrixDomain = 'https://my-matrix.squiz.net';
|
301
308
|
const { MockResourceBrowserContext, selectResource } = mockResourceBrowserContext({
|
@@ -362,6 +369,67 @@ describe('ImageButton', () => {
|
|
362
369
|
});
|
363
370
|
});
|
364
371
|
|
372
|
+
it('DAMImage: Updates the attributes of an existing dam image', async () => {
|
373
|
+
setShouldUseMockResourceBrowser(true);
|
374
|
+
setSelectedResource({
|
375
|
+
id: 'my-resource-id',
|
376
|
+
name: 'My resource',
|
377
|
+
url: 'myResourceUrl',
|
378
|
+
source: {
|
379
|
+
id: 'my-source-id',
|
380
|
+
type: 'dam',
|
381
|
+
configuration: {
|
382
|
+
externalType: 'bynder',
|
383
|
+
},
|
384
|
+
},
|
385
|
+
} as unknown as ResourceBrowserResource);
|
386
|
+
|
387
|
+
const { editor, getJsonContent } = await renderWithEditor(<ImageButton />, {
|
388
|
+
content: 'Some <img src="https://httpcats.com/529.jpg" alt="hi" /> nonsense',
|
389
|
+
context: {
|
390
|
+
editor: {
|
391
|
+
matrix: {
|
392
|
+
matrixDomain: 'https://my-matrix.squiz.net',
|
393
|
+
},
|
394
|
+
},
|
395
|
+
},
|
396
|
+
});
|
397
|
+
|
398
|
+
await act(() => editor.selectText(new NodeSelection(editor.state.doc.resolve(6))));
|
399
|
+
|
400
|
+
await openModal();
|
401
|
+
fireEvent.click(screen.getByRole('button', { name: 'From source' }));
|
402
|
+
// Trigger the mock Resource Browser to invoke its onChange callback to fill the form with data
|
403
|
+
await waitFor(() => {
|
404
|
+
expect(screen.getByRole('button', { name: 'Select ResourceBrowser Resource' })).toBeInTheDocument();
|
405
|
+
});
|
406
|
+
fireEvent.click(await screen.findByRole('button', { name: 'Select ResourceBrowser Resource' }));
|
407
|
+
// Close the image selection modal
|
408
|
+
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Apply' })));
|
409
|
+
|
410
|
+
expect(getJsonContent()).toEqual({
|
411
|
+
type: 'paragraph',
|
412
|
+
attrs: expect.any(Object),
|
413
|
+
content: [
|
414
|
+
{
|
415
|
+
text: 'Some ',
|
416
|
+
type: 'text',
|
417
|
+
},
|
418
|
+
{
|
419
|
+
type: 'DAMImage',
|
420
|
+
attrs: {
|
421
|
+
damObjectId: 'my-resource-id',
|
422
|
+
damSystemIdentifier: 'my-source-id',
|
423
|
+
damAdditional: undefined,
|
424
|
+
damSystemType: 'bynder',
|
425
|
+
src: 'myResourceUrl',
|
426
|
+
},
|
427
|
+
},
|
428
|
+
{ type: 'text', text: ' nonsense' },
|
429
|
+
],
|
430
|
+
});
|
431
|
+
});
|
432
|
+
|
365
433
|
it('Shows an error if a resource is not selected', async () => {
|
366
434
|
await renderWithEditor(<ImageButton />);
|
367
435
|
await openModal();
|
@@ -6,7 +6,8 @@ import { ImageFormData } from './Form/ImageForm';
|
|
6
6
|
import Button from '../../../ui/Button/Button';
|
7
7
|
import { ImageExtension } from '../../../Extensions/ImageExtension/ImageExtension';
|
8
8
|
import { NodeName } from '../../../Extensions/Extensions';
|
9
|
-
import { AssetImageExtension } from '../../../Extensions/ImageExtension/AssetImageExtension';
|
9
|
+
import { AssetImageExtension, AssetImageAttributes } from '../../../Extensions/ImageExtension/AssetImageExtension';
|
10
|
+
import { DAMImageExtension, DAMImageAttributes } from '../../../Extensions/ImageExtension/DAMImageExtension';
|
10
11
|
import { CodeBlockExtension } from 'remirror/dist-types/extensions';
|
11
12
|
import { getShortcutSymbol } from '../../../utils/getShortcutSymbol';
|
12
13
|
|
@@ -16,11 +17,14 @@ type ImageButtonProps = {
|
|
16
17
|
|
17
18
|
const ImageButton = ({ inPopover = false }: ImageButtonProps) => {
|
18
19
|
const [showModal, setShowModal] = useState(false);
|
19
|
-
const { insertImage, insertAssetImage } = useCommands<
|
20
|
+
const { insertImage, insertAssetImage, insertDAMImage } = useCommands<
|
21
|
+
ImageExtension | AssetImageExtension | DAMImageExtension
|
22
|
+
>();
|
20
23
|
const active = useActive<ImageExtension | AssetImageExtension | CodeBlockExtension>();
|
21
24
|
const selection = useCurrentSelection();
|
22
25
|
// if the active selection is not an image, disable the button as it means it will be text
|
23
|
-
const disabled =
|
26
|
+
const disabled =
|
27
|
+
(!selection.empty && !active.image() && !active.assetImage() && !active.DAMImage()) || active.codeBlock();
|
24
28
|
|
25
29
|
const handleClick = () => {
|
26
30
|
if (!showModal) {
|
@@ -29,11 +33,13 @@ const ImageButton = ({ inPopover = false }: ImageButtonProps) => {
|
|
29
33
|
};
|
30
34
|
|
31
35
|
const insertImageFromData = (data: ImageFormData) => {
|
32
|
-
const { imageType, image,
|
36
|
+
const { imageType, image, resourceImage } = data;
|
33
37
|
if (imageType === NodeName.Image) {
|
34
38
|
insertImage(image);
|
39
|
+
} else if (imageType === NodeName.DAMImage) {
|
40
|
+
insertDAMImage(resourceImage as DAMImageAttributes);
|
35
41
|
} else {
|
36
|
-
insertAssetImage(
|
42
|
+
insertAssetImage(resourceImage as AssetImageAttributes);
|
37
43
|
}
|
38
44
|
};
|
39
45
|
|
@@ -59,7 +65,7 @@ const ImageButton = ({ inPopover = false }: ImageButtonProps) => {
|
|
59
65
|
<>
|
60
66
|
<Button
|
61
67
|
handleOnClick={handleClick}
|
62
|
-
isActive={active.image() || active.assetImage()}
|
68
|
+
isActive={active.image() || active.assetImage() || active.DAMImage()}
|
63
69
|
icon={<ImageRoundedIcon />}
|
64
70
|
label={`Image (${getShortcutSymbol()}+L)`}
|
65
71
|
isDisabled={disabled}
|
@@ -19,7 +19,10 @@ const ImageModal = ({ onCancel, onSubmit }: ImageModalProps) => {
|
|
19
19
|
const formData = {
|
20
20
|
imageType: currentImage?.type.name === NodeName.Image ? NodeName.Image : NodeName.AssetImage,
|
21
21
|
image: currentImage?.type?.name === NodeName.Image ? currentImageAttrs : {},
|
22
|
-
|
22
|
+
resourceImage:
|
23
|
+
currentImage?.type?.name === NodeName.DAMImage || currentImage?.type?.name === NodeName.AssetImage
|
24
|
+
? currentImageAttrs
|
25
|
+
: {},
|
23
26
|
};
|
24
27
|
|
25
28
|
return (
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { useCommands, useActive } from '@remirror/react';
|
3
|
-
import { VerticalDivider } from '@remirror/react-components';
|
4
3
|
import { TableExtension } from '@remirror/extension-react-tables';
|
5
4
|
import Button from '../../../ui/Button/Button';
|
6
5
|
import TableViewRoundedIcon from '@mui/icons-material/TableViewRounded';
|
@@ -24,7 +23,6 @@ const TableButton = () => {
|
|
24
23
|
icon={<TableViewRoundedIcon />}
|
25
24
|
label="Insert table"
|
26
25
|
/>
|
27
|
-
<VerticalDivider />
|
28
26
|
</>
|
29
27
|
);
|
30
28
|
};
|
@@ -23,6 +23,7 @@ import { ImageExtension } from './ImageExtension/ImageExtension';
|
|
23
23
|
import { CommandsExtension } from './CommandsExtension/CommandsExtension';
|
24
24
|
import { EditorContextOptions } from '../Editor/EditorContext';
|
25
25
|
import { AssetImageExtension } from './ImageExtension/AssetImageExtension';
|
26
|
+
import { DAMImageExtension } from './ImageExtension/DAMImageExtension';
|
26
27
|
import { ExtendedCodeBlockExtension } from './CodeBlockExtension/CodeBlockExtension';
|
27
28
|
import { ClearFormattingExtension } from './ClearFormattingExtension/ClearFormattingExtension';
|
28
29
|
import { UnsupportedNodeExtension } from './UnsuportedExtension/UnsupportedNodeExtension';
|
@@ -34,6 +35,7 @@ export enum NodeName {
|
|
34
35
|
Image = 'image',
|
35
36
|
CodeBlock = 'codeBlock',
|
36
37
|
AssetImage = 'assetImage',
|
38
|
+
DAMImage = 'DAMImage',
|
37
39
|
Text = 'text',
|
38
40
|
TableControllerCell = 'tableControllerCell',
|
39
41
|
tableCell = 'tableCell',
|
@@ -66,6 +68,7 @@ export const createExtensions = (context: EditorContextOptions) => {
|
|
66
68
|
new AssetImageExtension({
|
67
69
|
matrixDomain: context.matrix.matrixDomain,
|
68
70
|
}),
|
71
|
+
new DAMImageExtension(),
|
69
72
|
new LinkExtension(),
|
70
73
|
new AssetLinkExtension({
|
71
74
|
matrixDomain: context.matrix.matrixDomain,
|
@@ -33,6 +33,15 @@ export class FetchUrlExtension extends PlainExtension<FetchUrlOptions> {
|
|
33
33
|
);
|
34
34
|
}
|
35
35
|
|
36
|
+
if (node.type.name === NodeName.DAMImage && node.attrs.src === '') {
|
37
|
+
promises.push(
|
38
|
+
this.fetchAndReplace(node.attrs, (url: string) => {
|
39
|
+
const newNode = state.schema.nodes[NodeName.DAMImage].create({ ...node.attrs, src: url });
|
40
|
+
tr.replaceWith(pos, pos + node.nodeSize, newNode);
|
41
|
+
}),
|
42
|
+
);
|
43
|
+
}
|
44
|
+
|
36
45
|
const assetLinkMark = this.findAssetLinkMark(node.marks as Mark[]);
|
37
46
|
if (node.type.name === 'text' && assetLinkMark) {
|
38
47
|
promises.push(
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import { renderWithEditor } from '../../../tests';
|
2
|
+
|
3
|
+
describe('DAMImageExtension', () => {
|
4
|
+
it('Parses HTML content representing an asset image', async () => {
|
5
|
+
const { getJsonContent } = await renderWithEditor(null, {
|
6
|
+
content: `<img
|
7
|
+
src="https://my-matrix.squiz.net/?a=this-is-actually-ignored"
|
8
|
+
data-dam-object-id="5ce8e8dc-1adc-4254-87a8-d1f5a5c9045a"
|
9
|
+
data-dam-system-identifier="byder001"
|
10
|
+
data-dam-system-type="bynder"
|
11
|
+
data-dam-additional='{"variant":"xyz"}'
|
12
|
+
/>`,
|
13
|
+
});
|
14
|
+
|
15
|
+
expect(getJsonContent()).toEqual({
|
16
|
+
type: 'paragraph',
|
17
|
+
attrs: expect.any(Object),
|
18
|
+
content: [
|
19
|
+
{
|
20
|
+
type: 'DAMImage',
|
21
|
+
attrs: {
|
22
|
+
damObjectId: '5ce8e8dc-1adc-4254-87a8-d1f5a5c9045a',
|
23
|
+
damSystemIdentifier: 'byder001',
|
24
|
+
damSystemType: 'bynder',
|
25
|
+
damAdditional: {
|
26
|
+
variant: 'xyz',
|
27
|
+
},
|
28
|
+
src: 'https://default-resource/',
|
29
|
+
},
|
30
|
+
},
|
31
|
+
],
|
32
|
+
});
|
33
|
+
});
|
34
|
+
|
35
|
+
it('Resolves to a regular image if HTML content is missing some of the expected attributes', async () => {
|
36
|
+
const { getJsonContent } = await renderWithEditor(null, {
|
37
|
+
content:
|
38
|
+
'<img src="https://my-matrix.squiz.net/?a=123" data-dam-object-id="5ce8e8dc-1adc-4254-87a8-d1f5a5c9045a" />',
|
39
|
+
});
|
40
|
+
|
41
|
+
expect(getJsonContent()).toEqual({
|
42
|
+
type: 'paragraph',
|
43
|
+
attrs: expect.any(Object),
|
44
|
+
content: [
|
45
|
+
{
|
46
|
+
type: 'image',
|
47
|
+
attrs: expect.objectContaining({
|
48
|
+
src: 'https://my-matrix.squiz.net/?a=123',
|
49
|
+
}),
|
50
|
+
},
|
51
|
+
],
|
52
|
+
});
|
53
|
+
});
|
54
|
+
|
55
|
+
it('Outputs expected HTML', async () => {
|
56
|
+
const { getHtmlContent } = await renderWithEditor(null, {
|
57
|
+
content: {
|
58
|
+
type: 'paragraph',
|
59
|
+
content: [
|
60
|
+
{
|
61
|
+
type: 'DAMImage',
|
62
|
+
attrs: {
|
63
|
+
damObjectId: '5ce8e8dc-1adc-4254-87a8-d1f5a5c9045a',
|
64
|
+
damSystemIdentifier: 'byder001',
|
65
|
+
damSystemType: 'bynder',
|
66
|
+
damAdditional: {
|
67
|
+
variant: 'xyz',
|
68
|
+
},
|
69
|
+
},
|
70
|
+
},
|
71
|
+
],
|
72
|
+
},
|
73
|
+
});
|
74
|
+
|
75
|
+
expect(getHtmlContent()).toEqual(
|
76
|
+
'<img ' +
|
77
|
+
'src="https://default-resource/" ' +
|
78
|
+
'data-dam-object-id="5ce8e8dc-1adc-4254-87a8-d1f5a5c9045a" ' +
|
79
|
+
'data-dam-system-identifier="byder001" ' +
|
80
|
+
'data-dam-system-type="bynder" ' +
|
81
|
+
'data-dam-additional="{"variant":"xyz"}" ' +
|
82
|
+
'draggable="true">' +
|
83
|
+
'<img class="ProseMirror-separator" alt="">' +
|
84
|
+
'<br class="ProseMirror-trailingBreak">',
|
85
|
+
);
|
86
|
+
});
|
87
|
+
});
|
@@ -0,0 +1,119 @@
|
|
1
|
+
import {
|
2
|
+
ApplySchemaAttributes,
|
3
|
+
command,
|
4
|
+
extension,
|
5
|
+
ExtensionPriority,
|
6
|
+
ExtensionTag,
|
7
|
+
isElementDomNode,
|
8
|
+
NodeExtension,
|
9
|
+
NodeExtensionSpec,
|
10
|
+
NodeSpecOverride,
|
11
|
+
omitExtraAttributes,
|
12
|
+
CommandFunction,
|
13
|
+
} from '@remirror/core';
|
14
|
+
import { getTextSelection } from 'remirror';
|
15
|
+
import { NodeName } from '../Extensions';
|
16
|
+
|
17
|
+
export type DAMImageAttributes = {
|
18
|
+
damObjectId: string;
|
19
|
+
damSystemIdentifier: string;
|
20
|
+
damSystemType: string;
|
21
|
+
damAdditional?: {
|
22
|
+
variant: string;
|
23
|
+
};
|
24
|
+
url: string;
|
25
|
+
};
|
26
|
+
|
27
|
+
@extension({
|
28
|
+
defaultOptions: {},
|
29
|
+
defaultPriority: ExtensionPriority.High,
|
30
|
+
})
|
31
|
+
export class DAMImageExtension extends NodeExtension {
|
32
|
+
get name() {
|
33
|
+
return NodeName.DAMImage as const;
|
34
|
+
}
|
35
|
+
|
36
|
+
createTags() {
|
37
|
+
return [ExtensionTag.InlineNode, ExtensionTag.Media];
|
38
|
+
}
|
39
|
+
|
40
|
+
createNodeSpec(extra: ApplySchemaAttributes, override: NodeSpecOverride): NodeExtensionSpec {
|
41
|
+
return {
|
42
|
+
inline: true,
|
43
|
+
draggable: true,
|
44
|
+
selectable: true,
|
45
|
+
...override,
|
46
|
+
attrs: {
|
47
|
+
...extra.defaults(),
|
48
|
+
damObjectId: {},
|
49
|
+
damSystemIdentifier: {},
|
50
|
+
damSystemType: {},
|
51
|
+
damAdditional: { default: undefined },
|
52
|
+
src: { default: '' },
|
53
|
+
},
|
54
|
+
parseDOM: [
|
55
|
+
{
|
56
|
+
tag: 'img[data-dam-object-id]',
|
57
|
+
getAttrs: (node) => {
|
58
|
+
if (!isElementDomNode(node)) {
|
59
|
+
return false;
|
60
|
+
}
|
61
|
+
|
62
|
+
const damObjectId = node.getAttribute('data-dam-object-id');
|
63
|
+
const damSystemIdentifier = node.getAttribute('data-dam-system-identifier');
|
64
|
+
const damSystemType = node.getAttribute('data-dam-system-type');
|
65
|
+
let damAdditional = node.getAttribute('data-dam-additional') || undefined;
|
66
|
+
|
67
|
+
if (!damObjectId || !damSystemIdentifier || !damSystemType) {
|
68
|
+
return false;
|
69
|
+
}
|
70
|
+
|
71
|
+
if (damAdditional) {
|
72
|
+
damAdditional = JSON.parse(damAdditional);
|
73
|
+
}
|
74
|
+
|
75
|
+
return {
|
76
|
+
...extra.parse(node),
|
77
|
+
damObjectId,
|
78
|
+
damSystemIdentifier,
|
79
|
+
damSystemType,
|
80
|
+
damAdditional,
|
81
|
+
src: '',
|
82
|
+
};
|
83
|
+
},
|
84
|
+
},
|
85
|
+
],
|
86
|
+
toDOM: (node) => {
|
87
|
+
const { damObjectId, damSystemIdentifier, damSystemType, damAdditional, src, ...rest } = omitExtraAttributes(
|
88
|
+
node.attrs,
|
89
|
+
extra,
|
90
|
+
);
|
91
|
+
|
92
|
+
return [
|
93
|
+
'img',
|
94
|
+
{
|
95
|
+
...extra.dom(node),
|
96
|
+
...rest,
|
97
|
+
src,
|
98
|
+
'data-dam-object-id': damObjectId,
|
99
|
+
'data-dam-system-identifier': damSystemIdentifier,
|
100
|
+
'data-dam-system-type': damSystemType,
|
101
|
+
'data-dam-additional': damAdditional ? JSON.stringify(damAdditional) : undefined,
|
102
|
+
},
|
103
|
+
];
|
104
|
+
},
|
105
|
+
};
|
106
|
+
}
|
107
|
+
|
108
|
+
@command()
|
109
|
+
insertDAMImage(attrs: DAMImageAttributes): CommandFunction {
|
110
|
+
return ({ tr, dispatch }) => {
|
111
|
+
const { from, to } = getTextSelection(tr.selection, tr.doc);
|
112
|
+
const node = this.type.create({ ...attrs, src: attrs.url });
|
113
|
+
|
114
|
+
dispatch?.(tr.replaceRangeWith(from, to, node));
|
115
|
+
|
116
|
+
return true;
|
117
|
+
};
|
118
|
+
}
|
119
|
+
}
|
@@ -0,0 +1,140 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
export const ICON_AI = (
|
4
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
5
|
+
<g id="sq-AI-icon" clipPath="url(#clip0_3060_44545)">
|
6
|
+
<g id="circle" filter="url(#filter0_dd_3060_44545)">
|
7
|
+
<rect x="2" y="2" width="20" height="20" rx="10" fill="url(#paint0_linear_3060_44545)" />
|
8
|
+
<rect x="1.5" y="1.5" width="21" height="21" rx="10.5" stroke="white" />
|
9
|
+
</g>
|
10
|
+
<g id="Vector" filter="url(#filter1_d_3060_44545)">
|
11
|
+
<path
|
12
|
+
d="M11.2084 9.79157L10.1219 7.39977C9.88269 6.86674 9.11731 6.86674 8.87813 7.39977L7.79157 9.79157L5.39977 10.8781C4.86674 11.1241 4.86674 11.8827 5.39977 12.1219L7.79157 13.2084L8.87813 15.6002C9.12415 16.1333 9.88269 16.1333 10.1219 15.6002L11.2084 13.2084L13.6002 12.1219C14.1333 11.8759 14.1333 11.1173 13.6002 10.8781L11.2084 9.79157Z"
|
13
|
+
fill="white"
|
14
|
+
/>
|
15
|
+
</g>
|
16
|
+
<g id="Vector_2" opacity="0.8" filter="url(#filter2_d_3060_44545)">
|
17
|
+
<path
|
18
|
+
d="M17.4491 10.4491L16.8493 11.7779C16.7126 12.074 16.2874 12.074 16.1507 11.7779L15.5509 10.4415L14.2221 9.84169C13.926 9.70501 13.926 9.2874 14.2221 9.15072L15.5585 8.55087L16.1583 7.2221C16.295 6.92597 16.7126 6.92597 16.8493 7.2221L17.4491 8.55847L18.7779 9.15831C19.074 9.29499 19.074 9.7126 18.7779 9.84928L17.4491 10.4491Z"
|
19
|
+
fill="white"
|
20
|
+
/>
|
21
|
+
</g>
|
22
|
+
<g id="Ellipse 55" opacity="0.8" filter="url(#filter3_d_3060_44545)">
|
23
|
+
<circle cx="14.5" cy="16.5" r="1.5" fill="white" />
|
24
|
+
</g>
|
25
|
+
</g>
|
26
|
+
<defs>
|
27
|
+
<filter
|
28
|
+
id="filter0_dd_3060_44545"
|
29
|
+
x="0"
|
30
|
+
y="0"
|
31
|
+
width="24"
|
32
|
+
height="24"
|
33
|
+
filterUnits="userSpaceOnUse"
|
34
|
+
colorInterpolationFilters="sRGB"
|
35
|
+
>
|
36
|
+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
37
|
+
<feColorMatrix
|
38
|
+
in="SourceAlpha"
|
39
|
+
type="matrix"
|
40
|
+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
41
|
+
result="hardAlpha"
|
42
|
+
/>
|
43
|
+
<feOffset dx="1" dy="1" />
|
44
|
+
<feComposite in2="hardAlpha" operator="out" />
|
45
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.2 0 0 0 0 0.709941 0 0 0 0 1 0 0 0 0.64 0" />
|
46
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3060_44545" />
|
47
|
+
<feColorMatrix
|
48
|
+
in="SourceAlpha"
|
49
|
+
type="matrix"
|
50
|
+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
51
|
+
result="hardAlpha"
|
52
|
+
/>
|
53
|
+
<feOffset dx="-1" dy="-1" />
|
54
|
+
<feComposite in2="hardAlpha" operator="out" />
|
55
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.96 0 0 0 0 0.76 0 0 0 0 0.48 0 0 0 0.64 0" />
|
56
|
+
<feBlend mode="normal" in2="effect1_dropShadow_3060_44545" result="effect2_dropShadow_3060_44545" />
|
57
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_3060_44545" result="shape" />
|
58
|
+
</filter>
|
59
|
+
<filter
|
60
|
+
id="filter1_d_3060_44545"
|
61
|
+
x="5"
|
62
|
+
y="7"
|
63
|
+
width="9"
|
64
|
+
height="10"
|
65
|
+
filterUnits="userSpaceOnUse"
|
66
|
+
colorInterpolationFilters="sRGB"
|
67
|
+
>
|
68
|
+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
69
|
+
<feColorMatrix
|
70
|
+
in="SourceAlpha"
|
71
|
+
type="matrix"
|
72
|
+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
73
|
+
result="hardAlpha"
|
74
|
+
/>
|
75
|
+
<feOffset dy="1" />
|
76
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0" />
|
77
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3060_44545" />
|
78
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3060_44545" result="shape" />
|
79
|
+
</filter>
|
80
|
+
<filter
|
81
|
+
id="filter2_d_3060_44545"
|
82
|
+
x="14"
|
83
|
+
y="7"
|
84
|
+
width="5"
|
85
|
+
height="6.30682"
|
86
|
+
filterUnits="userSpaceOnUse"
|
87
|
+
colorInterpolationFilters="sRGB"
|
88
|
+
>
|
89
|
+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
90
|
+
<feColorMatrix
|
91
|
+
in="SourceAlpha"
|
92
|
+
type="matrix"
|
93
|
+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
94
|
+
result="hardAlpha"
|
95
|
+
/>
|
96
|
+
<feOffset dy="1.30682" />
|
97
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0" />
|
98
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3060_44545" />
|
99
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3060_44545" result="shape" />
|
100
|
+
</filter>
|
101
|
+
<filter
|
102
|
+
id="filter3_d_3060_44545"
|
103
|
+
x="13"
|
104
|
+
y="15"
|
105
|
+
width="3"
|
106
|
+
height="4.32373"
|
107
|
+
filterUnits="userSpaceOnUse"
|
108
|
+
colorInterpolationFilters="sRGB"
|
109
|
+
>
|
110
|
+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
111
|
+
<feColorMatrix
|
112
|
+
in="SourceAlpha"
|
113
|
+
type="matrix"
|
114
|
+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
115
|
+
result="hardAlpha"
|
116
|
+
/>
|
117
|
+
<feOffset dy="1.32373" />
|
118
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.16 0" />
|
119
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3060_44545" />
|
120
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3060_44545" result="shape" />
|
121
|
+
</filter>
|
122
|
+
<linearGradient
|
123
|
+
id="paint0_linear_3060_44545"
|
124
|
+
x1="6.54545"
|
125
|
+
y1="3.81818"
|
126
|
+
x2="17"
|
127
|
+
y2="20.1818"
|
128
|
+
gradientUnits="userSpaceOnUse"
|
129
|
+
>
|
130
|
+
<stop stopColor="#F5D6AB" />
|
131
|
+
<stop offset="0.251162" stopColor="#F895A7" />
|
132
|
+
<stop offset="0.585" stopColor="#77A1F1" />
|
133
|
+
<stop offset="0.861262" stopColor="#68C8FF" />
|
134
|
+
</linearGradient>
|
135
|
+
<clipPath id="clip0_3060_44545">
|
136
|
+
<rect width="24" height="24" fill="white" />
|
137
|
+
</clipPath>
|
138
|
+
</defs>
|
139
|
+
</svg>
|
140
|
+
);
|
package/src/index.scss
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
@import 'tailwindcss/components';
|
4
4
|
@import 'tailwindcss/utilities';
|
5
5
|
|
6
|
+
@import '@squiz/sds/lib/package.css';
|
7
|
+
|
6
8
|
/* So we can use icons inside of FTE content */
|
7
9
|
@import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded';
|
8
10
|
|
@@ -22,3 +24,5 @@
|
|
22
24
|
@import './ui/CollapseBox/collapseBox';
|
23
25
|
|
24
26
|
@import './ui/Modal/modal';
|
27
|
+
|
28
|
+
@import './EditorToolbar/Tools/ContentTools/content-tools';
|