@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
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 2.2.0
|
4
|
+
|
5
|
+
### Minor Changes
|
6
|
+
|
7
|
+
- 985e1a5: ### Updates to DAM asset resolver package
|
8
|
+
|
9
|
+
**Changes:**
|
10
|
+
|
11
|
+
- Updated DAM resolver package versions.
|
12
|
+
- Updated Json validation schema so multiple resolvers can be used for SquizImage type.
|
13
|
+
- Updated higher order node map so Dam images could be resolved to simple url.
|
14
|
+
- Updated page builder resolver to handle DAM images
|
15
|
+
|
16
|
+
### Patch Changes
|
17
|
+
|
18
|
+
- Updated dependencies [985e1a5]
|
19
|
+
- @squiz/dx-json-schema-lib@1.72.0
|
20
|
+
|
21
|
+
## 2.1.0
|
22
|
+
|
23
|
+
### Minor Changes
|
24
|
+
|
25
|
+
- 37094f9: Added content tools dropdown & context to FTE
|
26
|
+
|
3
27
|
## 2.0.1
|
4
28
|
|
5
29
|
### Patch Changes
|
package/demo/App.tsx
CHANGED
@@ -81,6 +81,11 @@ function App() {
|
|
81
81
|
<a href="https://www.google.com"><strong>Mr Bean</strong></a>, nice to <a href="https://www.google.com">meet you</a>.
|
82
82
|
<img src="https://media2.giphy.com/media/3o6ozsIxg5legYvggo/giphy.gif" height="150" width="200"/>
|
83
83
|
<img data-matrix-asset-id="1" data-matrix-identifier="matrixIdentifier" data-matrix-domain="https://matrix-domain.squiz.net">
|
84
|
+
<img
|
85
|
+
data-dam-object-id="CA87B42E-9410-402F-B3669D6900DD0793"
|
86
|
+
data-dam-system-identifier="byder001"
|
87
|
+
data-dam-system-type="bynder"
|
88
|
+
>
|
84
89
|
</p>
|
85
90
|
`}
|
86
91
|
onChange={handleEditorChange}
|
package/demo/AppContext.tsx
CHANGED
@@ -5,66 +5,126 @@ import MatrixResourceBrowserPlugin, {
|
|
5
5
|
Resource,
|
6
6
|
ResourceReference,
|
7
7
|
} from '@squiz/matrix-resource-browser-plugin';
|
8
|
+
import DamResourceBrowserPlugin from '@squiz/dam-resource-browser-plugin';
|
8
9
|
import { EditorContext } from '../src';
|
9
10
|
import resources from './resources.json';
|
10
11
|
import sources from './sources.json';
|
11
12
|
import { ResolveNodeType } from '../src/types';
|
13
|
+
import { AiServiceProvider } from '@squiz/dxp-ai-client-react';
|
14
|
+
import { QueryClientProvider, QueryClient } from 'react-query';
|
12
15
|
|
13
16
|
export type AppContextProps = PropsWithChildren;
|
14
|
-
export const AppContext = ({ children }: AppContextProps) => (
|
15
|
-
<ResourceBrowserContextProvider
|
16
|
-
value={{
|
17
|
-
onRequestSources: (): Promise<ResourceBrowserSource[]> =>
|
18
|
-
Promise.resolve([
|
19
|
-
{
|
20
|
-
name: 'Matrix',
|
21
|
-
id: 'matrixIdentifier',
|
22
|
-
type: 'matrix',
|
23
|
-
},
|
24
|
-
]),
|
25
|
-
plugins: [
|
26
|
-
MatrixResourceBrowserPlugin({
|
27
|
-
onRequestSources: (): Promise<Source> => Promise.resolve(sources),
|
28
|
-
onRequestChildren: (source: Source, resource: Resource | null): Promise<Resource[]> =>
|
29
|
-
Promise.resolve(resource._children || resources),
|
30
|
-
onRequestResource: (reference: ResourceReference): Promise<Resource | null> => {
|
31
|
-
const flattenResources = (resources: unknown[]) => {
|
32
|
-
return [
|
33
|
-
...resources,
|
34
|
-
...resources.flatMap((resource) =>
|
35
|
-
'_children' in resource ? flattenResources(resource._children) : [],
|
36
|
-
),
|
37
|
-
];
|
38
|
-
};
|
39
17
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
18
|
+
// Set up React query for testing
|
19
|
+
const createTestQueryClient = () =>
|
20
|
+
new QueryClient({
|
21
|
+
defaultOptions: {
|
22
|
+
queries: {
|
23
|
+
retry: false,
|
24
|
+
},
|
25
|
+
},
|
26
|
+
});
|
27
|
+
|
28
|
+
const testQueryClient = createTestQueryClient();
|
29
|
+
|
30
|
+
export const AppContext = ({ children }: AppContextProps) => (
|
31
|
+
<QueryClientProvider client={testQueryClient}>
|
32
|
+
<ResourceBrowserContextProvider
|
49
33
|
value={{
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
34
|
+
onRequestSources: (): Promise<ResourceBrowserSource[]> =>
|
35
|
+
Promise.resolve([
|
36
|
+
{
|
37
|
+
name: 'Matrix',
|
38
|
+
id: 'matrixIdentifier',
|
39
|
+
type: 'matrix',
|
40
|
+
},
|
41
|
+
{
|
42
|
+
name: 'Bynder',
|
43
|
+
id: 'byder001',
|
44
|
+
type: 'dam',
|
45
|
+
configuration: {
|
46
|
+
externalType: 'bynder',
|
47
|
+
},
|
48
|
+
},
|
49
|
+
]),
|
50
|
+
plugins: [
|
51
|
+
DamResourceBrowserPlugin(),
|
52
|
+
MatrixResourceBrowserPlugin({
|
53
|
+
onRequestSources: (): Promise<Source> => Promise.resolve(sources),
|
54
|
+
onRequestChildren: (source: Source, resource: Resource | null): Promise<Resource[]> =>
|
55
|
+
Promise.resolve(resource._children || resources),
|
56
|
+
onRequestResource: (reference: ResourceReference): Promise<Resource | null> => {
|
57
|
+
const flattenResources = (resources: unknown[]) => {
|
58
|
+
return [
|
59
|
+
...resources,
|
60
|
+
...resources.flatMap((resource) =>
|
61
|
+
'_children' in resource ? flattenResources(resource._children) : [],
|
62
|
+
),
|
63
|
+
];
|
64
|
+
};
|
61
65
|
|
62
|
-
|
63
|
-
|
64
|
-
|
66
|
+
return Promise.resolve(
|
67
|
+
flattenResources(resources).find((resource) => resource.id === reference.resource) || null,
|
68
|
+
);
|
69
|
+
},
|
70
|
+
}),
|
71
|
+
],
|
65
72
|
}}
|
66
73
|
>
|
67
|
-
|
68
|
-
|
69
|
-
|
74
|
+
<AiServiceProvider
|
75
|
+
clientConfig={{
|
76
|
+
// We are using a simple Express proxy here for testing locally in FTE
|
77
|
+
// You can find this proxy here: https://gitlab.squiz.net/tgrob/dxp-ai-client-proxy
|
78
|
+
baseUrl: 'http://localhost:7777',
|
79
|
+
baseApiParams: {
|
80
|
+
headers: {
|
81
|
+
// You will need to create an API key to test this in FTE, details can be found here:
|
82
|
+
// https://squizgroup.atlassian.net/wiki/spaces/PRODUCT/pages/2895119377/FEaaS+Playbook+Configuring+a+Matrix+instance+for+the+Component+Service#Create-an-API-key
|
83
|
+
'x-api-key': '',
|
84
|
+
},
|
85
|
+
},
|
86
|
+
customFetch: async (input, init) => {
|
87
|
+
const response = await fetch(input, init);
|
88
|
+
return response;
|
89
|
+
},
|
90
|
+
}}
|
91
|
+
tags={['tag1']}
|
92
|
+
>
|
93
|
+
<EditorContext.Provider
|
94
|
+
value={{
|
95
|
+
matrix: {
|
96
|
+
matrixDomain: 'https://matrix-domain.squiz.net',
|
97
|
+
},
|
98
|
+
resolveNodeToUrl: (node: ResolveNodeType): Promise<string> => {
|
99
|
+
console.log(`resolveNodeToUrl ${JSON.stringify(node)}`);
|
100
|
+
// Matrix Resource
|
101
|
+
if (node.matrixAssetId) {
|
102
|
+
const nodeResource = node.matrixAssetId;
|
103
|
+
const flattenResources = (resources: unknown[]) => {
|
104
|
+
return [
|
105
|
+
...resources,
|
106
|
+
...resources.flatMap((resource) =>
|
107
|
+
'_children' in resource ? flattenResources(resource._children) : [],
|
108
|
+
),
|
109
|
+
];
|
110
|
+
};
|
111
|
+
|
112
|
+
const resource = flattenResources(resources).find((resource) => resource.id === nodeResource);
|
113
|
+
return Promise.resolve(resource.url || '');
|
114
|
+
}
|
115
|
+
|
116
|
+
// DAM Resource
|
117
|
+
if (node.damSystemType === 'bynder') {
|
118
|
+
return Promise.resolve('https://squiz-sandbox.bynder.com/m/4c92419541508c37/original/rx77.jpg');
|
119
|
+
}
|
120
|
+
|
121
|
+
return Promise.resolve('');
|
122
|
+
},
|
123
|
+
}}
|
124
|
+
>
|
125
|
+
{children}
|
126
|
+
</EditorContext.Provider>
|
127
|
+
</AiServiceProvider>
|
128
|
+
</ResourceBrowserContextProvider>
|
129
|
+
</QueryClientProvider>
|
70
130
|
);
|
package/jest.config.ts
CHANGED
@@ -23,5 +23,5 @@ export default {
|
|
23
23
|
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|css|scss)$':
|
24
24
|
'<rootDir>/file-transformer.js',
|
25
25
|
},
|
26
|
-
setupFilesAfterEnv: ['<rootDir>/jest.bootstrap.ts'],
|
26
|
+
setupFilesAfterEnv: ['<rootDir>/jest.bootstrap.ts', '<rootDir>/jest.setup.ts'],
|
27
27
|
};
|
package/jest.setup.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
import { useAiService } from '@squiz/dxp-ai-client-react';
|
2
|
+
|
3
|
+
// Mock the module
|
4
|
+
jest.mock('@squiz/dxp-ai-client-react', () => ({
|
5
|
+
useAiService: jest.fn(),
|
6
|
+
}));
|
7
|
+
|
8
|
+
// Cast useAiService to a Jest mock
|
9
|
+
const mockedUseAiService = useAiService as jest.MockedFunction<typeof useAiService>;
|
10
|
+
|
11
|
+
beforeEach(() => {
|
12
|
+
// Provide the mock implementation before each test
|
13
|
+
mockedUseAiService.mockReturnValue({
|
14
|
+
contentTools: [],
|
15
|
+
} as any);
|
16
|
+
});
|
@@ -52,7 +52,7 @@ const FloatingToolbar = () => {
|
|
52
52
|
extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, { key: "italic" }),
|
53
53
|
extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, { key: "underline" }),
|
54
54
|
];
|
55
|
-
if (active.image() || active.assetImage()) {
|
55
|
+
if (active.image() || active.assetImage() || active.DAMImage()) {
|
56
56
|
buttons = [react_1.default.createElement(ImageButton_1.default, { key: "add-image", inPopover: true })];
|
57
57
|
}
|
58
58
|
else if (marks?.[Extensions_1.MarkName.Link].isExclusivelyActive || marks?.[Extensions_1.MarkName.AssetLink].isExclusivelyActive) {
|
@@ -21,6 +21,7 @@ const ClearFormattingButton_1 = __importDefault(require("./Tools/ClearFormatting
|
|
21
21
|
const ListButtons_1 = __importDefault(require("./Tools/Lists/ListButtons"));
|
22
22
|
const HorizontalLineButton_1 = __importDefault(require("./Tools/HorizontalLine/HorizontalLineButton"));
|
23
23
|
const TableButton_1 = __importDefault(require("./Tools/Table/TableButton"));
|
24
|
+
const ContentToolsDropdown_1 = __importDefault(require("./Tools/ContentTools/ContentToolsDropdown"));
|
24
25
|
const hooks_1 = require("../hooks");
|
25
26
|
const Toolbar = ({ isVisible, enableTableTool }) => {
|
26
27
|
const extensionNames = (0, hooks_1.useExtensionNames)();
|
@@ -42,6 +43,7 @@ const Toolbar = ({ isVisible, enableTableTool }) => {
|
|
42
43
|
react_1.default.createElement(RemoveLinkButton_1.default, null))),
|
43
44
|
extensionNames.image && react_1.default.createElement(ImageButton_1.default, null),
|
44
45
|
extensionNames.clearFormatting && react_1.default.createElement(ClearFormattingButton_1.default, null),
|
45
|
-
enableTableTool && extensionNames.table && react_1.default.createElement(TableButton_1.default, null)
|
46
|
+
enableTableTool && extensionNames.table && react_1.default.createElement(TableButton_1.default, null),
|
47
|
+
react_1.default.createElement(ContentToolsDropdown_1.default, null))));
|
46
48
|
};
|
47
49
|
exports.Toolbar = Toolbar;
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
const AiIcon_1 = require("../../../Icons/AiIcon");
|
8
|
+
const sds_1 = require("@squiz/sds");
|
9
|
+
const dxp_ai_client_react_1 = require("@squiz/dxp-ai-client-react");
|
10
|
+
const react_2 = require("@remirror/react");
|
11
|
+
const ContentToolsDropdown = () => {
|
12
|
+
const { contentTools } = (0, dxp_ai_client_react_1.useAiService)();
|
13
|
+
const dropdownItems = contentTools.map((item) => ({
|
14
|
+
items: [
|
15
|
+
{
|
16
|
+
action: () => alert(JSON.stringify(item, null, 2)),
|
17
|
+
key: item.id,
|
18
|
+
label: react_1.default.createElement("span", null, item.name),
|
19
|
+
},
|
20
|
+
],
|
21
|
+
key: item.id,
|
22
|
+
}));
|
23
|
+
// No content tools to show, don't show dropdown at all
|
24
|
+
if (contentTools.length === 0) {
|
25
|
+
return null;
|
26
|
+
}
|
27
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
28
|
+
react_1.default.createElement(react_2.VerticalDivider, null),
|
29
|
+
react_1.default.createElement(sds_1.Dropdown, { title: "Content tools", "aria-label": "Content tools", buttonProps: {
|
30
|
+
format: sds_1.BUTTON_FORMAT_TRANSPARENT,
|
31
|
+
icon: AiIcon_1.ICON_AI,
|
32
|
+
theme: sds_1.BUTTON_THEME_DEFAULT,
|
33
|
+
}, className: "content-tools-dropdown", dropdownPosition: sds_1.DROPDOWN_POSITION_RIGHT, heading: 'Rewrite to...', sections: dropdownItems })));
|
34
|
+
};
|
35
|
+
exports.default = ContentToolsDropdown;
|
@@ -3,11 +3,12 @@ import { SubmitHandler } from 'react-hook-form';
|
|
3
3
|
import { ImageAttributes } from '@remirror/extension-image/dist-types/image-extension';
|
4
4
|
import { NodeName } from '../../../../Extensions/Extensions';
|
5
5
|
import { AssetImageAttributes } from '../../../../Extensions/ImageExtension/AssetImageExtension';
|
6
|
+
import { DAMImageAttributes } from '../../../../Extensions/ImageExtension/DAMImageExtension';
|
6
7
|
import { DeepPartial } from '../../../../types';
|
7
8
|
export type ImageFormData = {
|
8
9
|
imageType: NodeName;
|
9
10
|
image: Pick<ImageAttributes, 'src' | 'alt' | 'width' | 'height'>;
|
10
|
-
|
11
|
+
resourceImage: AssetImageAttributes & DAMImageAttributes;
|
11
12
|
};
|
12
13
|
export type FormProps = {
|
13
14
|
data: DeepPartial<ImageFormData>;
|
@@ -37,16 +37,22 @@ const InsertLinkRounded_1 = __importDefault(require("@mui/icons-material/InsertL
|
|
37
37
|
const Extensions_1 = require("../../../../Extensions/Extensions");
|
38
38
|
const validation_1 = require("../../../../utils/validation");
|
39
39
|
const Tabs_1 = require("../../../../ui/Tabs/Tabs");
|
40
|
-
const
|
40
|
+
const ResourceBrowserSelector_1 = require("../../../../ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector");
|
41
|
+
var ViewTypes;
|
42
|
+
(function (ViewTypes) {
|
43
|
+
ViewTypes["Resource"] = "Resource";
|
44
|
+
ViewTypes["URL"] = "URL";
|
45
|
+
})(ViewTypes || (ViewTypes = {}));
|
41
46
|
const imageTypeOptions = {
|
42
|
-
[
|
43
|
-
[
|
47
|
+
[ViewTypes.Resource]: { label: 'From source' },
|
48
|
+
[ViewTypes.URL]: { label: 'From URL' },
|
44
49
|
};
|
45
50
|
const ImageForm = ({ data, onSubmit }) => {
|
46
51
|
const { control, register, handleSubmit, setValue, watch, formState: { errors }, } = (0, react_hook_form_1.useForm)({
|
47
52
|
defaultValues: data,
|
48
53
|
});
|
49
|
-
const imageType = watch('imageType')
|
54
|
+
const imageType = watch('imageType');
|
55
|
+
const [viewType, setViewType] = (0, react_1.useState)(ViewTypes.Resource);
|
50
56
|
const [aspectRatioFromWidth, setAspectRatioFromWidth] = (0, react_1.useState)(9 / 16);
|
51
57
|
const [aspectRatioFromHeight, setAspectRatioFromHeight] = (0, react_1.useState)(16 / 9);
|
52
58
|
const [aspectRatioLocked, setAspectRatioLocked] = (0, react_1.useState)(true);
|
@@ -86,10 +92,32 @@ const ImageForm = ({ data, onSubmit }) => {
|
|
86
92
|
const toggleAspectRatio = () => {
|
87
93
|
setAspectRatioLocked(!aspectRatioLocked);
|
88
94
|
};
|
95
|
+
(0, react_1.useEffect)(() => {
|
96
|
+
if (imageType === Extensions_1.NodeName.Image) {
|
97
|
+
setViewType(ViewTypes.URL);
|
98
|
+
}
|
99
|
+
else {
|
100
|
+
setViewType(ViewTypes.Resource);
|
101
|
+
}
|
102
|
+
}, [imageType, setViewType]);
|
103
|
+
const handleChangeViewType = (0, react_1.useCallback)((value) => {
|
104
|
+
setViewType(value);
|
105
|
+
// If its the URL field type we know what the imageType should be
|
106
|
+
if (value === ViewTypes.URL) {
|
107
|
+
console.log(`handleChangeViewType: ${value} NodeName.Image`);
|
108
|
+
setValue('imageType', Extensions_1.NodeName.Image);
|
109
|
+
}
|
110
|
+
else {
|
111
|
+
// Need a value here and this is the assumed default elsewhere
|
112
|
+
// Will be set again later once Resource Browser returns a resource value
|
113
|
+
console.log(`handleChangeViewType: ${value} NodeName.AssetImage`);
|
114
|
+
setValue('imageType', Extensions_1.NodeName.AssetImage);
|
115
|
+
}
|
116
|
+
}, [setViewType, setValue]);
|
89
117
|
return (react_1.default.createElement("form", { className: "squiz-fte-form", onSubmit: handleSubmit(onSubmit) },
|
90
118
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-4" },
|
91
|
-
react_1.default.createElement(Tabs_1.Tabs, { value: imageType, options: imageTypeOptions, onChange:
|
92
|
-
|
119
|
+
react_1.default.createElement(Tabs_1.Tabs, { value: imageType === Extensions_1.NodeName.Image ? ViewTypes.URL : ViewTypes.Resource, options: imageTypeOptions, onChange: handleChangeViewType })),
|
120
|
+
viewType === ViewTypes.URL && (react_1.default.createElement(react_1.default.Fragment, null,
|
93
121
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
94
122
|
react_1.default.createElement(Input_1.Input, { label: "Source", required: true, error: errors?.image?.src?.message, ...register('image.src', {
|
95
123
|
onChange: setDimensionsFromURL,
|
@@ -135,9 +163,24 @@ const ImageForm = ({ data, onSubmit }) => {
|
|
135
163
|
}) }))),
|
136
164
|
react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
137
165
|
react_1.default.createElement(Input_1.Input, { label: "Alternative description", error: errors?.image?.alt?.message, ...register('image.alt') })))),
|
138
|
-
|
139
|
-
react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: "
|
140
|
-
validate: (
|
141
|
-
|
166
|
+
viewType === ViewTypes.Resource && (react_1.default.createElement("div", { className: "squiz-fte-form-group mb-2" },
|
167
|
+
react_1.default.createElement(react_hook_form_1.Controller, { control: control, name: "resourceImage", rules: {
|
168
|
+
validate: (value) => {
|
169
|
+
const matrixValidation = (0, validation_1.hasProperties)('An image must be selected', [
|
170
|
+
'matrixIdentifier',
|
171
|
+
'matrixAssetId',
|
172
|
+
])(value);
|
173
|
+
const damValidation = (0, validation_1.hasProperties)('An image must be selected', [
|
174
|
+
'damObjectId',
|
175
|
+
'damSystemIdentifier',
|
176
|
+
])(value);
|
177
|
+
// One of the two needs to validate
|
178
|
+
return matrixValidation && damValidation;
|
179
|
+
},
|
180
|
+
}, render: ({ field: { onChange, value }, fieldState: { error } }) => (react_1.default.createElement(ResourceBrowserSelector_1.ResourceBrowserSelector, { modalTitle: "Insert image", allowedTypes: ['image'], value: value, onChange: (value) => {
|
181
|
+
console.log(`onChange: ${value}`);
|
182
|
+
setValue('imageType', value.target.value.nodeType);
|
183
|
+
onChange(value);
|
184
|
+
}, error: error?.message })) })))));
|
142
185
|
};
|
143
186
|
exports.default = ImageForm;
|
@@ -35,23 +35,26 @@ const Extensions_1 = require("../../../Extensions/Extensions");
|
|
35
35
|
const getShortcutSymbol_1 = require("../../../utils/getShortcutSymbol");
|
36
36
|
const ImageButton = ({ inPopover = false }) => {
|
37
37
|
const [showModal, setShowModal] = (0, react_1.useState)(false);
|
38
|
-
const { insertImage, insertAssetImage } = (0, react_2.useCommands)();
|
38
|
+
const { insertImage, insertAssetImage, insertDAMImage } = (0, react_2.useCommands)();
|
39
39
|
const active = (0, react_2.useActive)();
|
40
40
|
const selection = (0, react_2.useCurrentSelection)();
|
41
41
|
// if the active selection is not an image, disable the button as it means it will be text
|
42
|
-
const disabled = (!selection.empty && !active.image() && !active.assetImage()) || active.codeBlock();
|
42
|
+
const disabled = (!selection.empty && !active.image() && !active.assetImage() && !active.DAMImage()) || active.codeBlock();
|
43
43
|
const handleClick = () => {
|
44
44
|
if (!showModal) {
|
45
45
|
setShowModal(true);
|
46
46
|
}
|
47
47
|
};
|
48
48
|
const insertImageFromData = (data) => {
|
49
|
-
const { imageType, image,
|
49
|
+
const { imageType, image, resourceImage } = data;
|
50
50
|
if (imageType === Extensions_1.NodeName.Image) {
|
51
51
|
insertImage(image);
|
52
52
|
}
|
53
|
+
else if (imageType === Extensions_1.NodeName.DAMImage) {
|
54
|
+
insertDAMImage(resourceImage);
|
55
|
+
}
|
53
56
|
else {
|
54
|
-
insertAssetImage(
|
57
|
+
insertAssetImage(resourceImage);
|
55
58
|
}
|
56
59
|
};
|
57
60
|
const handleSubmit = async (data) => {
|
@@ -70,7 +73,7 @@ const ImageButton = ({ inPopover = false }) => {
|
|
70
73
|
(0, react_2.useKeymap)('Mod-l', disabled ? () => false : handleShortcut);
|
71
74
|
}
|
72
75
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
73
|
-
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 (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+L)`, isDisabled: disabled }),
|
76
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleClick, isActive: active.image() || active.assetImage() || active.DAMImage(), icon: react_1.default.createElement(ImageRounded_1.default, null), label: `Image (${(0, getShortcutSymbol_1.getShortcutSymbol)()}+L)`, isDisabled: disabled }),
|
74
77
|
showModal && react_1.default.createElement(ImageModal_1.default, { onCancel: () => setShowModal(false), onSubmit: handleSubmit })));
|
75
78
|
};
|
76
79
|
exports.default = ImageButton;
|
@@ -16,7 +16,9 @@ const ImageModal = ({ onCancel, onSubmit }) => {
|
|
16
16
|
const formData = {
|
17
17
|
imageType: currentImage?.type.name === Extensions_1.NodeName.Image ? Extensions_1.NodeName.Image : Extensions_1.NodeName.AssetImage,
|
18
18
|
image: currentImage?.type?.name === Extensions_1.NodeName.Image ? currentImageAttrs : {},
|
19
|
-
|
19
|
+
resourceImage: currentImage?.type?.name === Extensions_1.NodeName.DAMImage || currentImage?.type?.name === Extensions_1.NodeName.AssetImage
|
20
|
+
? currentImageAttrs
|
21
|
+
: {},
|
20
22
|
};
|
21
23
|
return (react_1.default.createElement(FormModal_1.default, { title: "Image", icon: react_1.default.createElement(ImageRounded_1.default, null), onCancel: onCancel },
|
22
24
|
react_1.default.createElement(ImageForm_1.default, { data: formData, onSubmit: onSubmit })));
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
const react_1 = __importDefault(require("react"));
|
7
7
|
const react_2 = require("@remirror/react");
|
8
|
-
const react_components_1 = require("@remirror/react-components");
|
9
8
|
const Button_1 = __importDefault(require("../../../ui/Button/Button"));
|
10
9
|
const TableViewRounded_1 = __importDefault(require("@mui/icons-material/TableViewRounded"));
|
11
10
|
const TableButton = () => {
|
@@ -16,7 +15,6 @@ const TableButton = () => {
|
|
16
15
|
createTable({ rowsCount: 4, columnsCount: 3, withHeaderRow: false });
|
17
16
|
};
|
18
17
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
19
|
-
react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.table(), icon: react_1.default.createElement(TableViewRounded_1.default, null), label: "Insert table" })
|
20
|
-
react_1.default.createElement(react_components_1.VerticalDivider, null)));
|
18
|
+
react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.table(), icon: react_1.default.createElement(TableViewRounded_1.default, null), label: "Insert table" })));
|
21
19
|
};
|
22
20
|
exports.default = TableButton;
|
@@ -8,6 +8,7 @@ const LinkExtension_1 = require("./LinkExtension/LinkExtension");
|
|
8
8
|
const ImageExtension_1 = require("./ImageExtension/ImageExtension");
|
9
9
|
const CommandsExtension_1 = require("./CommandsExtension/CommandsExtension");
|
10
10
|
const AssetImageExtension_1 = require("./ImageExtension/AssetImageExtension");
|
11
|
+
const DAMImageExtension_1 = require("./ImageExtension/DAMImageExtension");
|
11
12
|
const CodeBlockExtension_1 = require("./CodeBlockExtension/CodeBlockExtension");
|
12
13
|
const ClearFormattingExtension_1 = require("./ClearFormattingExtension/ClearFormattingExtension");
|
13
14
|
const UnsupportedNodeExtension_1 = require("./UnsuportedExtension/UnsupportedNodeExtension");
|
@@ -19,6 +20,7 @@ var NodeName;
|
|
19
20
|
NodeName["Image"] = "image";
|
20
21
|
NodeName["CodeBlock"] = "codeBlock";
|
21
22
|
NodeName["AssetImage"] = "assetImage";
|
23
|
+
NodeName["DAMImage"] = "DAMImage";
|
22
24
|
NodeName["Text"] = "text";
|
23
25
|
NodeName["TableControllerCell"] = "tableControllerCell";
|
24
26
|
NodeName["tableCell"] = "tableCell";
|
@@ -50,6 +52,7 @@ const createExtensions = (context) => {
|
|
50
52
|
new AssetImageExtension_1.AssetImageExtension({
|
51
53
|
matrixDomain: context.matrix.matrixDomain,
|
52
54
|
}),
|
55
|
+
new DAMImageExtension_1.DAMImageExtension(),
|
53
56
|
new LinkExtension_1.LinkExtension(),
|
54
57
|
new AssetLinkExtension_1.AssetLinkExtension({
|
55
58
|
matrixDomain: context.matrix.matrixDomain,
|
@@ -25,6 +25,12 @@ let FetchUrlExtension = class FetchUrlExtension extends core_1.PlainExtension {
|
|
25
25
|
tr.replaceWith(pos, pos + node.nodeSize, newNode);
|
26
26
|
}));
|
27
27
|
}
|
28
|
+
if (node.type.name === Extensions_1.NodeName.DAMImage && node.attrs.src === '') {
|
29
|
+
promises.push(this.fetchAndReplace(node.attrs, (url) => {
|
30
|
+
const newNode = state.schema.nodes[Extensions_1.NodeName.DAMImage].create({ ...node.attrs, src: url });
|
31
|
+
tr.replaceWith(pos, pos + node.nodeSize, newNode);
|
32
|
+
}));
|
33
|
+
}
|
28
34
|
const assetLinkMark = this.findAssetLinkMark(node.marks);
|
29
35
|
if (node.type.name === 'text' && assetLinkMark) {
|
30
36
|
promises.push(this.fetchAndReplace(assetLinkMark.attrs, (url) => {
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { ApplySchemaAttributes, NodeExtension, NodeExtensionSpec, NodeSpecOverride, CommandFunction } from '@remirror/core';
|
2
|
+
import { NodeName } from '../Extensions';
|
3
|
+
export type DAMImageAttributes = {
|
4
|
+
damObjectId: string;
|
5
|
+
damSystemIdentifier: string;
|
6
|
+
damSystemType: string;
|
7
|
+
damAdditional?: {
|
8
|
+
variant: string;
|
9
|
+
};
|
10
|
+
url: string;
|
11
|
+
};
|
12
|
+
export declare class DAMImageExtension extends NodeExtension {
|
13
|
+
get name(): NodeName.DAMImage;
|
14
|
+
createTags(): ("inline" | "media")[];
|
15
|
+
createNodeSpec(extra: ApplySchemaAttributes, override: NodeSpecOverride): NodeExtensionSpec;
|
16
|
+
insertDAMImage(attrs: DAMImageAttributes): CommandFunction;
|
17
|
+
}
|