@contentful/field-editor-markdown 1.4.0 → 1.5.1
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/MarkdownEditor.js +2 -1
- package/dist/cjs/components/MarkdownBottomBar.js +41 -8
- package/dist/cjs/components/MarkdownPreview.js +4 -8
- package/dist/cjs/components/MarkdownToolbar.js +6 -2
- package/dist/cjs/dialogs/ZenModeModalDialog.js +37 -50
- package/dist/esm/MarkdownEditor.js +2 -1
- package/dist/esm/components/MarkdownBottomBar.js +42 -9
- package/dist/esm/components/MarkdownPreview.js +4 -8
- package/dist/esm/components/MarkdownToolbar.js +6 -2
- package/dist/esm/dialogs/ZenModeModalDialog.js +37 -50
- package/dist/types/components/MarkdownBottomBar.d.ts +4 -0
- package/package.json +5 -6
|
@@ -160,6 +160,7 @@ function MarkdownEditor(props) {
|
|
|
160
160
|
value: currentValue,
|
|
161
161
|
previewComponents: props.previewComponents
|
|
162
162
|
})), _react.createElement(_MarkdownBottomBar.MarkdownBottomBar, null, _react.createElement(_MarkdownBottomBar.MarkdownHelp, {
|
|
163
|
+
mode: selectedTab,
|
|
163
164
|
onClick: openMarkdownHelp
|
|
164
165
|
})), _react.createElement(_MarkdownConstraints.MarkdownConstraints, {
|
|
165
166
|
sdk: props.sdk,
|
|
@@ -168,7 +169,7 @@ function MarkdownEditor(props) {
|
|
|
168
169
|
}
|
|
169
170
|
function MarkdownEditorConnected(props) {
|
|
170
171
|
return _react.createElement(_fieldeditorshared.FieldConnector, {
|
|
171
|
-
|
|
172
|
+
debounce: 300,
|
|
172
173
|
field: props.sdk.field,
|
|
173
174
|
isInitiallyDisabled: props.isInitiallyDisabled
|
|
174
175
|
}, ({ value , disabled , setValue , externalReset })=>{
|
|
@@ -67,6 +67,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
67
67
|
}
|
|
68
68
|
return newObj;
|
|
69
69
|
}
|
|
70
|
+
const SANITIZE_LINK = 'https://en.wikipedia.org/wiki/HTML_sanitization';
|
|
70
71
|
const styles = {
|
|
71
72
|
root: (0, _emotion.css)({
|
|
72
73
|
display: 'flex',
|
|
@@ -80,7 +81,7 @@ const styles = {
|
|
|
80
81
|
help: (0, _emotion.css)({
|
|
81
82
|
color: _f36tokens.default.gray700,
|
|
82
83
|
fontSize: _f36tokens.default.fontSizeS,
|
|
83
|
-
button: {
|
|
84
|
+
'& button, & a': {
|
|
84
85
|
fontSize: _f36tokens.default.fontSizeS,
|
|
85
86
|
lineHeight: 'inherit'
|
|
86
87
|
}
|
|
@@ -92,17 +93,49 @@ function MarkdownCounter(props) {
|
|
|
92
93
|
className: styles.help
|
|
93
94
|
}, props.words, " ", props.words !== 1 ? 'words' : 'word', ", ", props.characters, ' ', props.characters !== 1 ? 'characters' : 'character');
|
|
94
95
|
}
|
|
96
|
+
function SanitizeMessage() {
|
|
97
|
+
return _react.createElement("span", null, "The preview of the content in this field will be sanitized.", ' ', _react.createElement(_f36components.TextLink, {
|
|
98
|
+
as: "a",
|
|
99
|
+
target: "_blank",
|
|
100
|
+
rel: "noopener noreferrer",
|
|
101
|
+
href: SANITIZE_LINK
|
|
102
|
+
}, "Learn more."));
|
|
103
|
+
}
|
|
104
|
+
function CheatSheetMessage({ onClick }) {
|
|
105
|
+
return _react.createElement("span", null, "Format your text like a pro with the", ' ', _react.createElement(_f36components.TextLink, {
|
|
106
|
+
as: "button",
|
|
107
|
+
testId: "open-markdown-cheatsheet-button",
|
|
108
|
+
onClick: onClick
|
|
109
|
+
}, "markdown cheatsheet"), ".");
|
|
110
|
+
}
|
|
95
111
|
function MarkdownHelp(props) {
|
|
112
|
+
let content;
|
|
113
|
+
switch(props.mode){
|
|
114
|
+
case 'preview':
|
|
115
|
+
content = _react.createElement(SanitizeMessage, null);
|
|
116
|
+
break;
|
|
117
|
+
case 'editor':
|
|
118
|
+
content = _react.createElement(CheatSheetMessage, {
|
|
119
|
+
onClick: props.onClick
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
case 'zen':
|
|
123
|
+
content = _react.createElement(_f36components.Stack, {
|
|
124
|
+
flexDirection: "column",
|
|
125
|
+
spacing: "spacing2Xs",
|
|
126
|
+
alignItems: "flex-start"
|
|
127
|
+
}, _react.createElement(CheatSheetMessage, {
|
|
128
|
+
onClick: props.onClick
|
|
129
|
+
}), _react.createElement(SanitizeMessage, null));
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
content = null;
|
|
133
|
+
throw new Error(`Invalid HelpMode provided in MarkdownHelp: ${props.mode}`);
|
|
134
|
+
}
|
|
96
135
|
return _react.createElement(_f36components.Paragraph, {
|
|
97
136
|
marginBottom: "none",
|
|
98
137
|
className: styles.help
|
|
99
|
-
},
|
|
100
|
-
as: "button",
|
|
101
|
-
testId: "open-markdown-cheatsheet-button",
|
|
102
|
-
onClick: ()=>{
|
|
103
|
-
props.onClick();
|
|
104
|
-
}
|
|
105
|
-
}, "markdown cheatsheet"), ".");
|
|
138
|
+
}, content);
|
|
106
139
|
}
|
|
107
140
|
function MarkdownBottomBar(props) {
|
|
108
141
|
return _react.createElement("div", {
|
|
@@ -11,9 +11,9 @@ Object.defineProperty(exports, "default", {
|
|
|
11
11
|
const _react = _interop_require_wildcard(require("react"));
|
|
12
12
|
const _reactmarkdown = _interop_require_default(require("react-markdown"));
|
|
13
13
|
const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
|
|
14
|
-
const _dompurify = _interop_require_default(require("dompurify"));
|
|
15
14
|
const _emotion = require("emotion");
|
|
16
15
|
const _rehyperaw = _interop_require_default(require("rehype-raw"));
|
|
16
|
+
const _rehypesanitize = _interop_require_default(require("rehype-sanitize"));
|
|
17
17
|
const _remarkgfm = _interop_require_default(require("remark-gfm"));
|
|
18
18
|
const _replaceMailtoAmp = require("../utils/replaceMailtoAmp");
|
|
19
19
|
function _interop_require_default(obj) {
|
|
@@ -227,18 +227,14 @@ const MarkdownPreview = _react.memo((props)=>{
|
|
|
227
227
|
const className = (0, _emotion.cx)(props.minHeight !== undefined ? (0, _emotion.css)({
|
|
228
228
|
minHeight: props.minHeight
|
|
229
229
|
}) : undefined, props.mode === 'default' ? styles.framed : styles.zen, props.direction === 'rtl' ? styles.rtl : undefined);
|
|
230
|
-
const cleanHTML = _react.useMemo(()=>{
|
|
231
|
-
return (0, _replaceMailtoAmp.replaceMailtoAmp)(_dompurify.default.sanitize(props.value));
|
|
232
|
-
}, [
|
|
233
|
-
props.value
|
|
234
|
-
]);
|
|
235
230
|
return _react.createElement("div", {
|
|
236
231
|
className: className,
|
|
237
232
|
"data-test-id": "markdown-preview"
|
|
238
233
|
}, _react.createElement(_reactmarkdown.default, {
|
|
239
234
|
className: styles.root,
|
|
240
235
|
rehypePlugins: [
|
|
241
|
-
_rehyperaw.default
|
|
236
|
+
_rehyperaw.default,
|
|
237
|
+
_rehypesanitize.default
|
|
242
238
|
],
|
|
243
239
|
remarkPlugins: [
|
|
244
240
|
_remarkgfm.default
|
|
@@ -252,7 +248,7 @@ const MarkdownPreview = _react.memo((props)=>{
|
|
|
252
248
|
Embedly: props.previewComponents?.embedly
|
|
253
249
|
})
|
|
254
250
|
}
|
|
255
|
-
},
|
|
251
|
+
}, (0, _replaceMailtoAmp.replaceMailtoAmp)(props.value)));
|
|
256
252
|
});
|
|
257
253
|
MarkdownPreview.displayName = 'MarkdownPreview';
|
|
258
254
|
const _default = MarkdownPreview;
|
|
@@ -105,6 +105,7 @@ const ToolbarButton = _react.forwardRef((props, ref)=>{
|
|
|
105
105
|
const { tooltip , onClick , children , className , variant ='transparent' , tooltipPlace ='top' , isDisabled =false , ...otherProps } = props;
|
|
106
106
|
return _react.createElement(_f36components.Tooltip, {
|
|
107
107
|
className: styles.tooltip,
|
|
108
|
+
usePortal: true,
|
|
108
109
|
placement: tooltipPlace,
|
|
109
110
|
content: tooltip
|
|
110
111
|
}, _react.createElement(_f36components.IconButton, {
|
|
@@ -337,8 +338,11 @@ function ZenMarkdownToolbar(props) {
|
|
|
337
338
|
return _react.createElement("div", {
|
|
338
339
|
className: styles.root
|
|
339
340
|
}, _react.createElement(_f36components.Flex, {
|
|
340
|
-
justifyContent: "space-between"
|
|
341
|
-
|
|
341
|
+
justifyContent: "space-between",
|
|
342
|
+
alignItems: "flex-start"
|
|
343
|
+
}, _react.createElement(_f36components.Flex, {
|
|
344
|
+
flexWrap: "wrap"
|
|
345
|
+
}, _react.createElement(MainButtons, props), _react.createElement(AdditionalButtons, props)), _react.createElement(_f36components.Flex, null, _react.createElement(_InsertLinkSelector.InsertLinkSelector, {
|
|
342
346
|
disabled: props.disabled,
|
|
343
347
|
onSelectExisting: props.actions.linkExistingMedia,
|
|
344
348
|
onAddNew: props.actions.addNewMedia,
|
|
@@ -17,6 +17,7 @@ _export(exports, {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
const _react = _interop_require_wildcard(require("react"));
|
|
20
|
+
const _f36components = require("@contentful/f36-components");
|
|
20
21
|
const _f36icons = require("@contentful/f36-icons");
|
|
21
22
|
const _f36tokens = _interop_require_default(require("@contentful/f36-tokens"));
|
|
22
23
|
const _emotion = require("emotion");
|
|
@@ -74,73 +75,59 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
74
75
|
const MarkdownPreview = _react.lazy(()=>Promise.resolve().then(()=>_interop_require_wildcard(require("../components/MarkdownPreview"))));
|
|
75
76
|
const styles = {
|
|
76
77
|
root: (0, _emotion.css)({
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
bottom: 0
|
|
78
|
+
display: 'grid',
|
|
79
|
+
gridTemplateRows: 'min-content 1fr min-content',
|
|
80
|
+
gridTemplateColumns: '1fr 1px 1fr',
|
|
81
|
+
height: '85vh'
|
|
82
82
|
}),
|
|
83
83
|
topSplit: (0, _emotion.css)({
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
height: '48px',
|
|
87
|
-
left: 0,
|
|
88
|
-
right: 0
|
|
84
|
+
gridRow: '1 / 2',
|
|
85
|
+
gridColumn: '1 / 4'
|
|
89
86
|
}),
|
|
90
87
|
bottomSplit: (0, _emotion.css)({
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
left: 0,
|
|
94
|
-
right: 0,
|
|
95
|
-
height: '36px'
|
|
88
|
+
gridRow: '3 / 4',
|
|
89
|
+
gridColumn: '1 / 4'
|
|
96
90
|
}),
|
|
97
91
|
editorSplit: (0, _emotion.css)({
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
top: '48px',
|
|
101
|
-
left: 0,
|
|
102
|
-
bottom: '36px',
|
|
103
|
-
overflowX: 'hidden',
|
|
92
|
+
gridRow: '2 / 3',
|
|
93
|
+
gridColumn: '1 / 2',
|
|
104
94
|
overflowY: 'scroll'
|
|
105
95
|
}),
|
|
106
96
|
editorSplitFullscreen: (0, _emotion.css)({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
gridRow: '2 / 3',
|
|
98
|
+
gridColumn: '1 / 4',
|
|
99
|
+
overflowY: 'scroll'
|
|
110
100
|
}),
|
|
111
101
|
previewSplit: (0, _emotion.css)({
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
top: '48px',
|
|
115
|
-
right: 0,
|
|
116
|
-
bottom: '36px',
|
|
117
|
-
overflowX: 'hidden',
|
|
102
|
+
gridRow: '2 / 3',
|
|
103
|
+
gridColumn: '3 / 4',
|
|
118
104
|
overflowY: 'scroll'
|
|
119
105
|
}),
|
|
120
106
|
separator: (0, _emotion.css)({
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
width: '1px'
|
|
125
|
-
background: _f36tokens.default.gray400,
|
|
126
|
-
left: '50%'
|
|
107
|
+
gridRow: '2 / 3',
|
|
108
|
+
gridColumn: '2 / 3',
|
|
109
|
+
backgroundColor: _f36tokens.default.gray400,
|
|
110
|
+
width: '1px'
|
|
127
111
|
}),
|
|
128
112
|
button: (0, _emotion.css)({
|
|
129
|
-
position: 'fixed',
|
|
130
113
|
cursor: 'pointer',
|
|
131
114
|
zIndex: 105,
|
|
132
|
-
top: '49%',
|
|
133
115
|
height: '30px',
|
|
134
116
|
backgroundColor: _f36tokens.default.gray100,
|
|
135
117
|
border: `1px solid ${_f36tokens.default.gray400}`,
|
|
136
118
|
padding: 0
|
|
137
119
|
}),
|
|
138
120
|
hideButton: (0, _emotion.css)({
|
|
139
|
-
|
|
121
|
+
gridRow: '2 / 3',
|
|
122
|
+
gridColumn: '2 / 3',
|
|
123
|
+
justifySelf: 'end',
|
|
124
|
+
alignSelf: 'center'
|
|
140
125
|
}),
|
|
141
126
|
showButton: (0, _emotion.css)({
|
|
142
|
-
|
|
143
|
-
|
|
127
|
+
gridRow: '2 / 3',
|
|
128
|
+
gridColumn: '3 / 4',
|
|
129
|
+
justifySelf: 'end',
|
|
130
|
+
alignSelf: 'center'
|
|
144
131
|
}),
|
|
145
132
|
icon: (0, _emotion.css)({
|
|
146
133
|
verticalAlign: 'middle'
|
|
@@ -177,17 +164,17 @@ const ZenModeModalDialog = (props)=>{
|
|
|
177
164
|
});
|
|
178
165
|
};
|
|
179
166
|
const direction = props.sdk.locales.direction[props.locale] ?? 'ltr';
|
|
180
|
-
return _react.createElement(
|
|
167
|
+
return _react.createElement(_f36components.Grid, {
|
|
181
168
|
className: styles.root,
|
|
182
169
|
"data-test-id": "zen-mode-markdown-editor"
|
|
183
|
-
}, _react.createElement(
|
|
170
|
+
}, _react.createElement(_f36components.Grid.Item, {
|
|
184
171
|
className: styles.topSplit
|
|
185
172
|
}, _react.createElement(_MarkdownToolbar.MarkdownToolbar, {
|
|
186
173
|
mode: "zen",
|
|
187
174
|
disabled: false,
|
|
188
175
|
canUploadAssets: false,
|
|
189
176
|
actions: actions
|
|
190
|
-
})), _react.createElement(
|
|
177
|
+
})), _react.createElement(_f36components.Grid.Item, {
|
|
191
178
|
className: (0, _emotion.cx)(styles.editorSplit, {
|
|
192
179
|
[styles.editorSplitFullscreen]: showPreview === false
|
|
193
180
|
})
|
|
@@ -206,7 +193,7 @@ const ZenModeModalDialog = (props)=>{
|
|
|
206
193
|
props.saveValueToSDK(value);
|
|
207
194
|
});
|
|
208
195
|
}
|
|
209
|
-
})), showPreview && _react.createElement(
|
|
196
|
+
})), showPreview && _react.createElement(_f36components.Grid.Item, {
|
|
210
197
|
className: styles.previewSplit
|
|
211
198
|
}, _react.createElement(_react.Suspense, {
|
|
212
199
|
fallback: _react.createElement(_MarkdownPreviewSkeleton.default, null)
|
|
@@ -215,7 +202,7 @@ const ZenModeModalDialog = (props)=>{
|
|
|
215
202
|
mode: "zen",
|
|
216
203
|
value: currentValue,
|
|
217
204
|
previewComponents: props.previewComponents
|
|
218
|
-
}))), showPreview && _react.createElement(
|
|
205
|
+
}))), showPreview && _react.createElement(_f36components.Grid.Item, {
|
|
219
206
|
className: styles.separator
|
|
220
207
|
}), showPreview && _react.createElement("button", {
|
|
221
208
|
className: (0, _emotion.cx)(styles.button, styles.hideButton),
|
|
@@ -237,9 +224,10 @@ const ZenModeModalDialog = (props)=>{
|
|
|
237
224
|
variant: "muted",
|
|
238
225
|
size: "tiny",
|
|
239
226
|
className: styles.icon
|
|
240
|
-
})), _react.createElement(
|
|
227
|
+
})), _react.createElement(_f36components.Grid.Item, {
|
|
241
228
|
className: styles.bottomSplit
|
|
242
229
|
}, _react.createElement(_MarkdownBottomBar.MarkdownBottomBar, null, _react.createElement(_MarkdownBottomBar.MarkdownHelp, {
|
|
230
|
+
mode: "zen",
|
|
243
231
|
onClick: ()=>{
|
|
244
232
|
(0, _CheatsheetModalDialog.openCheatsheetModal)(props.sdk.dialogs);
|
|
245
233
|
}
|
|
@@ -247,10 +235,9 @@ const ZenModeModalDialog = (props)=>{
|
|
|
247
235
|
};
|
|
248
236
|
const openZenMode = (dialogs, options)=>{
|
|
249
237
|
return dialogs.openCurrent({
|
|
250
|
-
width: '
|
|
238
|
+
width: 'fullWidth',
|
|
251
239
|
shouldCloseOnEscapePress: false,
|
|
252
|
-
|
|
253
|
-
shouldCloseOnOverlayClick: false,
|
|
240
|
+
shouldCloseOnOverlayClick: true,
|
|
254
241
|
parameters: {
|
|
255
242
|
type: _types.MarkdownDialogType.zenMode,
|
|
256
243
|
initialValue: options.initialValue,
|
|
@@ -98,6 +98,7 @@ export function MarkdownEditor(props) {
|
|
|
98
98
|
value: currentValue,
|
|
99
99
|
previewComponents: props.previewComponents
|
|
100
100
|
})), React.createElement(MarkdownBottomBar, null, React.createElement(MarkdownHelp, {
|
|
101
|
+
mode: selectedTab,
|
|
101
102
|
onClick: openMarkdownHelp
|
|
102
103
|
})), React.createElement(MarkdownConstraints, {
|
|
103
104
|
sdk: props.sdk,
|
|
@@ -106,7 +107,7 @@ export function MarkdownEditor(props) {
|
|
|
106
107
|
}
|
|
107
108
|
export function MarkdownEditorConnected(props) {
|
|
108
109
|
return React.createElement(FieldConnector, {
|
|
109
|
-
|
|
110
|
+
debounce: 300,
|
|
110
111
|
field: props.sdk.field,
|
|
111
112
|
isInitiallyDisabled: props.isInitiallyDisabled
|
|
112
113
|
}, ({ value , disabled , setValue , externalReset })=>{
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Paragraph, TextLink } from '@contentful/f36-components';
|
|
2
|
+
import { Paragraph, Stack, TextLink } from '@contentful/f36-components';
|
|
3
3
|
import tokens from '@contentful/f36-tokens';
|
|
4
4
|
import { css } from 'emotion';
|
|
5
|
+
const SANITIZE_LINK = 'https://en.wikipedia.org/wiki/HTML_sanitization';
|
|
5
6
|
const styles = {
|
|
6
7
|
root: css({
|
|
7
8
|
display: 'flex',
|
|
@@ -15,7 +16,7 @@ const styles = {
|
|
|
15
16
|
help: css({
|
|
16
17
|
color: tokens.gray700,
|
|
17
18
|
fontSize: tokens.fontSizeS,
|
|
18
|
-
button: {
|
|
19
|
+
'& button, & a': {
|
|
19
20
|
fontSize: tokens.fontSizeS,
|
|
20
21
|
lineHeight: 'inherit'
|
|
21
22
|
}
|
|
@@ -27,17 +28,49 @@ export function MarkdownCounter(props) {
|
|
|
27
28
|
className: styles.help
|
|
28
29
|
}, props.words, " ", props.words !== 1 ? 'words' : 'word', ", ", props.characters, ' ', props.characters !== 1 ? 'characters' : 'character');
|
|
29
30
|
}
|
|
31
|
+
function SanitizeMessage() {
|
|
32
|
+
return React.createElement("span", null, "The preview of the content in this field will be sanitized.", ' ', React.createElement(TextLink, {
|
|
33
|
+
as: "a",
|
|
34
|
+
target: "_blank",
|
|
35
|
+
rel: "noopener noreferrer",
|
|
36
|
+
href: SANITIZE_LINK
|
|
37
|
+
}, "Learn more."));
|
|
38
|
+
}
|
|
39
|
+
function CheatSheetMessage({ onClick }) {
|
|
40
|
+
return React.createElement("span", null, "Format your text like a pro with the", ' ', React.createElement(TextLink, {
|
|
41
|
+
as: "button",
|
|
42
|
+
testId: "open-markdown-cheatsheet-button",
|
|
43
|
+
onClick: onClick
|
|
44
|
+
}, "markdown cheatsheet"), ".");
|
|
45
|
+
}
|
|
30
46
|
export function MarkdownHelp(props) {
|
|
47
|
+
let content;
|
|
48
|
+
switch(props.mode){
|
|
49
|
+
case 'preview':
|
|
50
|
+
content = React.createElement(SanitizeMessage, null);
|
|
51
|
+
break;
|
|
52
|
+
case 'editor':
|
|
53
|
+
content = React.createElement(CheatSheetMessage, {
|
|
54
|
+
onClick: props.onClick
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
case 'zen':
|
|
58
|
+
content = React.createElement(Stack, {
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
spacing: "spacing2Xs",
|
|
61
|
+
alignItems: "flex-start"
|
|
62
|
+
}, React.createElement(CheatSheetMessage, {
|
|
63
|
+
onClick: props.onClick
|
|
64
|
+
}), React.createElement(SanitizeMessage, null));
|
|
65
|
+
break;
|
|
66
|
+
default:
|
|
67
|
+
content = null;
|
|
68
|
+
throw new Error(`Invalid HelpMode provided in MarkdownHelp: ${props.mode}`);
|
|
69
|
+
}
|
|
31
70
|
return React.createElement(Paragraph, {
|
|
32
71
|
marginBottom: "none",
|
|
33
72
|
className: styles.help
|
|
34
|
-
},
|
|
35
|
-
as: "button",
|
|
36
|
-
testId: "open-markdown-cheatsheet-button",
|
|
37
|
-
onClick: ()=>{
|
|
38
|
-
props.onClick();
|
|
39
|
-
}
|
|
40
|
-
}, "markdown cheatsheet"), ".");
|
|
73
|
+
}, content);
|
|
41
74
|
}
|
|
42
75
|
export function MarkdownBottomBar(props) {
|
|
43
76
|
return React.createElement("div", {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import ReactMarkdown from 'react-markdown';
|
|
3
3
|
import tokens from '@contentful/f36-tokens';
|
|
4
|
-
import DOMPurify from 'dompurify';
|
|
5
4
|
import { css, cx } from 'emotion';
|
|
6
5
|
import rehypeRaw from 'rehype-raw';
|
|
6
|
+
import rehypeSanitize from 'rehype-sanitize';
|
|
7
7
|
import remarkGfm from 'remark-gfm';
|
|
8
8
|
import { replaceMailtoAmp } from '../utils/replaceMailtoAmp';
|
|
9
9
|
const styles = {
|
|
@@ -173,18 +173,14 @@ const MarkdownPreview = React.memo((props)=>{
|
|
|
173
173
|
const className = cx(props.minHeight !== undefined ? css({
|
|
174
174
|
minHeight: props.minHeight
|
|
175
175
|
}) : undefined, props.mode === 'default' ? styles.framed : styles.zen, props.direction === 'rtl' ? styles.rtl : undefined);
|
|
176
|
-
const cleanHTML = React.useMemo(()=>{
|
|
177
|
-
return replaceMailtoAmp(DOMPurify.sanitize(props.value));
|
|
178
|
-
}, [
|
|
179
|
-
props.value
|
|
180
|
-
]);
|
|
181
176
|
return React.createElement("div", {
|
|
182
177
|
className: className,
|
|
183
178
|
"data-test-id": "markdown-preview"
|
|
184
179
|
}, React.createElement(ReactMarkdown, {
|
|
185
180
|
className: styles.root,
|
|
186
181
|
rehypePlugins: [
|
|
187
|
-
rehypeRaw
|
|
182
|
+
rehypeRaw,
|
|
183
|
+
rehypeSanitize
|
|
188
184
|
],
|
|
189
185
|
remarkPlugins: [
|
|
190
186
|
remarkGfm
|
|
@@ -198,7 +194,7 @@ const MarkdownPreview = React.memo((props)=>{
|
|
|
198
194
|
Embedly: props.previewComponents?.embedly
|
|
199
195
|
})
|
|
200
196
|
}
|
|
201
|
-
},
|
|
197
|
+
}, replaceMailtoAmp(props.value)));
|
|
202
198
|
});
|
|
203
199
|
MarkdownPreview.displayName = 'MarkdownPreview';
|
|
204
200
|
export default MarkdownPreview;
|
|
@@ -40,6 +40,7 @@ const ToolbarButton = React.forwardRef((props, ref)=>{
|
|
|
40
40
|
const { tooltip , onClick , children , className , variant ='transparent' , tooltipPlace ='top' , isDisabled =false , ...otherProps } = props;
|
|
41
41
|
return React.createElement(Tooltip, {
|
|
42
42
|
className: styles.tooltip,
|
|
43
|
+
usePortal: true,
|
|
43
44
|
placement: tooltipPlace,
|
|
44
45
|
content: tooltip
|
|
45
46
|
}, React.createElement(IconButton, {
|
|
@@ -272,8 +273,11 @@ export function ZenMarkdownToolbar(props) {
|
|
|
272
273
|
return React.createElement("div", {
|
|
273
274
|
className: styles.root
|
|
274
275
|
}, React.createElement(Flex, {
|
|
275
|
-
justifyContent: "space-between"
|
|
276
|
-
|
|
276
|
+
justifyContent: "space-between",
|
|
277
|
+
alignItems: "flex-start"
|
|
278
|
+
}, React.createElement(Flex, {
|
|
279
|
+
flexWrap: "wrap"
|
|
280
|
+
}, React.createElement(MainButtons, props), React.createElement(AdditionalButtons, props)), React.createElement(Flex, null, React.createElement(InsertLinkSelector, {
|
|
277
281
|
disabled: props.disabled,
|
|
278
282
|
onSelectExisting: props.actions.linkExistingMedia,
|
|
279
283
|
onAddNew: props.actions.addNewMedia,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { Grid } from '@contentful/f36-components';
|
|
2
3
|
import { ChevronLeftIcon, ChevronRightIcon } from '@contentful/f36-icons';
|
|
3
4
|
import tokens from '@contentful/f36-tokens';
|
|
4
5
|
import { css, cx } from 'emotion';
|
|
@@ -12,73 +13,59 @@ import { MarkdownDialogType } from '../types';
|
|
|
12
13
|
const MarkdownPreview = React.lazy(()=>import('../components/MarkdownPreview'));
|
|
13
14
|
const styles = {
|
|
14
15
|
root: css({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bottom: 0
|
|
16
|
+
display: 'grid',
|
|
17
|
+
gridTemplateRows: 'min-content 1fr min-content',
|
|
18
|
+
gridTemplateColumns: '1fr 1px 1fr',
|
|
19
|
+
height: '85vh'
|
|
20
20
|
}),
|
|
21
21
|
topSplit: css({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
height: '48px',
|
|
25
|
-
left: 0,
|
|
26
|
-
right: 0
|
|
22
|
+
gridRow: '1 / 2',
|
|
23
|
+
gridColumn: '1 / 4'
|
|
27
24
|
}),
|
|
28
25
|
bottomSplit: css({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
left: 0,
|
|
32
|
-
right: 0,
|
|
33
|
-
height: '36px'
|
|
26
|
+
gridRow: '3 / 4',
|
|
27
|
+
gridColumn: '1 / 4'
|
|
34
28
|
}),
|
|
35
29
|
editorSplit: css({
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
top: '48px',
|
|
39
|
-
left: 0,
|
|
40
|
-
bottom: '36px',
|
|
41
|
-
overflowX: 'hidden',
|
|
30
|
+
gridRow: '2 / 3',
|
|
31
|
+
gridColumn: '1 / 2',
|
|
42
32
|
overflowY: 'scroll'
|
|
43
33
|
}),
|
|
44
34
|
editorSplitFullscreen: css({
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
gridRow: '2 / 3',
|
|
36
|
+
gridColumn: '1 / 4',
|
|
37
|
+
overflowY: 'scroll'
|
|
48
38
|
}),
|
|
49
39
|
previewSplit: css({
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
top: '48px',
|
|
53
|
-
right: 0,
|
|
54
|
-
bottom: '36px',
|
|
55
|
-
overflowX: 'hidden',
|
|
40
|
+
gridRow: '2 / 3',
|
|
41
|
+
gridColumn: '3 / 4',
|
|
56
42
|
overflowY: 'scroll'
|
|
57
43
|
}),
|
|
58
44
|
separator: css({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
width: '1px'
|
|
63
|
-
background: tokens.gray400,
|
|
64
|
-
left: '50%'
|
|
45
|
+
gridRow: '2 / 3',
|
|
46
|
+
gridColumn: '2 / 3',
|
|
47
|
+
backgroundColor: tokens.gray400,
|
|
48
|
+
width: '1px'
|
|
65
49
|
}),
|
|
66
50
|
button: css({
|
|
67
|
-
position: 'fixed',
|
|
68
51
|
cursor: 'pointer',
|
|
69
52
|
zIndex: 105,
|
|
70
|
-
top: '49%',
|
|
71
53
|
height: '30px',
|
|
72
54
|
backgroundColor: tokens.gray100,
|
|
73
55
|
border: `1px solid ${tokens.gray400}`,
|
|
74
56
|
padding: 0
|
|
75
57
|
}),
|
|
76
58
|
hideButton: css({
|
|
77
|
-
|
|
59
|
+
gridRow: '2 / 3',
|
|
60
|
+
gridColumn: '2 / 3',
|
|
61
|
+
justifySelf: 'end',
|
|
62
|
+
alignSelf: 'center'
|
|
78
63
|
}),
|
|
79
64
|
showButton: css({
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
gridRow: '2 / 3',
|
|
66
|
+
gridColumn: '3 / 4',
|
|
67
|
+
justifySelf: 'end',
|
|
68
|
+
alignSelf: 'center'
|
|
82
69
|
}),
|
|
83
70
|
icon: css({
|
|
84
71
|
verticalAlign: 'middle'
|
|
@@ -115,17 +102,17 @@ export const ZenModeModalDialog = (props)=>{
|
|
|
115
102
|
});
|
|
116
103
|
};
|
|
117
104
|
const direction = props.sdk.locales.direction[props.locale] ?? 'ltr';
|
|
118
|
-
return React.createElement(
|
|
105
|
+
return React.createElement(Grid, {
|
|
119
106
|
className: styles.root,
|
|
120
107
|
"data-test-id": "zen-mode-markdown-editor"
|
|
121
|
-
}, React.createElement(
|
|
108
|
+
}, React.createElement(Grid.Item, {
|
|
122
109
|
className: styles.topSplit
|
|
123
110
|
}, React.createElement(MarkdownToolbar, {
|
|
124
111
|
mode: "zen",
|
|
125
112
|
disabled: false,
|
|
126
113
|
canUploadAssets: false,
|
|
127
114
|
actions: actions
|
|
128
|
-
})), React.createElement(
|
|
115
|
+
})), React.createElement(Grid.Item, {
|
|
129
116
|
className: cx(styles.editorSplit, {
|
|
130
117
|
[styles.editorSplitFullscreen]: showPreview === false
|
|
131
118
|
})
|
|
@@ -144,7 +131,7 @@ export const ZenModeModalDialog = (props)=>{
|
|
|
144
131
|
props.saveValueToSDK(value);
|
|
145
132
|
});
|
|
146
133
|
}
|
|
147
|
-
})), showPreview && React.createElement(
|
|
134
|
+
})), showPreview && React.createElement(Grid.Item, {
|
|
148
135
|
className: styles.previewSplit
|
|
149
136
|
}, React.createElement(React.Suspense, {
|
|
150
137
|
fallback: React.createElement(MarkdownPreviewSkeleton, null)
|
|
@@ -153,7 +140,7 @@ export const ZenModeModalDialog = (props)=>{
|
|
|
153
140
|
mode: "zen",
|
|
154
141
|
value: currentValue,
|
|
155
142
|
previewComponents: props.previewComponents
|
|
156
|
-
}))), showPreview && React.createElement(
|
|
143
|
+
}))), showPreview && React.createElement(Grid.Item, {
|
|
157
144
|
className: styles.separator
|
|
158
145
|
}), showPreview && React.createElement("button", {
|
|
159
146
|
className: cx(styles.button, styles.hideButton),
|
|
@@ -175,9 +162,10 @@ export const ZenModeModalDialog = (props)=>{
|
|
|
175
162
|
variant: "muted",
|
|
176
163
|
size: "tiny",
|
|
177
164
|
className: styles.icon
|
|
178
|
-
})), React.createElement(
|
|
165
|
+
})), React.createElement(Grid.Item, {
|
|
179
166
|
className: styles.bottomSplit
|
|
180
167
|
}, React.createElement(MarkdownBottomBar, null, React.createElement(MarkdownHelp, {
|
|
168
|
+
mode: "zen",
|
|
181
169
|
onClick: ()=>{
|
|
182
170
|
openCheatsheetModal(props.sdk.dialogs);
|
|
183
171
|
}
|
|
@@ -185,10 +173,9 @@ export const ZenModeModalDialog = (props)=>{
|
|
|
185
173
|
};
|
|
186
174
|
export const openZenMode = (dialogs, options)=>{
|
|
187
175
|
return dialogs.openCurrent({
|
|
188
|
-
width: '
|
|
176
|
+
width: 'fullWidth',
|
|
189
177
|
shouldCloseOnEscapePress: false,
|
|
190
|
-
|
|
191
|
-
shouldCloseOnOverlayClick: false,
|
|
178
|
+
shouldCloseOnOverlayClick: true,
|
|
192
179
|
parameters: {
|
|
193
180
|
type: MarkdownDialogType.zenMode,
|
|
194
181
|
initialValue: options.initialValue,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { MarkdownTab } from 'types';
|
|
2
3
|
export declare function MarkdownCounter(props: {
|
|
3
4
|
words: number;
|
|
4
5
|
characters: number;
|
|
5
6
|
}): React.JSX.Element;
|
|
7
|
+
type HelpMode = MarkdownTab | 'zen';
|
|
6
8
|
export declare function MarkdownHelp(props: {
|
|
7
9
|
onClick: () => void;
|
|
10
|
+
mode: HelpMode;
|
|
8
11
|
}): React.JSX.Element;
|
|
9
12
|
export declare function MarkdownBottomBar(props: {
|
|
10
13
|
children: React.ReactNode;
|
|
11
14
|
}): React.JSX.Element;
|
|
15
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-markdown",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -38,26 +38,25 @@
|
|
|
38
38
|
"@contentful/f36-components": "^4.0.27",
|
|
39
39
|
"@contentful/f36-icons": "^4.1.0",
|
|
40
40
|
"@contentful/f36-tokens": "^4.0.0",
|
|
41
|
-
"@contentful/field-editor-shared": "^1.
|
|
41
|
+
"@contentful/field-editor-shared": "^1.4.1",
|
|
42
42
|
"@types/codemirror": "0.0.109",
|
|
43
43
|
"codemirror": "^5.65.11",
|
|
44
44
|
"constate": "^3.2.0",
|
|
45
|
-
"dompurify": "^2.2.9",
|
|
46
45
|
"emotion": "^10.0.17",
|
|
47
46
|
"lodash": "^4.17.15",
|
|
48
47
|
"react-markdown": "^8.0.7",
|
|
49
48
|
"rehype-raw": "^6.1.1",
|
|
49
|
+
"rehype-sanitize": "^6.0.0",
|
|
50
50
|
"remark-gfm": "^3.0.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@babel/core": "^7.5.5",
|
|
54
54
|
"@contentful/app-sdk": "^4.2.0",
|
|
55
|
-
"@contentful/field-editor-test-utils": "^1.4.
|
|
56
|
-
"@types/dompurify": "^2.2.2"
|
|
55
|
+
"@contentful/field-editor-test-utils": "^1.4.2"
|
|
57
56
|
},
|
|
58
57
|
"peerDependencies": {
|
|
59
58
|
"@contentful/app-sdk": "^4.2.0",
|
|
60
59
|
"react": ">=16.8.0"
|
|
61
60
|
},
|
|
62
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "0fc8da4aa8ad8603c3adf4bd77b26ba46de11e33"
|
|
63
62
|
}
|