@_sh/strapi-plugin-ckeditor 2.1.3 → 3.0.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/README.md +373 -194
- package/admin/src/Input/components/Editor.js +133 -0
- package/admin/src/{components/Input/MediaLib/index.js → Input/components/MediaLib.js} +20 -23
- package/admin/src/Input/config/index.js +2 -0
- package/admin/src/Input/config/language.js +45 -0
- package/admin/src/Input/config/plugins.js +24 -0
- package/admin/src/Input/config/presets.js +18 -0
- package/admin/src/Input/config/styling.js +30 -0
- package/admin/src/Input/index.js +67 -0
- package/admin/src/{components/Input/CKEditor → Input}/plugins/StrapiMediaLib.js +8 -10
- package/admin/src/{components/Input/CKEditor → Input}/plugins/StrapiUploadAdapter.js +16 -34
- package/admin/src/{components/Input/CKEditor → Input}/plugins/index.js +1 -1
- package/admin/src/Input/presets/colors.js +122 -0
- package/admin/src/Input/presets/default.js +378 -0
- package/admin/src/Input/presets/index.js +7 -0
- package/admin/src/Input/theme/additional.js +189 -0
- package/admin/src/{components/Input/CKEditor → Input}/theme/common.js +18 -20
- package/admin/src/{components/Input/CKEditor → Input}/theme/dark.js +22 -18
- package/admin/src/{components/Input/CKEditor → Input}/theme/light.js +22 -17
- package/admin/src/index.js +76 -106
- package/admin/src/pluginId.js +7 -0
- package/admin/src/utils/exportToGlobal.js +8 -0
- package/admin/src/utils/{getEditorConfig.js → getPluginConfig.js} +10 -12
- package/admin/src/utils/getPresetsOptions.js +38 -0
- package/package.json +6 -83
- package/server/controllers/config.js +13 -11
- package/server/controllers/index.js +3 -3
- package/server/register.js +1 -1
- package/server/routes/index.js +7 -12
- package/server/services/config.js +16 -11
- package/server/services/index.js +3 -3
- package/admin/src/components/Input/CKEditor/configs/base.js +0 -639
- package/admin/src/components/Input/CKEditor/configs/blockBalloon.js +0 -25
- package/admin/src/components/Input/CKEditor/configs/index.js +0 -11
- package/admin/src/components/Input/CKEditor/configs/toolbar.js +0 -17
- package/admin/src/components/Input/CKEditor/configs/toolbarBalloon.js +0 -17
- package/admin/src/components/Input/CKEditor/configuration.js +0 -167
- package/admin/src/components/Input/CKEditor/index.js +0 -119
- package/admin/src/components/Input/CKEditor/styling.js +0 -24
- package/admin/src/components/Input/CKEditor/theme/additional.js +0 -187
- package/admin/src/components/Input/index.js +0 -48
- package/admin/src/utils/pluginId.js +0 -5
- /package/admin/src/{components/CKEditorIcon.js → CKEditorIcon.js} +0 -0
- /package/admin/src/{components/Input/CKEditor → Input}/theme/index.js +0 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { CKEditor } from '@ckeditor/ckeditor5-react';
|
|
5
|
+
import { ClassicEditor } from 'ckeditor5';
|
|
6
|
+
import { Box, Loader } from '@strapi/design-system';
|
|
7
|
+
import 'ckeditor5/ckeditor5.css';
|
|
8
|
+
|
|
9
|
+
import { MediaLib } from './MediaLib';
|
|
10
|
+
import { getConfiguredPreset, GlobalStyling } from '../config';
|
|
11
|
+
|
|
12
|
+
const Wrapper = styled('div')`
|
|
13
|
+
${({ styles }) => styles}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export const Editor = ({
|
|
17
|
+
onChange,
|
|
18
|
+
name,
|
|
19
|
+
value = '',
|
|
20
|
+
disabled = false,
|
|
21
|
+
presetName,
|
|
22
|
+
maxLength,
|
|
23
|
+
}) => {
|
|
24
|
+
const [editorInstance, setEditorInstance] = useState(false);
|
|
25
|
+
|
|
26
|
+
const [mediaLibVisible, setMediaLibVisible] = useState(false);
|
|
27
|
+
|
|
28
|
+
const [preset, setPreset] = useState(null);
|
|
29
|
+
|
|
30
|
+
const [lengthMax, setLengthMax] = useState(false);
|
|
31
|
+
|
|
32
|
+
const wordCounter = useRef(null);
|
|
33
|
+
|
|
34
|
+
const handleToggleMediaLib = () => setMediaLibVisible((prev) => !prev);
|
|
35
|
+
|
|
36
|
+
const handleCounter = (number) =>
|
|
37
|
+
number > maxLength ? setLengthMax(true) : setLengthMax(false);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
(async () => {
|
|
41
|
+
const currentPreset = await getConfiguredPreset(
|
|
42
|
+
presetName,
|
|
43
|
+
handleToggleMediaLib
|
|
44
|
+
);
|
|
45
|
+
setPreset(currentPreset);
|
|
46
|
+
})();
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
{preset && <GlobalStyling />}
|
|
52
|
+
<Wrapper styles={preset?.styles}>
|
|
53
|
+
{!preset && (
|
|
54
|
+
<LoaderBox hasRadius background="neutral100">
|
|
55
|
+
<Loader>Loading...</Loader>
|
|
56
|
+
</LoaderBox>
|
|
57
|
+
)}
|
|
58
|
+
{preset && (
|
|
59
|
+
<>
|
|
60
|
+
<CKEditor
|
|
61
|
+
editor={ClassicEditor}
|
|
62
|
+
config={preset.editorConfig}
|
|
63
|
+
disabled={disabled}
|
|
64
|
+
data={value}
|
|
65
|
+
onReady={(editor) => {
|
|
66
|
+
if (preset.editorConfig.WordCountPlugin) {
|
|
67
|
+
const wordCountPlugin = editor.plugins.get('WordCount');
|
|
68
|
+
wordCountPlugin.on('update', (evt, stats) =>
|
|
69
|
+
handleCounter(stats.characters)
|
|
70
|
+
);
|
|
71
|
+
const wordCountWrapper = wordCounter.current;
|
|
72
|
+
wordCountWrapper?.appendChild(
|
|
73
|
+
wordCountPlugin.wordCountContainer
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (editor.plugins.has('ImageUploadEditing')) {
|
|
78
|
+
editor.plugins
|
|
79
|
+
.get('ImageUploadEditing')
|
|
80
|
+
.on('uploadComplete', (evt, { data, imageElement }) =>
|
|
81
|
+
editor.model.change((writer) =>
|
|
82
|
+
writer.setAttribute('alt', data.alt, imageElement)
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setEditorInstance(editor);
|
|
88
|
+
}}
|
|
89
|
+
onChange={(event, editor) => {
|
|
90
|
+
const data = editor.getData();
|
|
91
|
+
onChange({ target: { name, value: data } });
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
<MediaLib
|
|
95
|
+
isOpen={mediaLibVisible}
|
|
96
|
+
onToggle={handleToggleMediaLib}
|
|
97
|
+
editor={editorInstance}
|
|
98
|
+
/>
|
|
99
|
+
{preset.editorConfig.WordCountPlugin && (
|
|
100
|
+
<CounterLoaderBox
|
|
101
|
+
color={lengthMax ? 'danger500' : 'neutral400'}
|
|
102
|
+
ref={wordCounter}
|
|
103
|
+
>
|
|
104
|
+
{!editorInstance && <Loader small>Loading...</Loader>}
|
|
105
|
+
</CounterLoaderBox>
|
|
106
|
+
)}
|
|
107
|
+
</>
|
|
108
|
+
)}
|
|
109
|
+
</Wrapper>
|
|
110
|
+
</>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
Editor.propTypes = {
|
|
115
|
+
onChange: PropTypes.func.isRequired,
|
|
116
|
+
fieldName: PropTypes.string.isRequired,
|
|
117
|
+
value: PropTypes.string,
|
|
118
|
+
disabled: PropTypes.bool,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const CounterLoaderBox = styled(Box)`
|
|
122
|
+
display: flex;
|
|
123
|
+
width: 100%;
|
|
124
|
+
justify-content: flex-end;
|
|
125
|
+
align-items: center;
|
|
126
|
+
`;
|
|
127
|
+
const LoaderBox = styled(Box)`
|
|
128
|
+
display: flex;
|
|
129
|
+
height: 200px;
|
|
130
|
+
width: 100%;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
align-items: center;
|
|
133
|
+
`;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { prefixFileUrlWithBackendUrl, useLibrary } from
|
|
3
|
-
import PropTypes from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { prefixFileUrlWithBackendUrl, useLibrary } from '@strapi/helper-plugin';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
4
|
|
|
5
|
-
const MediaLib = ({ isOpen
|
|
5
|
+
export const MediaLib = ({ isOpen = false, onToggle = () => {}, editor }) => {
|
|
6
6
|
const { components } = useLibrary();
|
|
7
|
-
const MediaLibraryDialog = components[
|
|
7
|
+
const MediaLibraryDialog = components['media-library'];
|
|
8
8
|
|
|
9
9
|
const handleChangeAssets = (assets) => {
|
|
10
|
-
let newValue =
|
|
10
|
+
let newValue = '';
|
|
11
11
|
|
|
12
12
|
assets.map(({ name, url, alt, formats, mime, width, height }) => {
|
|
13
|
-
if (mime.includes(
|
|
14
|
-
if (formats &&
|
|
15
|
-
let set =
|
|
13
|
+
if (mime.includes('image')) {
|
|
14
|
+
if (formats && globalThis.SH_CKE_UPLOAD_ADAPTER_IS_RESPONSIVE) {
|
|
15
|
+
let set = '';
|
|
16
16
|
let keys = Object.keys(formats).sort((a, b) => formats[a].width - formats[b].width);
|
|
17
|
-
keys.map((k) => (set += prefixFileUrlWithBackendUrl(formats[k].url)
|
|
17
|
+
keys.map((k) => (set += prefixFileUrlWithBackendUrl(formats[k].url) +` ${formats[k].width}w,`));
|
|
18
18
|
newValue = `<img src="${prefixFileUrlWithBackendUrl(url)}" alt="${alt}" width="${width}" height="${height}" srcset="${set}" />`;
|
|
19
19
|
} else {
|
|
20
20
|
newValue = `<img src="${prefixFileUrlWithBackendUrl(url)}" alt="${alt}" width="${width}" height="${height}" />`;
|
|
21
21
|
}
|
|
22
|
-
} else if (mime.includes(
|
|
22
|
+
} else if (mime.includes('video')) {
|
|
23
23
|
newValue = `
|
|
24
24
|
<video class="video" controls width="500px">
|
|
25
25
|
<source src="${prefixFileUrlWithBackendUrl(url)}" type="${mime}" />
|
|
26
26
|
</video>`;
|
|
27
27
|
} else {
|
|
28
|
-
newValue = `<a href="${prefixFileUrlWithBackendUrl(url)}">${name ||
|
|
28
|
+
newValue = `<a href="${prefixFileUrlWithBackendUrl(url)}">${name || 'Open document'}</a>`;
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -44,7 +44,7 @@ const MediaLib = ({ isOpen, onChange, onToggle, editor, uploadConfig: { responsi
|
|
|
44
44
|
mime: f.mime,
|
|
45
45
|
formats: f.formats,
|
|
46
46
|
width: f.width,
|
|
47
|
-
height: f.height
|
|
47
|
+
height: f.height,
|
|
48
48
|
}));
|
|
49
49
|
|
|
50
50
|
handleChangeAssets(formattedFiles);
|
|
@@ -54,19 +54,16 @@ const MediaLib = ({ isOpen, onChange, onToggle, editor, uploadConfig: { responsi
|
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
onToggle: () => {},
|
|
57
|
+
return (
|
|
58
|
+
<MediaLibraryDialog
|
|
59
|
+
onClose={onToggle}
|
|
60
|
+
onSelectAssets={handleSelectAssets}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
64
63
|
};
|
|
65
64
|
|
|
66
65
|
MediaLib.propTypes = {
|
|
67
66
|
isOpen: PropTypes.bool,
|
|
68
67
|
onChange: PropTypes.func,
|
|
69
68
|
onToggle: PropTypes.func,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export default MediaLib;
|
|
69
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { auth } from '@strapi/helper-plugin';
|
|
2
|
+
|
|
3
|
+
const importLang = async (config, language) => {
|
|
4
|
+
const translations = await import(
|
|
5
|
+
/* webpackMode: "lazy-once" */ `ckeditor5/translations/${language}.js`
|
|
6
|
+
).catch((e) => console.log(e));
|
|
7
|
+
|
|
8
|
+
config.translations = translations.default;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const detecti18n = () => {
|
|
12
|
+
const urlSearchParams = new URLSearchParams(window.location.search);
|
|
13
|
+
const params = Object.fromEntries(urlSearchParams.entries());
|
|
14
|
+
const i18n = params['plugins[i18n][locale]'];
|
|
15
|
+
return i18n && i18n.split('-')[0];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const setLanguage = async (config) => {
|
|
19
|
+
const i18nLang = detecti18n();
|
|
20
|
+
const preferedLanguage = auth.getUserInfo().preferedLanguage || 'en';
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
ui = preferedLanguage,
|
|
24
|
+
content,
|
|
25
|
+
textPartLanguage,
|
|
26
|
+
ignorei18n,
|
|
27
|
+
} = config.language || {};
|
|
28
|
+
|
|
29
|
+
if (i18nLang) {
|
|
30
|
+
config.language = {
|
|
31
|
+
ui: typeof config.language === 'string' ? config.language : ui,
|
|
32
|
+
content: ignorei18n ? content : i18nLang,
|
|
33
|
+
textPartLanguage: textPartLanguage,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!config.language) {
|
|
38
|
+
config.language = preferedLanguage;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await importLang(
|
|
42
|
+
config,
|
|
43
|
+
typeof config.language === 'string' ? config.language : config.language.ui
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { auth } from '@strapi/helper-plugin';
|
|
2
|
+
|
|
3
|
+
export const setPlugins = (config, toggleMediaLib) => {
|
|
4
|
+
const presetPluginNames = config?.plugins
|
|
5
|
+
? [...config.plugins.map((p) => p.pluginName)]
|
|
6
|
+
: [];
|
|
7
|
+
|
|
8
|
+
if (presetPluginNames.includes('StrapiMediaLib')) {
|
|
9
|
+
config.strapiMediaLib = { toggle: toggleMediaLib };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (presetPluginNames.includes('StrapiUploadAdapter')) {
|
|
13
|
+
config.strapiUploadAdapter = {
|
|
14
|
+
uploadUrl: `${strapi.backendURL}/upload`,
|
|
15
|
+
headers: { Authorization: 'Bearer ' + auth.getToken() },
|
|
16
|
+
backendUrl: strapi.backendURL,
|
|
17
|
+
responsive: globalThis.SH_CKE_UPLOAD_ADAPTER_IS_RESPONSIVE,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (presetPluginNames.includes('WordCount')) {
|
|
22
|
+
config.WordCountPlugin = true;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import basePresets from '../presets';
|
|
2
|
+
import { setPlugins } from './plugins';
|
|
3
|
+
import { setLanguage } from './language';
|
|
4
|
+
|
|
5
|
+
export const getConfiguredPreset = async (presetName, toggleMediaLib) => {
|
|
6
|
+
const { presets: userPresets, dontMergePresets } =
|
|
7
|
+
globalThis.SH_CKE_CONFIG || {};
|
|
8
|
+
|
|
9
|
+
const preset = dontMergePresets
|
|
10
|
+
? userPresets[presetName]
|
|
11
|
+
: basePresets[presetName];
|
|
12
|
+
|
|
13
|
+
setPlugins(preset.editorConfig, toggleMediaLib);
|
|
14
|
+
|
|
15
|
+
await setLanguage(preset.editorConfig);
|
|
16
|
+
|
|
17
|
+
return preset;
|
|
18
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createGlobalStyle } from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import baseTheme from '../theme';
|
|
5
|
+
|
|
6
|
+
const GlobalStyle = createGlobalStyle`
|
|
7
|
+
${({ theme }) => theme.common}
|
|
8
|
+
${({ theme, variant }) => theme[variant]}
|
|
9
|
+
${({ theme }) => theme.additional}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const getSystemColorScheme = () =>
|
|
13
|
+
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
14
|
+
? 'dark'
|
|
15
|
+
: 'light';
|
|
16
|
+
|
|
17
|
+
export const GlobalStyling = () => {
|
|
18
|
+
const { theme: userTheme, dontMergeTheme } = globalThis.SH_CKE_CONFIG || {};
|
|
19
|
+
|
|
20
|
+
const profileTheme = localStorage.getItem('STRAPI_THEME');
|
|
21
|
+
|
|
22
|
+
const variant =
|
|
23
|
+
profileTheme && profileTheme !== 'system'
|
|
24
|
+
? profileTheme
|
|
25
|
+
: getSystemColorScheme();
|
|
26
|
+
|
|
27
|
+
const theme = dontMergeTheme ? userTheme : { ...baseTheme, ...userTheme };
|
|
28
|
+
|
|
29
|
+
return <GlobalStyle theme={theme} variant={variant} />;
|
|
30
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import {
|
|
5
|
+
Field,
|
|
6
|
+
FieldHint,
|
|
7
|
+
FieldError,
|
|
8
|
+
FieldLabel,
|
|
9
|
+
} from '@strapi/design-system';
|
|
10
|
+
import { Stack } from '@strapi/design-system';
|
|
11
|
+
|
|
12
|
+
import { Editor } from './components/Editor';
|
|
13
|
+
|
|
14
|
+
const Wysiwyg = React.forwardRef((props, ref) => {
|
|
15
|
+
const {
|
|
16
|
+
name,
|
|
17
|
+
attribute,
|
|
18
|
+
onChange,
|
|
19
|
+
value,
|
|
20
|
+
intlLabel,
|
|
21
|
+
labelAction,
|
|
22
|
+
disabled,
|
|
23
|
+
error,
|
|
24
|
+
description,
|
|
25
|
+
required,
|
|
26
|
+
} = props;
|
|
27
|
+
const { formatMessage } = useIntl();
|
|
28
|
+
const { preset, maxLengthCharacters, ...options } = attribute.options;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Field
|
|
32
|
+
name={name}
|
|
33
|
+
id={name}
|
|
34
|
+
error={error}
|
|
35
|
+
required={required}
|
|
36
|
+
hint={description && formatMessage(description)}
|
|
37
|
+
>
|
|
38
|
+
<Stack spacing={1}>
|
|
39
|
+
<FieldLabel action={labelAction}>{formatMessage(intlLabel)}</FieldLabel>
|
|
40
|
+
<Editor
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
name={name}
|
|
43
|
+
onChange={onChange}
|
|
44
|
+
value={value}
|
|
45
|
+
presetName={preset}
|
|
46
|
+
maxLength={maxLengthCharacters}
|
|
47
|
+
/>
|
|
48
|
+
<FieldHint />
|
|
49
|
+
<FieldError />
|
|
50
|
+
</Stack>
|
|
51
|
+
</Field>
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
Wysiwyg.propTypes = {
|
|
56
|
+
intlLabel: PropTypes.object.isRequired,
|
|
57
|
+
onChange: PropTypes.func.isRequired,
|
|
58
|
+
attribute: PropTypes.object.isRequired,
|
|
59
|
+
name: PropTypes.string.isRequired,
|
|
60
|
+
description: PropTypes.object,
|
|
61
|
+
disabled: PropTypes.bool,
|
|
62
|
+
error: PropTypes.string,
|
|
63
|
+
labelAction: PropTypes.object,
|
|
64
|
+
required: PropTypes.bool,
|
|
65
|
+
value: PropTypes.string,
|
|
66
|
+
};
|
|
67
|
+
export default Wysiwyg;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const mediaLibIcon =
|
|
1
|
+
import { Plugin, ButtonView } from 'ckeditor5';
|
|
2
|
+
|
|
3
|
+
const mediaLibIcon =
|
|
4
|
+
'<svg width="1em" height="1em" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
|
|
4
5
|
'<path fill-rule="evenodd" clip-rule="evenodd" d="M4.3.6a.9.9 0 100 1.8h15.311a.9.9 0 100-1.8H4.301zm17.1 3.7A1.6 1.6' +
|
|
5
6
|
' 0 0123 5.9v15.5a1.6 1.6 0 01-1.6 1.6H2.6A1.601 1.601 0 011 21.4V8 5.915C1 5.03 1.716 4.3 2.6' +
|
|
6
7
|
' 4.3h18.8zM5.032 19.18h14.336l-3.136-3.205-1.792 1.831-4.032-4.12-5.376 5.494zm13.44-8.697c0 ' +
|
|
7
8
|
'1.282-.985 2.289-2.24 2.289-1.254 0-2.24-1.007-2.24-2.29 0-1.281.986-2.288 2.24-2.288 1.255 0 2.24 1.007 2.24 2.289z">' +
|
|
8
|
-
'</path></svg>'
|
|
9
|
+
'</path></svg>';
|
|
9
10
|
|
|
10
11
|
export default class StrapiMediaLib extends Plugin {
|
|
11
|
-
|
|
12
12
|
/**
|
|
13
13
|
* Strapi function used to show media library modal.
|
|
14
14
|
* Should be provided via connect method before using toggle method.
|
|
@@ -18,20 +18,19 @@ export default class StrapiMediaLib extends Plugin {
|
|
|
18
18
|
strapiToggle = null;
|
|
19
19
|
|
|
20
20
|
static get pluginName() {
|
|
21
|
-
return 'StrapiMediaLib'
|
|
21
|
+
return 'StrapiMediaLib';
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
init() {
|
|
25
25
|
const editor = this.editor;
|
|
26
26
|
const config = editor.config.get('strapiMediaLib');
|
|
27
27
|
editor.ui.componentFactory.add('strapiMediaLib', () => {
|
|
28
|
-
|
|
29
28
|
const button = new ButtonView();
|
|
30
29
|
|
|
31
30
|
button.set({
|
|
32
|
-
label:
|
|
31
|
+
label: 'Media Library',
|
|
33
32
|
icon: mediaLibIcon,
|
|
34
|
-
tooltip: true
|
|
33
|
+
tooltip: true,
|
|
35
34
|
});
|
|
36
35
|
|
|
37
36
|
button.on('execute', config.toggle.bind(this));
|
|
@@ -39,5 +38,4 @@ export default class StrapiMediaLib extends Plugin {
|
|
|
39
38
|
return button;
|
|
40
39
|
});
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
}
|
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
const FileRepository = window.CKEditor5.upload.FileRepository;
|
|
1
|
+
import { Plugin, FileRepository } from 'ckeditor5';
|
|
3
2
|
|
|
4
|
-
/**
|
|
5
|
-
* Similar to Simple upload adapter but customized for Strapi.
|
|
6
|
-
* Inspired by https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-upload/src/adapters/simpleuploadadapter.js
|
|
7
|
-
*/
|
|
8
3
|
export default class StrapiUploadAdapter extends Plugin {
|
|
9
|
-
/**
|
|
10
|
-
* @inheritDoc
|
|
11
|
-
*/
|
|
12
4
|
static get requires() {
|
|
13
5
|
return [FileRepository];
|
|
14
6
|
}
|
|
15
7
|
|
|
16
|
-
/**
|
|
17
|
-
* @inheritDoc
|
|
18
|
-
*/
|
|
19
8
|
static get pluginName() {
|
|
20
|
-
return
|
|
9
|
+
return 'StrapiUploadAdapter';
|
|
21
10
|
}
|
|
22
11
|
|
|
23
|
-
/**
|
|
24
|
-
* @inheritDoc
|
|
25
|
-
*/
|
|
26
12
|
init() {
|
|
27
|
-
|
|
28
|
-
// uploadUrl
|
|
29
|
-
// headers
|
|
30
|
-
// responsive
|
|
31
|
-
|
|
32
|
-
const options = this.editor.config.get("strapiUploadAdapter");
|
|
13
|
+
const options = this.editor.config.get('strapiUploadAdapter');
|
|
33
14
|
|
|
34
15
|
if (!options) {
|
|
35
16
|
return;
|
|
@@ -61,13 +42,11 @@ class Adapter {
|
|
|
61
42
|
constructor(loader, options) {
|
|
62
43
|
/**
|
|
63
44
|
* FileLoader instance to use during the upload.
|
|
64
|
-
*
|
|
65
45
|
*/
|
|
66
46
|
this.loader = loader;
|
|
67
47
|
|
|
68
48
|
/**
|
|
69
49
|
* The configuration of the adapter.
|
|
70
|
-
*
|
|
71
50
|
*/
|
|
72
51
|
this.options = options;
|
|
73
52
|
}
|
|
@@ -109,8 +88,8 @@ class Adapter {
|
|
|
109
88
|
_initRequest() {
|
|
110
89
|
const xhr = (this.xhr = new XMLHttpRequest());
|
|
111
90
|
|
|
112
|
-
xhr.open(
|
|
113
|
-
xhr.responseType =
|
|
91
|
+
xhr.open('POST', this.options.uploadUrl, true);
|
|
92
|
+
xhr.responseType = 'json';
|
|
114
93
|
}
|
|
115
94
|
|
|
116
95
|
/**
|
|
@@ -126,9 +105,9 @@ class Adapter {
|
|
|
126
105
|
const loader = this.loader;
|
|
127
106
|
const genericErrorText = `Couldn't upload file: ${file.name}.`;
|
|
128
107
|
|
|
129
|
-
xhr.addEventListener(
|
|
130
|
-
xhr.addEventListener(
|
|
131
|
-
xhr.addEventListener(
|
|
108
|
+
xhr.addEventListener('error', () => reject(genericErrorText));
|
|
109
|
+
xhr.addEventListener('abort', () => reject());
|
|
110
|
+
xhr.addEventListener('load', () => {
|
|
132
111
|
const response = xhr.response;
|
|
133
112
|
|
|
134
113
|
if (!Array.isArray(response) || response.error || response.length !== 1) {
|
|
@@ -141,14 +120,18 @@ class Adapter {
|
|
|
141
120
|
|
|
142
121
|
const { backendUrl, responsive } = this.options || {};
|
|
143
122
|
const { name, url, alternativeText, formats, provider } = response[0];
|
|
144
|
-
const defaultUrl = provider !==
|
|
123
|
+
const defaultUrl = provider !== 'local' ? url : backendUrl + url;
|
|
145
124
|
|
|
146
125
|
if (formats && responsive) {
|
|
147
126
|
let urls = { default: defaultUrl };
|
|
148
127
|
let keys = Object.keys(formats).sort(
|
|
149
128
|
(a, b) => formats[a].width - formats[b].width
|
|
150
129
|
);
|
|
151
|
-
keys.map(
|
|
130
|
+
keys.map(
|
|
131
|
+
(k) =>
|
|
132
|
+
(urls[formats[k].width] =
|
|
133
|
+
provider !== 'local' ? url : backendUrl + formats[k].url)
|
|
134
|
+
);
|
|
152
135
|
resolve({ alt: alternativeText || name, urls: urls });
|
|
153
136
|
} else {
|
|
154
137
|
resolve(
|
|
@@ -163,9 +146,8 @@ class Adapter {
|
|
|
163
146
|
});
|
|
164
147
|
|
|
165
148
|
// Upload progress when it is supported.
|
|
166
|
-
/* istanbul ignore else */
|
|
167
149
|
if (xhr.upload) {
|
|
168
|
-
xhr.upload.addEventListener(
|
|
150
|
+
xhr.upload.addEventListener('progress', (evt) => {
|
|
169
151
|
if (evt.lengthComputable) {
|
|
170
152
|
loader.uploadTotal = evt.total;
|
|
171
153
|
loader.uploaded = evt.loaded;
|
|
@@ -196,7 +178,7 @@ class Adapter {
|
|
|
196
178
|
// Prepare the form data.
|
|
197
179
|
const data = new FormData();
|
|
198
180
|
|
|
199
|
-
data.append(
|
|
181
|
+
data.append('files', file);
|
|
200
182
|
|
|
201
183
|
// Send the request.
|
|
202
184
|
this.xhr.send(data);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as StrapiMediaLib } from './StrapiMediaLib';
|
|
2
|
-
export { default as StrapiUploadAdapter } from './StrapiUploadAdapter';
|
|
2
|
+
export { default as StrapiUploadAdapter } from './StrapiUploadAdapter';
|