@contentful/field-editor-markdown 1.2.1 → 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.
Files changed (136) hide show
  1. package/dist/cjs/MarkdownActions.js +235 -0
  2. package/dist/cjs/MarkdownEditor.js +180 -0
  3. package/dist/cjs/__fixtures__/FakeSdk.js +183 -0
  4. package/dist/cjs/__fixtures__/asset/index.js +37 -0
  5. package/dist/cjs/__fixtures__/content-type/index.js +16 -0
  6. package/dist/cjs/__fixtures__/entry/index.js +33 -0
  7. package/dist/cjs/__fixtures__/fixtures.js +71 -0
  8. package/dist/cjs/__fixtures__/locale/index.js +40 -0
  9. package/dist/cjs/__fixtures__/space/index.js +16 -0
  10. package/dist/cjs/codemirrorImports.js +9 -0
  11. package/dist/cjs/components/HeadingSelector.js +66 -0
  12. package/dist/cjs/components/InsertLinkSelector.js +86 -0
  13. package/dist/cjs/components/MarkdownBottomBar.js +111 -0
  14. package/dist/cjs/components/MarkdownConstraints.js +79 -0
  15. package/dist/cjs/components/MarkdownPreview.js +249 -0
  16. package/dist/cjs/components/MarkdownTabs.js +128 -0
  17. package/dist/cjs/components/MarkdownTextarea/CodeMirrorWrapper.js +383 -0
  18. package/dist/cjs/components/MarkdownTextarea/MarkdownCommands.js +233 -0
  19. package/dist/cjs/components/MarkdownTextarea/MarkdownTextarea.js +190 -0
  20. package/dist/cjs/components/MarkdownTextarea/createMarkdownEditor.js +101 -0
  21. package/dist/cjs/components/MarkdownToolbar.js +367 -0
  22. package/dist/cjs/components/icons.js +193 -0
  23. package/dist/cjs/dialogs/CheatsheetModalDialog.js +239 -0
  24. package/dist/cjs/dialogs/ConfirmInsertAssetModalDialog.js +94 -0
  25. package/dist/cjs/dialogs/EmdebExternalContentDialog.js +202 -0
  26. package/dist/cjs/dialogs/InsertLinkModalDialog.js +149 -0
  27. package/dist/cjs/dialogs/InsertTableModalDialog.js +140 -0
  28. package/dist/cjs/dialogs/SpecialCharacterModalDialog.js +146 -0
  29. package/dist/cjs/dialogs/ZenModeModalDialog.js +257 -0
  30. package/dist/cjs/dialogs/openMarkdownDialog.js +121 -0
  31. package/dist/cjs/dialogs/renderMarkdownDialog.js +108 -0
  32. package/dist/cjs/index.js +29 -0
  33. package/dist/cjs/types.js +20 -0
  34. package/dist/cjs/utils/insertAssetLinks.js +122 -0
  35. package/dist/cjs/utils/insertAssetLinks.spec.js +22 -0
  36. package/dist/cjs/utils/isValidUrl.js +22 -0
  37. package/dist/cjs/utils/linkOrganizer.js +187 -0
  38. package/dist/cjs/utils/linkOrganizer.spec.js +96 -0
  39. package/dist/cjs/utils/replaceMailtoAmp.js +15 -0
  40. package/dist/cjs/utils/replaceMailtoAmp.spec.js +22 -0
  41. package/dist/cjs/utils/specialCharacters.js +228 -0
  42. package/dist/cjs/utils/userAgent.js +28 -0
  43. package/dist/esm/MarkdownActions.js +186 -0
  44. package/dist/esm/MarkdownEditor.js +118 -0
  45. package/dist/esm/__fixtures__/FakeSdk.js +173 -0
  46. package/dist/esm/__fixtures__/asset/index.js +6 -0
  47. package/dist/esm/__fixtures__/content-type/index.js +2 -0
  48. package/dist/esm/__fixtures__/entry/index.js +5 -0
  49. package/dist/esm/__fixtures__/fixtures.js +6 -0
  50. package/dist/esm/__fixtures__/locale/index.js +15 -0
  51. package/dist/esm/__fixtures__/space/index.js +2 -0
  52. package/dist/esm/codemirrorImports.js +5 -0
  53. package/dist/esm/components/HeadingSelector.js +17 -0
  54. package/dist/esm/components/InsertLinkSelector.js +37 -0
  55. package/dist/esm/components/MarkdownBottomBar.js +46 -0
  56. package/dist/esm/components/MarkdownConstraints.js +25 -0
  57. package/dist/esm/components/MarkdownPreview.js +195 -0
  58. package/dist/esm/components/MarkdownTabs.js +74 -0
  59. package/dist/esm/components/MarkdownTextarea/CodeMirrorWrapper.js +329 -0
  60. package/dist/esm/components/MarkdownTextarea/MarkdownCommands.js +218 -0
  61. package/dist/esm/components/MarkdownTextarea/MarkdownTextarea.js +136 -0
  62. package/dist/esm/components/MarkdownTextarea/createMarkdownEditor.js +52 -0
  63. package/dist/esm/components/MarkdownToolbar.js +302 -0
  64. package/dist/esm/components/icons.js +112 -0
  65. package/dist/esm/dialogs/CheatsheetModalDialog.js +177 -0
  66. package/dist/esm/dialogs/ConfirmInsertAssetModalDialog.js +37 -0
  67. package/dist/esm/dialogs/EmdebExternalContentDialog.js +140 -0
  68. package/dist/esm/dialogs/InsertLinkModalDialog.js +92 -0
  69. package/dist/esm/dialogs/InsertTableModalDialog.js +78 -0
  70. package/dist/esm/dialogs/SpecialCharacterModalDialog.js +84 -0
  71. package/dist/esm/dialogs/ZenModeModalDialog.js +195 -0
  72. package/dist/esm/dialogs/openMarkdownDialog.js +72 -0
  73. package/dist/esm/dialogs/renderMarkdownDialog.js +59 -0
  74. package/dist/esm/index.js +5 -0
  75. package/dist/esm/types.js +10 -0
  76. package/dist/esm/utils/insertAssetLinks.js +99 -0
  77. package/dist/esm/utils/insertAssetLinks.spec.js +18 -0
  78. package/dist/esm/utils/isValidUrl.js +4 -0
  79. package/dist/esm/utils/linkOrganizer.js +152 -0
  80. package/dist/esm/utils/linkOrganizer.spec.js +53 -0
  81. package/dist/esm/utils/replaceMailtoAmp.js +5 -0
  82. package/dist/esm/utils/replaceMailtoAmp.spec.js +18 -0
  83. package/dist/esm/utils/specialCharacters.js +218 -0
  84. package/dist/esm/utils/userAgent.js +13 -0
  85. package/dist/{MarkdownActions.d.ts → types/MarkdownActions.d.ts} +38 -38
  86. package/dist/{MarkdownEditor.d.ts → types/MarkdownEditor.d.ts} +22 -22
  87. package/dist/types/__fixtures__/FakeSdk.d.ts +8 -0
  88. package/dist/types/__fixtures__/asset/index.d.ts +6 -0
  89. package/dist/types/__fixtures__/content-type/index.d.ts +2 -0
  90. package/dist/types/__fixtures__/entry/index.d.ts +5 -0
  91. package/dist/types/__fixtures__/fixtures.d.ts +6 -0
  92. package/dist/types/__fixtures__/locale/index.d.ts +42 -0
  93. package/dist/types/__fixtures__/space/index.d.ts +2 -0
  94. package/dist/{codemirrorImports.d.ts → types/codemirrorImports.d.ts} +5 -5
  95. package/dist/{components → types/components}/HeadingSelector.d.ts +7 -7
  96. package/dist/{components → types/components}/InsertLinkSelector.d.ts +9 -9
  97. package/dist/{components → types/components}/MarkdownBottomBar.d.ts +11 -11
  98. package/dist/{components → types/components}/MarkdownConstraints.d.ts +6 -6
  99. package/dist/{components → types/components}/MarkdownPreview.d.ts +14 -14
  100. package/dist/{components → types/components}/MarkdownTabs.d.ts +8 -8
  101. package/dist/{components → types/components}/MarkdownTextarea/CodeMirrorWrapper.d.ts +58 -58
  102. package/dist/{components → types/components}/MarkdownTextarea/MarkdownCommands.d.ts +33 -33
  103. package/dist/{components → types/components}/MarkdownTextarea/MarkdownTextarea.d.ts +17 -17
  104. package/dist/{components → types/components}/MarkdownTextarea/createMarkdownEditor.d.ts +55 -55
  105. package/dist/{components → types/components}/MarkdownToolbar.d.ts +12 -12
  106. package/dist/types/components/icons.d.ts +18 -0
  107. package/dist/{dialogs → types/dialogs}/CheatsheetModalDialog.d.ts +4 -4
  108. package/dist/{dialogs → types/dialogs}/ConfirmInsertAssetModalDialog.d.ts +23 -23
  109. package/dist/{dialogs → types/dialogs}/EmdebExternalContentDialog.d.ts +9 -9
  110. package/dist/{dialogs → types/dialogs}/InsertLinkModalDialog.d.ts +17 -17
  111. package/dist/{dialogs → types/dialogs}/InsertTableModalDialog.d.ts +13 -13
  112. package/dist/{dialogs → types/dialogs}/SpecialCharacterModalDialog.d.ts +9 -9
  113. package/dist/{dialogs → types/dialogs}/ZenModeModalDialog.d.ts +24 -24
  114. package/dist/{dialogs → types/dialogs}/openMarkdownDialog.d.ts +5 -5
  115. package/dist/{dialogs → types/dialogs}/renderMarkdownDialog.d.ts +8 -8
  116. package/dist/{index.d.ts → types/index.d.ts} +5 -5
  117. package/dist/{types.d.ts → types/types.d.ts} +75 -75
  118. package/dist/{utils → types/utils}/insertAssetLinks.d.ts +29 -29
  119. package/dist/types/utils/insertAssetLinks.spec.d.ts +1 -0
  120. package/dist/{utils → types/utils}/isValidUrl.d.ts +2 -2
  121. package/dist/{utils → types/utils}/linkOrganizer.d.ts +6 -6
  122. package/dist/types/utils/linkOrganizer.spec.d.ts +1 -0
  123. package/dist/{utils → types/utils}/replaceMailtoAmp.d.ts +1 -1
  124. package/dist/types/utils/replaceMailtoAmp.spec.d.ts +1 -0
  125. package/dist/{utils → types/utils}/specialCharacters.d.ts +4 -4
  126. package/dist/{utils → types/utils}/userAgent.d.ts +1 -1
  127. package/package.json +25 -11
  128. package/CHANGELOG.md +0 -314
  129. package/dist/components/icons.d.ts +0 -18
  130. package/dist/field-editor-markdown.cjs.development.js +0 -3609
  131. package/dist/field-editor-markdown.cjs.development.js.map +0 -1
  132. package/dist/field-editor-markdown.cjs.production.min.js +0 -216
  133. package/dist/field-editor-markdown.cjs.production.min.js.map +0 -1
  134. package/dist/field-editor-markdown.esm.js +0 -3599
  135. package/dist/field-editor-markdown.esm.js.map +0 -1
  136. package/dist/index.js +0 -8
@@ -0,0 +1,140 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { ModalContent, ModalControls, Text, TextLink, Button, Checkbox, Radio, Form, FormControl, TextInput } from '@contentful/f36-components';
3
+ import tokens from '@contentful/f36-tokens';
4
+ import { css } from 'emotion';
5
+ import { MarkdownDialogType } from '../types';
6
+ import { isValidUrl } from '../utils/isValidUrl';
7
+ const styles = {
8
+ widthFiledGroup: css({
9
+ display: 'flex',
10
+ flexWrap: 'nowrap',
11
+ alignItems: 'flex-start'
12
+ }),
13
+ radioButtonGroup: css({
14
+ display: 'inline-flex',
15
+ alignItems: 'flex-start',
16
+ paddingTop: tokens.spacingXl
17
+ }),
18
+ radioButton: css({
19
+ marginLeft: tokens.spacingM
20
+ })
21
+ };
22
+ const makeEmbedlyLink = ({ url , width , selectedUnit , attachSocial })=>{
23
+ const s = {
24
+ percent: '%',
25
+ px: 'px'
26
+ };
27
+ return [
28
+ '<a href="' + url + '" class="embedly-card" ',
29
+ 'data-card-width="' + width + s[selectedUnit] + '" ',
30
+ 'data-card-controls="' + (attachSocial ? '1' : '0') + '"',
31
+ '>Embedded content: ' + url + '</a>'
32
+ ].join('');
33
+ };
34
+ const isWidthValid = (width, unit)=>unit === 'percent' ? width <= 100 : true;
35
+ export const EmbedExternalContentModal = ({ onClose })=>{
36
+ const mainInputRef = useRef(null);
37
+ const [url, setUrl] = useState('https://');
38
+ const [selectedUnit, setUnit] = useState('percent');
39
+ const [urlIsValid, setUrlValidity] = useState(true);
40
+ const [width, setWidth] = useState('100');
41
+ const [attachSocial, setAttachSocial] = useState(false);
42
+ useEffect(()=>{
43
+ if (mainInputRef.current?.focus) {
44
+ mainInputRef.current.focus();
45
+ }
46
+ }, [
47
+ mainInputRef
48
+ ]);
49
+ return React.createElement(React.Fragment, null, React.createElement(ModalContent, {
50
+ testId: "embed-external-dialog"
51
+ }, React.createElement(Form, null, React.createElement(FormControl, {
52
+ id: "external-link-url-field",
53
+ isRequired: true,
54
+ isInvalid: !urlIsValid
55
+ }, React.createElement(FormControl.Label, null, "Content URL"), React.createElement(TextInput, {
56
+ name: "external-link-url",
57
+ value: url,
58
+ onChange: (e)=>{
59
+ const value = e.target.value;
60
+ setUrl(value);
61
+ setUrlValidity(isValidUrl(value));
62
+ },
63
+ testId: "external-link-url-field",
64
+ placeholder: "https://example.com",
65
+ ref: mainInputRef
66
+ }), React.createElement(FormControl.HelpText, null, "Include protocol (e.g. https://)"), !urlIsValid && React.createElement(FormControl.ValidationMessage, null, "URL is invalid")), React.createElement(TextLink, {
67
+ href: "http://embed.ly/providers",
68
+ target: "_blank",
69
+ rel: "noopener noreferrer"
70
+ }, "Supported sources"), React.createElement("div", {
71
+ className: styles.widthFiledGroup
72
+ }, React.createElement(FormControl, {
73
+ id: "embedded-content-width",
74
+ isRequired: true,
75
+ isInvalid: !isWidthValid(Number(width), selectedUnit)
76
+ }, React.createElement(FormControl.Label, null, "Width"), React.createElement(TextInput, {
77
+ value: width,
78
+ name: "embedded-content-width",
79
+ testId: "embedded-content-width",
80
+ type: "number",
81
+ width: "small",
82
+ onChange: (e)=>setWidth(e.target.value)
83
+ }), !isWidthValid(Number(width), selectedUnit) && React.createElement(FormControl.ValidationMessage, null, "Should be equal or less then 100")), React.createElement("div", {
84
+ className: styles.radioButtonGroup
85
+ }, React.createElement(Radio, {
86
+ id: "unit-option-percent",
87
+ value: "percent",
88
+ isChecked: selectedUnit === 'percent',
89
+ onChange: ()=>setUnit('percent'),
90
+ className: styles.radioButton
91
+ }, "percent"), React.createElement(Radio, {
92
+ id: "unit-option-pixels",
93
+ value: "pixels",
94
+ isChecked: selectedUnit === 'px',
95
+ onChange: ()=>setUnit('px'),
96
+ className: styles.radioButton
97
+ }, "pixels"))), React.createElement(Checkbox, {
98
+ id: "attach-social-checkbox",
99
+ name: "attach-social-checkbox",
100
+ value: "Yes",
101
+ isChecked: attachSocial,
102
+ onChange: ()=>setAttachSocial(!attachSocial),
103
+ testId: "attach-social-checkbox"
104
+ }, "Attach social sharing links to this element"), React.createElement(Text, {
105
+ as: "p",
106
+ fontColor: "gray500",
107
+ marginTop: "spacingXs"
108
+ }, "To enable this embedded content in your application make sure to add the\xa0", React.createElement(TextLink, {
109
+ href: "http://embed.ly/docs/products/cards",
110
+ target: "_blank",
111
+ rel: "noopener noreferrer"
112
+ }, "Embedly's platform.js"), "\xa0on your development environment"))), React.createElement(ModalControls, null, React.createElement(Button, {
113
+ testId: "emded-external-cancel",
114
+ onClick: ()=>onClose(false),
115
+ variant: "secondary",
116
+ size: "small"
117
+ }, "Cancel"), React.createElement(Button, {
118
+ testId: "embed-external-confirm",
119
+ onClick: ()=>onClose(makeEmbedlyLink({
120
+ url,
121
+ width: Number(width),
122
+ selectedUnit,
123
+ attachSocial
124
+ })),
125
+ variant: "positive",
126
+ size: "small"
127
+ }, "Insert")));
128
+ };
129
+ export const openEmbedExternalContentDialog = (dialogs)=>{
130
+ return dialogs.openCurrent({
131
+ title: 'Embed external content',
132
+ width: 'large',
133
+ minHeight: '435px',
134
+ shouldCloseOnEscapePress: true,
135
+ shouldCloseOnOverlayClick: true,
136
+ parameters: {
137
+ type: MarkdownDialogType.embedExternalContent
138
+ }
139
+ });
140
+ };
@@ -0,0 +1,92 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { ModalContent, ModalControls, Button, Form, FormControl, TextInput } from '@contentful/f36-components';
3
+ import { MarkdownDialogType } from '../types';
4
+ import { isValidUrl } from '../utils/isValidUrl';
5
+ export const InsertLinkModal = ({ selectedText , onClose })=>{
6
+ const mainInputRef = useRef(null);
7
+ const [text, setText] = useState(selectedText || '');
8
+ const [url, setUrl] = useState('');
9
+ const [touched, setTouched] = useState(false);
10
+ const [title, setTitle] = useState('');
11
+ const onInsert = (values)=>onClose(values);
12
+ const urlIsValid = isValidUrl(url);
13
+ useEffect(()=>{
14
+ if (mainInputRef?.current?.focus) {
15
+ mainInputRef.current.focus();
16
+ }
17
+ }, [
18
+ mainInputRef
19
+ ]);
20
+ return React.createElement(React.Fragment, null, React.createElement(ModalContent, {
21
+ testId: "insert-link-modal"
22
+ }, React.createElement(Form, {
23
+ onSubmit: ()=>onInsert({
24
+ url,
25
+ text,
26
+ title
27
+ })
28
+ }, React.createElement(FormControl, {
29
+ id: "link-text-field",
30
+ isDisabled: Boolean(selectedText)
31
+ }, React.createElement(FormControl.Label, null, "Link text"), React.createElement(TextInput, {
32
+ name: "link-text",
33
+ value: text,
34
+ onChange: (e)=>{
35
+ setText(e.target.value);
36
+ },
37
+ testId: "link-text-field"
38
+ })), React.createElement(FormControl, {
39
+ id: "target-url-field",
40
+ isInvalid: touched && !urlIsValid
41
+ }, React.createElement(FormControl.Label, null, "Target URL"), React.createElement(TextInput, {
42
+ name: "target-url",
43
+ value: url,
44
+ onChange: (e)=>{
45
+ setUrl(e.target.value);
46
+ setTouched(true);
47
+ },
48
+ placeholder: "https://",
49
+ maxLength: 2100,
50
+ testId: "target-url-field",
51
+ ref: mainInputRef
52
+ }), React.createElement(FormControl.HelpText, null, "Include protocol (e.g. https://)"), touched && !urlIsValid && React.createElement(FormControl.ValidationMessage, null, "Invalid URL")), React.createElement(FormControl, {
53
+ id: "link-title-field"
54
+ }, React.createElement(FormControl.Label, null, "Link title"), React.createElement(TextInput, {
55
+ name: "link-title",
56
+ value: title,
57
+ onChange: (e)=>{
58
+ setTitle(e.target.value);
59
+ },
60
+ testId: "link-title-field"
61
+ }), React.createElement(FormControl.HelpText, null, "Extra link information, usually shown as a tooltip on mouse hover")))), React.createElement(ModalControls, null, React.createElement(Button, {
62
+ testId: "insert-link-cancel",
63
+ onClick: ()=>onClose(false),
64
+ variant: "secondary",
65
+ size: "small"
66
+ }, "Cancel"), React.createElement(Button, {
67
+ testId: "insert-link-confirm",
68
+ onClick: ()=>{
69
+ onInsert({
70
+ url,
71
+ text,
72
+ title
73
+ });
74
+ },
75
+ isDisabled: !urlIsValid,
76
+ variant: "positive",
77
+ size: "small"
78
+ }, "Insert")));
79
+ };
80
+ export const openInsertLinkDialog = (dialogs, params)=>{
81
+ return dialogs.openCurrent({
82
+ title: 'Insert link',
83
+ width: 'large',
84
+ minHeight: '410px',
85
+ shouldCloseOnEscapePress: true,
86
+ shouldCloseOnOverlayClick: true,
87
+ parameters: {
88
+ type: MarkdownDialogType.insertLink,
89
+ ...params
90
+ }
91
+ });
92
+ };
@@ -0,0 +1,78 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { ModalContent, ModalControls, Button, Form, FormControl, TextInput } from '@contentful/f36-components';
3
+ import inRange from 'lodash/inRange';
4
+ import { MarkdownDialogType } from '../types';
5
+ export const InsertTableModal = ({ onClose })=>{
6
+ const mainInputRef = useRef(null);
7
+ const [rows, setRows] = useState(2);
8
+ const [cols, setColumns] = useState(1);
9
+ const rowsAreValid = inRange(rows, 2, 100);
10
+ const colsAreValid = inRange(cols, 1, 100);
11
+ useEffect(()=>{
12
+ if (mainInputRef.current?.focus) {
13
+ mainInputRef.current.focus();
14
+ }
15
+ }, [
16
+ mainInputRef
17
+ ]);
18
+ return React.createElement(React.Fragment, null, React.createElement(ModalContent, {
19
+ testId: "insert-table-modal"
20
+ }, React.createElement(Form, null, React.createElement(FormControl, {
21
+ id: "insert-table-rows-number-field",
22
+ isRequired: true,
23
+ isInvalid: !rowsAreValid
24
+ }, React.createElement(FormControl.Label, null, "Number of rows"), React.createElement(TextInput, {
25
+ name: "rows",
26
+ value: rows.toString(),
27
+ onChange: (e)=>setRows(Number(e.target.value)),
28
+ testId: "insert-table-rows-number-field",
29
+ min: 2,
30
+ max: 100,
31
+ pattern: "[1-9][0-9]*",
32
+ type: "number",
33
+ width: "small",
34
+ autoComplete: "off",
35
+ ref: mainInputRef
36
+ }), !rowsAreValid && React.createElement(FormControl.ValidationMessage, null, "Should be between 2 and 100")), React.createElement(FormControl, {
37
+ id: "insert-table-columns-number-field",
38
+ isRequired: true,
39
+ isInvalid: !colsAreValid
40
+ }, React.createElement(FormControl.Label, null, "Number of columns"), React.createElement(TextInput, {
41
+ name: "columns",
42
+ value: cols.toString(),
43
+ onChange: (e)=>setColumns(Number(e.target.value)),
44
+ testId: "insert-table-columns-number-field",
45
+ min: 1,
46
+ max: 100,
47
+ pattern: "[1-9][0-9]*",
48
+ type: "number",
49
+ width: "small",
50
+ autoComplete: "off"
51
+ }), !colsAreValid && React.createElement(FormControl.ValidationMessage, null, "Should be between 1 and 100")))), React.createElement(ModalControls, null, React.createElement(Button, {
52
+ testId: "insert-table-cancel",
53
+ onClick: ()=>onClose(false),
54
+ variant: "secondary",
55
+ size: "small"
56
+ }, "Cancel"), React.createElement(Button, {
57
+ testId: "insert-table-confirm",
58
+ onClick: ()=>onClose({
59
+ rows,
60
+ cols
61
+ }),
62
+ variant: "positive",
63
+ size: "small",
64
+ isDisabled: !rowsAreValid || !colsAreValid
65
+ }, "Insert")));
66
+ };
67
+ export const openInsertTableDialog = (dialogs)=>{
68
+ return dialogs.openCurrent({
69
+ title: 'Insert table',
70
+ width: 'medium',
71
+ minHeight: '260px',
72
+ shouldCloseOnEscapePress: true,
73
+ shouldCloseOnOverlayClick: true,
74
+ parameters: {
75
+ type: MarkdownDialogType.insertTable
76
+ }
77
+ });
78
+ };
@@ -0,0 +1,84 @@
1
+ import React, { useState } from 'react';
2
+ import { ModalContent, ModalControls, Text, Flex, Button, Tooltip } from '@contentful/f36-components';
3
+ import tokens from '@contentful/f36-tokens';
4
+ import { css } from 'emotion';
5
+ import { MarkdownDialogType } from '../types';
6
+ import { specialCharacters } from '../utils/specialCharacters';
7
+ const styles = {
8
+ buttonPanel: css({
9
+ display: 'flex',
10
+ flexWrap: 'wrap'
11
+ }),
12
+ charButton: css({
13
+ border: `1px solid ${tokens.gray500}`,
14
+ width: '4.1rem',
15
+ height: '4.1rem',
16
+ fontSize: tokens.fontSizeXl,
17
+ marginTop: tokens.spacing2Xs,
18
+ marginRight: tokens.spacing2Xs
19
+ }),
20
+ selectedCharButton: css({
21
+ backgroundColor: tokens.gray100
22
+ }),
23
+ tooltip: css({
24
+ zIndex: 1000
25
+ }),
26
+ button: css({
27
+ marginTop: tokens.spacingM,
28
+ marginRight: tokens.spacingS
29
+ })
30
+ };
31
+ export const SpecialCharacterModalDialog = ({ onClose })=>{
32
+ const [selectedCharacter, setSelectedCharacter] = useState(specialCharacters[0]);
33
+ return React.createElement(React.Fragment, null, React.createElement(ModalContent, {
34
+ testId: "insert-special-character-modal"
35
+ }, React.createElement(Flex, {
36
+ flexDirection: "column",
37
+ alignItems: "center"
38
+ }, React.createElement(Text, {
39
+ as: "div",
40
+ lineHeight: "lineHeight3Xl",
41
+ fontSize: "fontSize3Xl",
42
+ marginBottom: "spacingS"
43
+ }, String.fromCharCode(selectedCharacter.code)), React.createElement(Text, {
44
+ as: "div",
45
+ marginBottom: "spacingS"
46
+ }, selectedCharacter.desc)), React.createElement("div", {
47
+ className: styles.buttonPanel
48
+ }, specialCharacters.map((char)=>React.createElement("div", {
49
+ key: char.code
50
+ }, React.createElement(Tooltip, {
51
+ className: styles.tooltip,
52
+ content: char.desc
53
+ }, React.createElement(Button, {
54
+ testId: "special-character-button",
55
+ isActive: char.code === selectedCharacter.code,
56
+ className: styles.charButton,
57
+ variant: "transparent",
58
+ onClick: ()=>setSelectedCharacter(char)
59
+ }, String.fromCharCode(char.code))))))), React.createElement(ModalControls, null, React.createElement(Button, {
60
+ testId: "insert-character-cancel",
61
+ className: styles.button,
62
+ onClick: ()=>onClose(false),
63
+ variant: "secondary",
64
+ size: "small"
65
+ }, "Cancel"), React.createElement(Button, {
66
+ className: styles.button,
67
+ testId: "insert-character-confirm",
68
+ onClick: ()=>onClose(String.fromCharCode(selectedCharacter.code)),
69
+ variant: "positive",
70
+ size: "small"
71
+ }, "Insert selected")));
72
+ };
73
+ export const openInsertSpecialCharacter = (dialogs)=>{
74
+ return dialogs.openCurrent({
75
+ title: 'Insert special character',
76
+ width: 'large',
77
+ minHeight: '600px',
78
+ shouldCloseOnEscapePress: true,
79
+ shouldCloseOnOverlayClick: true,
80
+ parameters: {
81
+ type: MarkdownDialogType.insertSpecialCharacter
82
+ }
83
+ });
84
+ };
@@ -0,0 +1,195 @@
1
+ import * as React from 'react';
2
+ import { ChevronLeftIcon, ChevronRightIcon } from '@contentful/f36-icons';
3
+ import tokens from '@contentful/f36-tokens';
4
+ import { css, cx } from 'emotion';
5
+ import { MarkdownBottomBar, MarkdownHelp } from '../components/MarkdownBottomBar';
6
+ import { MarkdownPreview } from '../components/MarkdownPreview';
7
+ import { MarkdownTextarea } from '../components/MarkdownTextarea/MarkdownTextarea';
8
+ import { MarkdownToolbar } from '../components/MarkdownToolbar';
9
+ import { openCheatsheetModal } from '../dialogs/CheatsheetModalDialog';
10
+ import { createMarkdownActions } from '../MarkdownActions';
11
+ import { MarkdownDialogType } from '../types';
12
+ const styles = {
13
+ root: css({
14
+ position: 'fixed',
15
+ left: 0,
16
+ right: 0,
17
+ top: 0,
18
+ bottom: 0
19
+ }),
20
+ topSplit: css({
21
+ position: 'fixed',
22
+ top: 0,
23
+ height: '48px',
24
+ left: 0,
25
+ right: 0
26
+ }),
27
+ bottomSplit: css({
28
+ position: 'fixed',
29
+ bottom: 0,
30
+ left: 0,
31
+ right: 0,
32
+ height: '36px'
33
+ }),
34
+ editorSplit: css({
35
+ width: '50%',
36
+ position: 'fixed',
37
+ top: '48px',
38
+ left: 0,
39
+ bottom: '36px',
40
+ overflowX: 'hidden',
41
+ overflowY: 'scroll'
42
+ }),
43
+ editorSplitFullscreen: css({
44
+ left: 0,
45
+ right: 0,
46
+ width: '100%'
47
+ }),
48
+ previewSplit: css({
49
+ width: '50%',
50
+ position: 'fixed',
51
+ top: '48px',
52
+ right: 0,
53
+ bottom: '36px',
54
+ overflowX: 'hidden',
55
+ overflowY: 'scroll'
56
+ }),
57
+ separator: css({
58
+ position: 'fixed',
59
+ top: '48px',
60
+ bottom: '36px',
61
+ width: '1px',
62
+ background: tokens.gray400,
63
+ left: '50%'
64
+ }),
65
+ button: css({
66
+ position: 'fixed',
67
+ cursor: 'pointer',
68
+ zIndex: 105,
69
+ top: '49%',
70
+ height: '30px',
71
+ backgroundColor: tokens.gray100,
72
+ border: `1px solid ${tokens.gray400}`,
73
+ padding: 0
74
+ }),
75
+ hideButton: css({
76
+ left: '50%'
77
+ }),
78
+ showButton: css({
79
+ right: 0,
80
+ borderRightWidth: 0
81
+ }),
82
+ icon: css({
83
+ verticalAlign: 'middle'
84
+ })
85
+ };
86
+ export const ZenModeModalDialog = (props)=>{
87
+ const [currentValue, setCurrentValue] = React.useState(props.initialValue ?? '');
88
+ const [showPreview, setShowPreview] = React.useState(true);
89
+ const [editor, setEditor] = React.useState(null);
90
+ React.useEffect(()=>{
91
+ props.sdk?.window?.updateHeight('100%');
92
+ }, []);
93
+ React.useEffect(()=>{
94
+ setTimeout(()=>{
95
+ editor?.setFullsize();
96
+ editor?.refresh();
97
+ }, 150);
98
+ }, [
99
+ editor
100
+ ]);
101
+ const actions = React.useMemo(()=>{
102
+ return createMarkdownActions({
103
+ sdk: props.sdk,
104
+ editor,
105
+ locale: props.locale
106
+ });
107
+ }, [
108
+ editor
109
+ ]);
110
+ actions.closeZenMode = ()=>{
111
+ props.onClose({
112
+ value: currentValue,
113
+ cursor: editor?.getCursor()
114
+ });
115
+ };
116
+ const direction = props.sdk.locales.direction[props.locale] ?? 'ltr';
117
+ return React.createElement("div", {
118
+ className: styles.root,
119
+ "data-test-id": "zen-mode-markdown-editor"
120
+ }, React.createElement("div", {
121
+ className: styles.topSplit
122
+ }, React.createElement(MarkdownToolbar, {
123
+ mode: "zen",
124
+ disabled: false,
125
+ canUploadAssets: false,
126
+ actions: actions
127
+ })), React.createElement("div", {
128
+ className: cx(styles.editorSplit, {
129
+ [styles.editorSplitFullscreen]: showPreview === false
130
+ })
131
+ }, React.createElement(MarkdownTextarea, {
132
+ mode: "zen",
133
+ visible: true,
134
+ disabled: false,
135
+ direction: direction,
136
+ onReady: (editor)=>{
137
+ editor.setContent(props.initialValue ?? '');
138
+ editor.setReadOnly(false);
139
+ setEditor(editor);
140
+ editor.focus();
141
+ editor.events.onChange((value)=>{
142
+ setCurrentValue(value);
143
+ props.saveValueToSDK(value);
144
+ });
145
+ }
146
+ })), showPreview && React.createElement("div", {
147
+ className: styles.previewSplit
148
+ }, React.createElement(MarkdownPreview, {
149
+ direction: direction,
150
+ mode: "zen",
151
+ value: currentValue,
152
+ previewComponents: props.previewComponents
153
+ })), showPreview && React.createElement("div", {
154
+ className: styles.separator
155
+ }), showPreview && React.createElement("button", {
156
+ className: cx(styles.button, styles.hideButton),
157
+ "aria-label": "Hide preview",
158
+ onClick: ()=>{
159
+ setShowPreview(false);
160
+ }
161
+ }, React.createElement(ChevronRightIcon, {
162
+ variant: "muted",
163
+ size: "tiny",
164
+ className: styles.icon
165
+ })), !showPreview && React.createElement("button", {
166
+ className: cx(styles.button, styles.showButton),
167
+ "aria-label": "Show preview",
168
+ onClick: ()=>{
169
+ setShowPreview(true);
170
+ }
171
+ }, React.createElement(ChevronLeftIcon, {
172
+ variant: "muted",
173
+ size: "tiny",
174
+ className: styles.icon
175
+ })), React.createElement("div", {
176
+ className: styles.bottomSplit
177
+ }, React.createElement(MarkdownBottomBar, null, React.createElement(MarkdownHelp, {
178
+ onClick: ()=>{
179
+ openCheatsheetModal(props.sdk.dialogs);
180
+ }
181
+ }))));
182
+ };
183
+ export const openZenMode = (dialogs, options)=>{
184
+ return dialogs.openCurrent({
185
+ width: 'zen',
186
+ shouldCloseOnEscapePress: false,
187
+ minHeight: '100vh',
188
+ shouldCloseOnOverlayClick: false,
189
+ parameters: {
190
+ type: MarkdownDialogType.zenMode,
191
+ initialValue: options.initialValue,
192
+ locale: options.locale
193
+ }
194
+ });
195
+ };
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import { ModalDialogLauncher } from '@contentful/field-editor-shared';
3
+ import { MarkdownDialogType } from '../types';
4
+ import { CheatsheetModalDialog } from './CheatsheetModalDialog';
5
+ import { ConfirmInsertAssetModalDialog } from './ConfirmInsertAssetModalDialog';
6
+ import { EmbedExternalContentModal } from './EmdebExternalContentDialog';
7
+ import { InsertLinkModal } from './InsertLinkModalDialog';
8
+ import { InsertTableModal } from './InsertTableModalDialog';
9
+ import { SpecialCharacterModalDialog } from './SpecialCharacterModalDialog';
10
+ import { ZenModeModalDialog } from './ZenModeModalDialog';
11
+ export const openMarkdownDialog = (sdk, previewComponents)=>(options)=>{
12
+ if (options.parameters?.type === MarkdownDialogType.cheatsheet) {
13
+ return ModalDialogLauncher.openDialog(options, ()=>{
14
+ return React.createElement(CheatsheetModalDialog, null);
15
+ });
16
+ } else if (options.parameters?.type === MarkdownDialogType.insertLink) {
17
+ const selectedText = options.parameters.selectedText;
18
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
19
+ return React.createElement(InsertLinkModal, {
20
+ selectedText: selectedText,
21
+ onClose: onClose
22
+ });
23
+ });
24
+ } else if (options.parameters?.type === MarkdownDialogType.insertSpecialCharacter) {
25
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
26
+ return React.createElement(SpecialCharacterModalDialog, {
27
+ onClose: onClose
28
+ });
29
+ });
30
+ } else if (options.parameters?.type === MarkdownDialogType.insertTable) {
31
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
32
+ return React.createElement(InsertTableModal, {
33
+ onClose: onClose
34
+ });
35
+ });
36
+ } else if (options.parameters?.type === MarkdownDialogType.embedExternalContent) {
37
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
38
+ return React.createElement(EmbedExternalContentModal, {
39
+ onClose: onClose
40
+ });
41
+ });
42
+ } else if (options.parameters?.type === MarkdownDialogType.confirmInsertAsset) {
43
+ const locale = options.parameters.locale;
44
+ const assets = options.parameters.assets;
45
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
46
+ return React.createElement(ConfirmInsertAssetModalDialog, {
47
+ onClose: onClose,
48
+ locale: locale,
49
+ assets: assets
50
+ });
51
+ });
52
+ } else if (options.parameters?.type === MarkdownDialogType.zenMode) {
53
+ const initialValue = options.parameters.initialValue;
54
+ const locale = options.parameters.locale;
55
+ return ModalDialogLauncher.openDialog(options, ({ onClose })=>{
56
+ return React.createElement(ZenModeModalDialog, {
57
+ saveValueToSDK: (value)=>{
58
+ if (value) {
59
+ return sdk?.field?.setValue(value);
60
+ }
61
+ return sdk?.field?.removeValue();
62
+ },
63
+ onClose: onClose,
64
+ initialValue: initialValue,
65
+ locale: locale,
66
+ sdk: sdk,
67
+ previewComponents: previewComponents
68
+ });
69
+ });
70
+ }
71
+ return Promise.reject();
72
+ };