@contentful/field-editor-markdown 1.2.0 → 1.3.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/dist/cjs/MarkdownActions.js +235 -0
- package/dist/cjs/MarkdownEditor.js +180 -0
- package/dist/cjs/__fixtures__/FakeSdk.js +183 -0
- package/dist/cjs/__fixtures__/asset/index.js +37 -0
- package/dist/cjs/__fixtures__/content-type/index.js +16 -0
- package/dist/cjs/__fixtures__/entry/index.js +33 -0
- package/dist/cjs/__fixtures__/fixtures.js +71 -0
- package/dist/cjs/__fixtures__/locale/index.js +40 -0
- package/dist/cjs/__fixtures__/space/index.js +16 -0
- package/dist/cjs/codemirrorImports.js +9 -0
- package/dist/cjs/components/HeadingSelector.js +66 -0
- package/dist/cjs/components/InsertLinkSelector.js +86 -0
- package/dist/cjs/components/MarkdownBottomBar.js +111 -0
- package/dist/cjs/components/MarkdownConstraints.js +79 -0
- package/dist/cjs/components/MarkdownPreview.js +249 -0
- package/dist/cjs/components/MarkdownTabs.js +128 -0
- package/dist/cjs/components/MarkdownTextarea/CodeMirrorWrapper.js +383 -0
- package/dist/cjs/components/MarkdownTextarea/MarkdownCommands.js +233 -0
- package/dist/cjs/components/MarkdownTextarea/MarkdownTextarea.js +190 -0
- package/dist/cjs/components/MarkdownTextarea/createMarkdownEditor.js +101 -0
- package/dist/cjs/components/MarkdownToolbar.js +367 -0
- package/dist/cjs/components/icons.js +193 -0
- package/dist/cjs/dialogs/CheatsheetModalDialog.js +239 -0
- package/dist/cjs/dialogs/ConfirmInsertAssetModalDialog.js +94 -0
- package/dist/cjs/dialogs/EmdebExternalContentDialog.js +202 -0
- package/dist/cjs/dialogs/InsertLinkModalDialog.js +149 -0
- package/dist/cjs/dialogs/InsertTableModalDialog.js +140 -0
- package/dist/cjs/dialogs/SpecialCharacterModalDialog.js +146 -0
- package/dist/cjs/dialogs/ZenModeModalDialog.js +257 -0
- package/dist/cjs/dialogs/openMarkdownDialog.js +121 -0
- package/dist/cjs/dialogs/renderMarkdownDialog.js +108 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/types.js +20 -0
- package/dist/cjs/utils/insertAssetLinks.js +122 -0
- package/dist/cjs/utils/insertAssetLinks.spec.js +22 -0
- package/dist/cjs/utils/isValidUrl.js +22 -0
- package/dist/cjs/utils/linkOrganizer.js +187 -0
- package/dist/cjs/utils/linkOrganizer.spec.js +96 -0
- package/dist/cjs/utils/replaceMailtoAmp.js +15 -0
- package/dist/cjs/utils/replaceMailtoAmp.spec.js +22 -0
- package/dist/cjs/utils/specialCharacters.js +228 -0
- package/dist/cjs/utils/userAgent.js +28 -0
- package/dist/esm/MarkdownActions.js +186 -0
- package/dist/esm/MarkdownEditor.js +118 -0
- package/dist/esm/__fixtures__/FakeSdk.js +173 -0
- package/dist/esm/__fixtures__/asset/index.js +6 -0
- package/dist/esm/__fixtures__/content-type/index.js +2 -0
- package/dist/esm/__fixtures__/entry/index.js +5 -0
- package/dist/esm/__fixtures__/fixtures.js +6 -0
- package/dist/esm/__fixtures__/locale/index.js +15 -0
- package/dist/esm/__fixtures__/space/index.js +2 -0
- package/dist/esm/codemirrorImports.js +5 -0
- package/dist/esm/components/HeadingSelector.js +17 -0
- package/dist/esm/components/InsertLinkSelector.js +37 -0
- package/dist/esm/components/MarkdownBottomBar.js +46 -0
- package/dist/esm/components/MarkdownConstraints.js +25 -0
- package/dist/esm/components/MarkdownPreview.js +195 -0
- package/dist/esm/components/MarkdownTabs.js +74 -0
- package/dist/esm/components/MarkdownTextarea/CodeMirrorWrapper.js +329 -0
- package/dist/esm/components/MarkdownTextarea/MarkdownCommands.js +218 -0
- package/dist/esm/components/MarkdownTextarea/MarkdownTextarea.js +136 -0
- package/dist/esm/components/MarkdownTextarea/createMarkdownEditor.js +52 -0
- package/dist/esm/components/MarkdownToolbar.js +302 -0
- package/dist/esm/components/icons.js +112 -0
- package/dist/esm/dialogs/CheatsheetModalDialog.js +177 -0
- package/dist/esm/dialogs/ConfirmInsertAssetModalDialog.js +37 -0
- package/dist/esm/dialogs/EmdebExternalContentDialog.js +140 -0
- package/dist/esm/dialogs/InsertLinkModalDialog.js +92 -0
- package/dist/esm/dialogs/InsertTableModalDialog.js +78 -0
- package/dist/esm/dialogs/SpecialCharacterModalDialog.js +84 -0
- package/dist/esm/dialogs/ZenModeModalDialog.js +195 -0
- package/dist/esm/dialogs/openMarkdownDialog.js +72 -0
- package/dist/esm/dialogs/renderMarkdownDialog.js +59 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/types.js +10 -0
- package/dist/esm/utils/insertAssetLinks.js +99 -0
- package/dist/esm/utils/insertAssetLinks.spec.js +18 -0
- package/dist/esm/utils/isValidUrl.js +4 -0
- package/dist/esm/utils/linkOrganizer.js +152 -0
- package/dist/esm/utils/linkOrganizer.spec.js +53 -0
- package/dist/esm/utils/replaceMailtoAmp.js +5 -0
- package/dist/esm/utils/replaceMailtoAmp.spec.js +18 -0
- package/dist/esm/utils/specialCharacters.js +218 -0
- package/dist/esm/utils/userAgent.js +13 -0
- package/dist/{MarkdownActions.d.ts → types/MarkdownActions.d.ts} +38 -38
- package/dist/{MarkdownEditor.d.ts → types/MarkdownEditor.d.ts} +22 -22
- package/dist/types/__fixtures__/FakeSdk.d.ts +8 -0
- package/dist/types/__fixtures__/asset/index.d.ts +6 -0
- package/dist/types/__fixtures__/content-type/index.d.ts +2 -0
- package/dist/types/__fixtures__/entry/index.d.ts +5 -0
- package/dist/types/__fixtures__/fixtures.d.ts +6 -0
- package/dist/types/__fixtures__/locale/index.d.ts +42 -0
- package/dist/types/__fixtures__/space/index.d.ts +2 -0
- package/dist/{codemirrorImports.d.ts → types/codemirrorImports.d.ts} +5 -5
- package/dist/{components → types/components}/HeadingSelector.d.ts +7 -7
- package/dist/{components → types/components}/InsertLinkSelector.d.ts +9 -9
- package/dist/{components → types/components}/MarkdownBottomBar.d.ts +11 -11
- package/dist/{components → types/components}/MarkdownConstraints.d.ts +6 -6
- package/dist/{components → types/components}/MarkdownPreview.d.ts +14 -14
- package/dist/{components → types/components}/MarkdownTabs.d.ts +8 -8
- package/dist/{components → types/components}/MarkdownTextarea/CodeMirrorWrapper.d.ts +58 -58
- package/dist/{components → types/components}/MarkdownTextarea/MarkdownCommands.d.ts +33 -33
- package/dist/{components → types/components}/MarkdownTextarea/MarkdownTextarea.d.ts +17 -17
- package/dist/{components → types/components}/MarkdownTextarea/createMarkdownEditor.d.ts +55 -55
- package/dist/{components → types/components}/MarkdownToolbar.d.ts +12 -12
- package/dist/types/components/icons.d.ts +18 -0
- package/dist/{dialogs → types/dialogs}/CheatsheetModalDialog.d.ts +4 -4
- package/dist/{dialogs → types/dialogs}/ConfirmInsertAssetModalDialog.d.ts +23 -23
- package/dist/{dialogs → types/dialogs}/EmdebExternalContentDialog.d.ts +9 -9
- package/dist/{dialogs → types/dialogs}/InsertLinkModalDialog.d.ts +17 -17
- package/dist/{dialogs → types/dialogs}/InsertTableModalDialog.d.ts +13 -13
- package/dist/{dialogs → types/dialogs}/SpecialCharacterModalDialog.d.ts +9 -9
- package/dist/{dialogs → types/dialogs}/ZenModeModalDialog.d.ts +24 -24
- package/dist/{dialogs → types/dialogs}/openMarkdownDialog.d.ts +5 -5
- package/dist/{dialogs → types/dialogs}/renderMarkdownDialog.d.ts +8 -8
- package/dist/{index.d.ts → types/index.d.ts} +5 -5
- package/dist/{types.d.ts → types/types.d.ts} +75 -75
- package/dist/{utils → types/utils}/insertAssetLinks.d.ts +29 -29
- package/dist/types/utils/insertAssetLinks.spec.d.ts +1 -0
- package/dist/{utils → types/utils}/isValidUrl.d.ts +2 -2
- package/dist/{utils → types/utils}/linkOrganizer.d.ts +6 -6
- package/dist/types/utils/linkOrganizer.spec.d.ts +1 -0
- package/dist/{utils → types/utils}/replaceMailtoAmp.d.ts +1 -1
- package/dist/types/utils/replaceMailtoAmp.spec.d.ts +1 -0
- package/dist/{utils → types/utils}/specialCharacters.d.ts +4 -4
- package/dist/{utils → types/utils}/userAgent.d.ts +1 -1
- package/package.json +25 -11
- package/CHANGELOG.md +0 -308
- package/dist/components/icons.d.ts +0 -18
- package/dist/field-editor-markdown.cjs.development.js +0 -3605
- package/dist/field-editor-markdown.cjs.development.js.map +0 -1
- package/dist/field-editor-markdown.cjs.production.min.js +0 -216
- package/dist/field-editor-markdown.cjs.production.min.js.map +0 -1
- package/dist/field-editor-markdown.esm.js +0 -3595
- package/dist/field-editor-markdown.esm.js.map +0 -1
- package/dist/index.js +0 -8
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { openConfirmInsertAsset } from './dialogs/ConfirmInsertAssetModalDialog';
|
|
2
|
+
import { openEmbedExternalContentDialog } from './dialogs/EmdebExternalContentDialog';
|
|
3
|
+
import { openInsertLinkDialog } from './dialogs/InsertLinkModalDialog';
|
|
4
|
+
import { openInsertTableDialog } from './dialogs/InsertTableModalDialog';
|
|
5
|
+
import { openInsertSpecialCharacter } from './dialogs/SpecialCharacterModalDialog';
|
|
6
|
+
import { openZenMode } from './dialogs/ZenModeModalDialog';
|
|
7
|
+
import { insertAssetLinks } from './utils/insertAssetLinks';
|
|
8
|
+
import * as LinkOrganizer from './utils/linkOrganizer';
|
|
9
|
+
export function createMarkdownActions(props) {
|
|
10
|
+
const { sdk , editor , locale } = props;
|
|
11
|
+
const insertAssetsWithConfirmation = async (assets)=>{
|
|
12
|
+
if (assets) {
|
|
13
|
+
const { links , fallbacks } = await insertAssetLinks(assets, {
|
|
14
|
+
localeCode: locale,
|
|
15
|
+
defaultLocaleCode: sdk.locales.default,
|
|
16
|
+
fallbackCode: sdk.locales.fallbacks[locale]
|
|
17
|
+
});
|
|
18
|
+
if (links && links.length > 0) {
|
|
19
|
+
if (fallbacks) {
|
|
20
|
+
const insertAnyway = await openConfirmInsertAsset(sdk.dialogs, {
|
|
21
|
+
locale: locale,
|
|
22
|
+
assets: fallbacks
|
|
23
|
+
});
|
|
24
|
+
if (!insertAnyway) {
|
|
25
|
+
throw Error('User decided to not use fallbacks');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return links.map((link)=>link.asMarkdown).join('\n\n');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return '';
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
headings: {
|
|
35
|
+
h1: ()=>{
|
|
36
|
+
editor?.actions.h1();
|
|
37
|
+
},
|
|
38
|
+
h2: ()=>{
|
|
39
|
+
editor?.actions.h2();
|
|
40
|
+
},
|
|
41
|
+
h3: ()=>{
|
|
42
|
+
editor?.actions.h3();
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
simple: {
|
|
46
|
+
italic: ()=>{
|
|
47
|
+
editor?.actions.italic();
|
|
48
|
+
},
|
|
49
|
+
bold: ()=>{
|
|
50
|
+
editor?.actions.bold();
|
|
51
|
+
},
|
|
52
|
+
quote: ()=>{
|
|
53
|
+
editor?.actions.quote();
|
|
54
|
+
},
|
|
55
|
+
ol: ()=>{
|
|
56
|
+
editor?.actions.ol();
|
|
57
|
+
},
|
|
58
|
+
ul: ()=>{
|
|
59
|
+
editor?.actions.ul();
|
|
60
|
+
},
|
|
61
|
+
strike: ()=>{
|
|
62
|
+
editor?.actions.strike();
|
|
63
|
+
},
|
|
64
|
+
code: ()=>{
|
|
65
|
+
editor?.actions.code();
|
|
66
|
+
},
|
|
67
|
+
hr: ()=>{
|
|
68
|
+
editor?.actions.hr();
|
|
69
|
+
},
|
|
70
|
+
indent: ()=>{
|
|
71
|
+
editor?.actions.indent();
|
|
72
|
+
},
|
|
73
|
+
dedent: ()=>{
|
|
74
|
+
editor?.actions.dedent();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
history: {
|
|
78
|
+
undo: ()=>{
|
|
79
|
+
editor?.actions.undo();
|
|
80
|
+
},
|
|
81
|
+
redo: ()=>{
|
|
82
|
+
editor?.actions.redo();
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
insertLink: async ()=>{
|
|
86
|
+
if (!editor) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
editor.usePrimarySelection();
|
|
90
|
+
const selectedText = editor.getSelectedText();
|
|
91
|
+
const result = await openInsertLinkDialog(sdk.dialogs, {
|
|
92
|
+
selectedText
|
|
93
|
+
});
|
|
94
|
+
if (result) {
|
|
95
|
+
editor.actions.link(result.url, selectedText || result.text, result.title);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
insertSpecialCharacter: async ()=>{
|
|
99
|
+
if (!editor) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const result = await openInsertSpecialCharacter(sdk.dialogs);
|
|
103
|
+
if (result) {
|
|
104
|
+
editor.insert(result);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
insertTable: async ()=>{
|
|
108
|
+
if (!editor) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const result = await openInsertTableDialog(sdk.dialogs);
|
|
112
|
+
if (result) {
|
|
113
|
+
editor.actions.table(result);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
organizeLinks: ()=>{
|
|
117
|
+
if (!editor) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let text = editor.getContent();
|
|
121
|
+
if (!text) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
text = LinkOrganizer.convertInlineToRef(text);
|
|
125
|
+
text = LinkOrganizer.rewriteRefs(text);
|
|
126
|
+
editor.setContent(text);
|
|
127
|
+
sdk.notifier.success('All your links are now references at the bottom of your document.');
|
|
128
|
+
},
|
|
129
|
+
embedExternalContent: async ()=>{
|
|
130
|
+
if (!editor) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const result = await openEmbedExternalContentDialog(sdk.dialogs);
|
|
134
|
+
if (result) {
|
|
135
|
+
editor.insert(result);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
addNewMedia: async ()=>{
|
|
139
|
+
if (!editor) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const { entity: asset } = await sdk.navigator.openNewAsset({
|
|
144
|
+
slideIn: {
|
|
145
|
+
waitForClose: true
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const markdownLinks = await insertAssetsWithConfirmation([
|
|
149
|
+
asset
|
|
150
|
+
]);
|
|
151
|
+
editor.insert(markdownLinks);
|
|
152
|
+
} finally{
|
|
153
|
+
editor.focus();
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
linkExistingMedia: async ()=>{
|
|
157
|
+
if (!editor) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const assets = await sdk.dialogs.selectMultipleAssets({
|
|
162
|
+
locale: locale
|
|
163
|
+
});
|
|
164
|
+
const markdownLinks = await insertAssetsWithConfirmation(assets);
|
|
165
|
+
editor.insert(markdownLinks);
|
|
166
|
+
} finally{
|
|
167
|
+
editor.focus();
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
openZenMode: async ()=>{
|
|
171
|
+
if (!editor) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const result = await openZenMode(sdk.dialogs, {
|
|
175
|
+
initialValue: editor.getContent(),
|
|
176
|
+
locale: props.locale
|
|
177
|
+
});
|
|
178
|
+
editor.setContent(result.value);
|
|
179
|
+
if (result.cursor) {
|
|
180
|
+
editor.setCursor(result.cursor);
|
|
181
|
+
}
|
|
182
|
+
editor.focus();
|
|
183
|
+
},
|
|
184
|
+
closeZenMode: ()=>{}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import tokens from '@contentful/f36-tokens';
|
|
3
|
+
import { FieldConnector } from '@contentful/field-editor-shared';
|
|
4
|
+
import { css } from 'emotion';
|
|
5
|
+
import { MarkdownBottomBar, MarkdownHelp } from './components/MarkdownBottomBar';
|
|
6
|
+
import { MarkdownConstraints } from './components/MarkdownConstraints';
|
|
7
|
+
import { MarkdownPreview } from './components/MarkdownPreview';
|
|
8
|
+
import { MarkdownTabs } from './components/MarkdownTabs';
|
|
9
|
+
import { MarkdownTextarea } from './components/MarkdownTextarea/MarkdownTextarea';
|
|
10
|
+
import { MarkdownToolbar } from './components/MarkdownToolbar';
|
|
11
|
+
import { openCheatsheetModal } from './dialogs/CheatsheetModalDialog';
|
|
12
|
+
import { createMarkdownActions } from './MarkdownActions';
|
|
13
|
+
const styles = {
|
|
14
|
+
container: css({
|
|
15
|
+
display: 'flex',
|
|
16
|
+
flexDirection: 'column',
|
|
17
|
+
fontFamily: tokens.fontStackPrimary
|
|
18
|
+
})
|
|
19
|
+
};
|
|
20
|
+
export function MarkdownEditor(props) {
|
|
21
|
+
const [currentValue, setCurrentValue] = React.useState(props.initialValue ?? '');
|
|
22
|
+
const [selectedTab, setSelectedTab] = React.useState('editor');
|
|
23
|
+
const [editor, setEditor] = React.useState(null);
|
|
24
|
+
const [canUploadAssets, setCanUploadAssets] = React.useState(false);
|
|
25
|
+
React.useEffect(()=>{
|
|
26
|
+
if (editor && props.onReady) {
|
|
27
|
+
props.onReady(editor);
|
|
28
|
+
setTimeout(()=>{
|
|
29
|
+
editor.refresh();
|
|
30
|
+
}, 1);
|
|
31
|
+
}
|
|
32
|
+
}, [
|
|
33
|
+
editor
|
|
34
|
+
]);
|
|
35
|
+
React.useEffect(()=>{
|
|
36
|
+
props.sdk.access.can('create', 'Asset').then((value)=>{
|
|
37
|
+
setCanUploadAssets(value);
|
|
38
|
+
});
|
|
39
|
+
}, []);
|
|
40
|
+
React.useEffect(()=>{
|
|
41
|
+
if (editor) {
|
|
42
|
+
editor.setReadOnly(props.disabled);
|
|
43
|
+
}
|
|
44
|
+
}, [
|
|
45
|
+
editor,
|
|
46
|
+
props.disabled
|
|
47
|
+
]);
|
|
48
|
+
const isActionDisabled = editor === null || props.disabled || selectedTab !== 'editor';
|
|
49
|
+
const direction = props.sdk.locales.direction[props.sdk.field.locale] ?? 'ltr';
|
|
50
|
+
const actions = React.useMemo(()=>{
|
|
51
|
+
return createMarkdownActions({
|
|
52
|
+
sdk: props.sdk,
|
|
53
|
+
editor,
|
|
54
|
+
locale: props.sdk.field.locale
|
|
55
|
+
});
|
|
56
|
+
}, [
|
|
57
|
+
editor
|
|
58
|
+
]);
|
|
59
|
+
const openMarkdownHelp = React.useCallback(()=>{
|
|
60
|
+
openCheatsheetModal(props.sdk.dialogs);
|
|
61
|
+
}, []);
|
|
62
|
+
return React.createElement("div", {
|
|
63
|
+
className: styles.container,
|
|
64
|
+
"data-test-id": "markdown-editor"
|
|
65
|
+
}, React.createElement(MarkdownTabs, {
|
|
66
|
+
active: selectedTab,
|
|
67
|
+
onSelect: (tab)=>{
|
|
68
|
+
setSelectedTab(tab);
|
|
69
|
+
}
|
|
70
|
+
}), React.createElement(MarkdownToolbar, {
|
|
71
|
+
mode: "default",
|
|
72
|
+
disabled: isActionDisabled,
|
|
73
|
+
canUploadAssets: canUploadAssets,
|
|
74
|
+
actions: actions
|
|
75
|
+
}), React.createElement(MarkdownTextarea, {
|
|
76
|
+
minHeight: props.minHeight,
|
|
77
|
+
mode: "default",
|
|
78
|
+
visible: selectedTab === 'editor',
|
|
79
|
+
disabled: isActionDisabled,
|
|
80
|
+
direction: direction,
|
|
81
|
+
onReady: (editor)=>{
|
|
82
|
+
editor.setContent(props.initialValue ?? '');
|
|
83
|
+
editor.setReadOnly(props.disabled);
|
|
84
|
+
setEditor(editor);
|
|
85
|
+
editor.events.onChange((value)=>{
|
|
86
|
+
const trimmedValue = value.replace(/^\s+$/gm, '');
|
|
87
|
+
props.saveValueToSDK(trimmedValue);
|
|
88
|
+
setCurrentValue(value);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}), selectedTab === 'preview' && React.createElement(MarkdownPreview, {
|
|
92
|
+
direction: direction,
|
|
93
|
+
minHeight: props.minHeight,
|
|
94
|
+
mode: "default",
|
|
95
|
+
value: currentValue,
|
|
96
|
+
previewComponents: props.previewComponents
|
|
97
|
+
}), React.createElement(MarkdownBottomBar, null, React.createElement(MarkdownHelp, {
|
|
98
|
+
onClick: openMarkdownHelp
|
|
99
|
+
})), React.createElement(MarkdownConstraints, {
|
|
100
|
+
sdk: props.sdk,
|
|
101
|
+
value: currentValue
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
export function MarkdownEditorConnected(props) {
|
|
105
|
+
return React.createElement(FieldConnector, {
|
|
106
|
+
throttle: 300,
|
|
107
|
+
field: props.sdk.field,
|
|
108
|
+
isInitiallyDisabled: props.isInitiallyDisabled
|
|
109
|
+
}, ({ value , disabled , setValue , externalReset })=>{
|
|
110
|
+
return React.createElement(MarkdownEditor, {
|
|
111
|
+
...props,
|
|
112
|
+
key: `markdown-editor-${externalReset}`,
|
|
113
|
+
initialValue: value,
|
|
114
|
+
disabled: disabled,
|
|
115
|
+
saveValueToSDK: setValue
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createFakeCMAAdapter, createFakeFieldAPI, createFakeLocalesAPI, createFakeSpaceAPI } from '@contentful/field-editor-test-utils';
|
|
2
|
+
import { assets, contentTypes, entries, locales as localesFixtures, spaces } from './fixtures';
|
|
3
|
+
const newLink = (linkType, id)=>({
|
|
4
|
+
sys: {
|
|
5
|
+
id,
|
|
6
|
+
linkType,
|
|
7
|
+
type: 'Link'
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
export function newReferenceEditorFakeSdk(props) {
|
|
11
|
+
const rawInitialValue = window.localStorage.getItem('initialValue');
|
|
12
|
+
const initialValue = rawInitialValue ? JSON.parse(rawInitialValue) : props?.initialValue;
|
|
13
|
+
const rawValidations = window.localStorage.getItem('fieldValidations');
|
|
14
|
+
const validations = rawValidations ? JSON.parse(rawValidations) : props?.validations;
|
|
15
|
+
const customizeMock = (field)=>{
|
|
16
|
+
return validations ? {
|
|
17
|
+
...field,
|
|
18
|
+
validations
|
|
19
|
+
} : field;
|
|
20
|
+
};
|
|
21
|
+
const [field, mitt] = createFakeFieldAPI(customizeMock, initialValue);
|
|
22
|
+
const space = createFakeSpaceAPI();
|
|
23
|
+
const locales = createFakeLocalesAPI();
|
|
24
|
+
const entryLinks = [
|
|
25
|
+
newLink('Entry', entries.published.sys.id),
|
|
26
|
+
newLink('Entry', entries.changed.sys.id),
|
|
27
|
+
newLink('Entry', entries.empty.sys.id)
|
|
28
|
+
];
|
|
29
|
+
const assetLinks = [
|
|
30
|
+
newLink('Asset', assets.published.sys.id),
|
|
31
|
+
newLink('Asset', assets.changed.sys.id),
|
|
32
|
+
newLink('Asset', assets.empty.sys.id)
|
|
33
|
+
];
|
|
34
|
+
let selectorCounter = 0;
|
|
35
|
+
const delay = (ms)=>{
|
|
36
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
37
|
+
};
|
|
38
|
+
const localizeContentTypes = (contentTypes)=>{
|
|
39
|
+
return contentTypes.map((contentType)=>({
|
|
40
|
+
...contentType,
|
|
41
|
+
fields: contentType.fields.map((field)=>({
|
|
42
|
+
...field,
|
|
43
|
+
localized: true
|
|
44
|
+
}))
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
const sdk = {
|
|
48
|
+
field,
|
|
49
|
+
locales,
|
|
50
|
+
cmaAdapter: createFakeCMAAdapter({
|
|
51
|
+
Entry: {
|
|
52
|
+
get: async ({ entryId })=>{
|
|
53
|
+
if (props?.fetchDelay) {
|
|
54
|
+
await delay(props.fetchDelay);
|
|
55
|
+
}
|
|
56
|
+
if (entryId === entries.empty.sys.id) {
|
|
57
|
+
return entries.empty;
|
|
58
|
+
}
|
|
59
|
+
if (entryId === entries.published.sys.id) {
|
|
60
|
+
return entries.published;
|
|
61
|
+
}
|
|
62
|
+
if (entryId === entries.changed.sys.id) {
|
|
63
|
+
return entries.changed;
|
|
64
|
+
}
|
|
65
|
+
return Promise.reject({});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
Asset: {
|
|
69
|
+
get: async ({ assetId })=>{
|
|
70
|
+
if (props?.fetchDelay) {
|
|
71
|
+
await delay(props.fetchDelay);
|
|
72
|
+
}
|
|
73
|
+
if (assetId === assets.empty.sys.id) {
|
|
74
|
+
return assets.empty;
|
|
75
|
+
}
|
|
76
|
+
if (assetId === assets.published.sys.id) {
|
|
77
|
+
return assets.published;
|
|
78
|
+
}
|
|
79
|
+
if (assetId === assets.changed.sys.id) {
|
|
80
|
+
return assets.changed;
|
|
81
|
+
}
|
|
82
|
+
return Promise.reject({});
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
Space: {
|
|
86
|
+
get: async (params)=>{
|
|
87
|
+
if (params.spaceId === spaces.indifferent.sys.id) {
|
|
88
|
+
return spaces.indifferent;
|
|
89
|
+
}
|
|
90
|
+
return Promise.reject({});
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
ContentType: {
|
|
94
|
+
get: async ({ contentTypeId })=>{
|
|
95
|
+
if (contentTypeId === contentTypes.published.sys.id) {
|
|
96
|
+
return contentTypes.published;
|
|
97
|
+
}
|
|
98
|
+
return Promise.reject({});
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
Locale: {
|
|
102
|
+
getMany: async ()=>localesFixtures.list
|
|
103
|
+
}
|
|
104
|
+
}),
|
|
105
|
+
space: {
|
|
106
|
+
...space,
|
|
107
|
+
getCachedContentTypes () {
|
|
108
|
+
return localizeContentTypes(space.getCachedContentTypes());
|
|
109
|
+
},
|
|
110
|
+
getContentTypes () {
|
|
111
|
+
return Promise.resolve(space.getContentTypes().then((response)=>{
|
|
112
|
+
return {
|
|
113
|
+
...response,
|
|
114
|
+
items: localizeContentTypes(response.items)
|
|
115
|
+
};
|
|
116
|
+
}));
|
|
117
|
+
},
|
|
118
|
+
async getEntityScheduledActions () {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
dialogs: {
|
|
123
|
+
selectSingleAsset: async ()=>{
|
|
124
|
+
selectorCounter++;
|
|
125
|
+
return assetLinks[selectorCounter % assetLinks.length];
|
|
126
|
+
},
|
|
127
|
+
selectMultipleAssets: async ()=>{
|
|
128
|
+
selectorCounter++;
|
|
129
|
+
return selectorCounter % 2 ? assetLinks.slice(0, 2) : [
|
|
130
|
+
assetLinks[2]
|
|
131
|
+
];
|
|
132
|
+
},
|
|
133
|
+
selectSingleEntry: async ()=>{
|
|
134
|
+
selectorCounter++;
|
|
135
|
+
return entryLinks[selectorCounter % entryLinks.length];
|
|
136
|
+
},
|
|
137
|
+
selectMultipleEntries: async ()=>{
|
|
138
|
+
selectorCounter++;
|
|
139
|
+
return selectorCounter % 2 ? entryLinks.slice(0, 2) : [
|
|
140
|
+
entryLinks[2]
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
navigator: {
|
|
145
|
+
openNewAsset: async ()=>({
|
|
146
|
+
entity: newLink('Asset', assets.empty.sys.id)
|
|
147
|
+
}),
|
|
148
|
+
openAsset: async ()=>{
|
|
149
|
+
alert('open Asset in slide in');
|
|
150
|
+
return {};
|
|
151
|
+
},
|
|
152
|
+
openNewEntry: async ()=>({
|
|
153
|
+
entity: newLink('Entry', entries.empty.sys.id)
|
|
154
|
+
}),
|
|
155
|
+
openEntry: async ()=>{
|
|
156
|
+
alert('open entry in slide in');
|
|
157
|
+
return {};
|
|
158
|
+
},
|
|
159
|
+
onSlideInNavigation: ()=>()=>({})
|
|
160
|
+
},
|
|
161
|
+
access: {
|
|
162
|
+
can: async ()=>true
|
|
163
|
+
},
|
|
164
|
+
ids: {
|
|
165
|
+
space: 'space-id',
|
|
166
|
+
environment: 'environment-id'
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
return [
|
|
170
|
+
sdk,
|
|
171
|
+
mitt
|
|
172
|
+
];
|
|
173
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import changed from './changed_asset.json';
|
|
2
|
+
import created from './created_asset.json';
|
|
3
|
+
import empty from './empty_asset.json';
|
|
4
|
+
import invalid from './invalid_asset.json';
|
|
5
|
+
import published from './published_asset.json';
|
|
6
|
+
export { changed, empty, published, invalid, created };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import englishDefault from './english_default_locale.json';
|
|
2
|
+
import german from './german_locale.json';
|
|
3
|
+
const list = {
|
|
4
|
+
sys: {
|
|
5
|
+
type: 'Array'
|
|
6
|
+
},
|
|
7
|
+
total: 2,
|
|
8
|
+
skip: 0,
|
|
9
|
+
limit: 100,
|
|
10
|
+
items: [
|
|
11
|
+
englishDefault,
|
|
12
|
+
german
|
|
13
|
+
]
|
|
14
|
+
};
|
|
15
|
+
export { englishDefault, german, list };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Menu } from '@contentful/f36-components';
|
|
3
|
+
export const HeadingSelector = (props)=>{
|
|
4
|
+
const handleMenuClick = (heading)=>{
|
|
5
|
+
props.onSelect(heading);
|
|
6
|
+
};
|
|
7
|
+
return React.createElement(Menu, null, React.createElement(Menu.Trigger, null, props.children), React.createElement(Menu.List, null, React.createElement(Menu.Item, {
|
|
8
|
+
testId: "markdown-action-button-heading-h1",
|
|
9
|
+
onClick: ()=>handleMenuClick('h1')
|
|
10
|
+
}, "Heading 1"), React.createElement(Menu.Item, {
|
|
11
|
+
testId: "markdown-action-button-heading-h2",
|
|
12
|
+
onClick: ()=>handleMenuClick('h2')
|
|
13
|
+
}, "Heading 2"), React.createElement(Menu.Item, {
|
|
14
|
+
testId: "markdown-action-button-heading-h3",
|
|
15
|
+
onClick: ()=>handleMenuClick('h3')
|
|
16
|
+
}, "Heading 3")));
|
|
17
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button, Menu } from '@contentful/f36-components';
|
|
3
|
+
import { AssetIcon, ChevronDownIcon } from '@contentful/f36-icons';
|
|
4
|
+
export const InsertLinkSelector = (props)=>{
|
|
5
|
+
if (props.canAddNew) {
|
|
6
|
+
return React.createElement(MultipleMediaContextMenu, props);
|
|
7
|
+
} else {
|
|
8
|
+
return React.createElement(Button, {
|
|
9
|
+
isDisabled: props.disabled,
|
|
10
|
+
startIcon: React.createElement(AssetIcon, null),
|
|
11
|
+
testId: "markdownEditor.linkExistingAssets",
|
|
12
|
+
size: "small",
|
|
13
|
+
variant: "secondary",
|
|
14
|
+
onClick: ()=>{
|
|
15
|
+
props.onSelectExisting();
|
|
16
|
+
}
|
|
17
|
+
}, "Insert media");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const MultipleMediaContextMenu = (props)=>{
|
|
21
|
+
return React.createElement(Menu, {
|
|
22
|
+
placement: "bottom-end"
|
|
23
|
+
}, React.createElement(Menu.Trigger, null, React.createElement(Button, {
|
|
24
|
+
endIcon: React.createElement(ChevronDownIcon, null),
|
|
25
|
+
isDisabled: props.disabled,
|
|
26
|
+
startIcon: React.createElement(AssetIcon, null),
|
|
27
|
+
testId: "markdownEditor.insertMediaDropdownTrigger",
|
|
28
|
+
size: "small",
|
|
29
|
+
variant: "secondary"
|
|
30
|
+
}, "Insert media")), React.createElement(Menu.List, null, React.createElement(Menu.Item, {
|
|
31
|
+
testId: "markdownEditor.uploadAssetsAndLink",
|
|
32
|
+
onClick: ()=>props.onAddNew()
|
|
33
|
+
}, "Add new media and link"), React.createElement(Menu.Item, {
|
|
34
|
+
testId: "markdownEditor.linkExistingAssets",
|
|
35
|
+
onClick: ()=>props.onSelectExisting()
|
|
36
|
+
}, "Link existing media")));
|
|
37
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Paragraph, TextLink } from '@contentful/f36-components';
|
|
3
|
+
import tokens from '@contentful/f36-tokens';
|
|
4
|
+
import { css } from 'emotion';
|
|
5
|
+
const styles = {
|
|
6
|
+
root: css({
|
|
7
|
+
display: 'flex',
|
|
8
|
+
justifyContent: 'space-between',
|
|
9
|
+
background: tokens.gray100,
|
|
10
|
+
border: `1px solid ${tokens.gray400}`,
|
|
11
|
+
borderBottomLeftRadius: tokens.borderRadiusSmall,
|
|
12
|
+
borderBottomRightRadius: tokens.borderRadiusSmall,
|
|
13
|
+
padding: `${tokens.spacingXs} ${tokens.spacingS}`
|
|
14
|
+
}),
|
|
15
|
+
help: css({
|
|
16
|
+
color: tokens.gray700,
|
|
17
|
+
fontSize: tokens.fontSizeS,
|
|
18
|
+
button: {
|
|
19
|
+
fontSize: tokens.fontSizeS,
|
|
20
|
+
lineHeight: 'inherit'
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
};
|
|
24
|
+
export function MarkdownCounter(props) {
|
|
25
|
+
return React.createElement(Paragraph, {
|
|
26
|
+
marginBottom: "none",
|
|
27
|
+
className: styles.help
|
|
28
|
+
}, props.words, " ", props.words !== 1 ? 'words' : 'word', ", ", props.characters, ' ', props.characters !== 1 ? 'characters' : 'character');
|
|
29
|
+
}
|
|
30
|
+
export function MarkdownHelp(props) {
|
|
31
|
+
return React.createElement(Paragraph, {
|
|
32
|
+
marginBottom: "none",
|
|
33
|
+
className: styles.help
|
|
34
|
+
}, "Format your text like a pro with the", ' ', React.createElement(TextLink, {
|
|
35
|
+
as: "button",
|
|
36
|
+
testId: "open-markdown-cheatsheet-button",
|
|
37
|
+
onClick: ()=>{
|
|
38
|
+
props.onClick();
|
|
39
|
+
}
|
|
40
|
+
}, "markdown cheatsheet"), ".");
|
|
41
|
+
}
|
|
42
|
+
export function MarkdownBottomBar(props) {
|
|
43
|
+
return React.createElement("div", {
|
|
44
|
+
className: styles.root
|
|
45
|
+
}, props.children);
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import tokens from '@contentful/f36-tokens';
|
|
3
|
+
import { CharCounter, CharValidation, ConstraintsUtils } from '@contentful/field-editor-shared';
|
|
4
|
+
import { css } from 'emotion';
|
|
5
|
+
const styles = {
|
|
6
|
+
root: css({
|
|
7
|
+
display: 'flex',
|
|
8
|
+
justifyContent: 'space-between',
|
|
9
|
+
fontSize: tokens.fontSizeM,
|
|
10
|
+
marginTop: tokens.spacingXs,
|
|
11
|
+
color: tokens.gray700
|
|
12
|
+
})
|
|
13
|
+
};
|
|
14
|
+
export function MarkdownConstraints(props) {
|
|
15
|
+
const constraints = ConstraintsUtils.fromFieldValidations(props.sdk.field.validations, props.sdk.field.type);
|
|
16
|
+
const checkConstraint = ConstraintsUtils.makeChecker(constraints);
|
|
17
|
+
return React.createElement("div", {
|
|
18
|
+
className: styles.root
|
|
19
|
+
}, React.createElement(CharCounter, {
|
|
20
|
+
value: props.value,
|
|
21
|
+
checkConstraint: checkConstraint
|
|
22
|
+
}), React.createElement(CharValidation, {
|
|
23
|
+
constraints: constraints
|
|
24
|
+
}));
|
|
25
|
+
}
|