@exmg/exm-markdown-editor 0.0.2-alpha.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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/actions.d.ts +6 -0
- package/dist/actions.js +227 -0
- package/dist/exm-markdown-editor-base.d.ts +81 -0
- package/dist/exm-markdown-editor-base.js +252 -0
- package/dist/exm-markdown-editor-toolbar-base.d.ts +12 -0
- package/dist/exm-markdown-editor-toolbar-base.js +52 -0
- package/dist/exm-markdown-editor-toolbar.d.ts +9 -0
- package/dist/exm-markdown-editor-toolbar.js +12 -0
- package/dist/exm-markdown-editor.d.ts +76 -0
- package/dist/exm-markdown-editor.js +83 -0
- package/dist/icons.d.ts +6 -0
- package/dist/icons.js +28 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/styles/exm-markdown-codemirror-css.d.ts +1 -0
- package/dist/styles/exm-markdown-codemirror-css.js +491 -0
- package/dist/styles/exm-markdown-editor-css.d.ts +1 -0
- package/dist/styles/exm-markdown-editor-css.js +143 -0
- package/dist/styles/exm-markdown-editor-toolbar-css.d.ts +1 -0
- package/dist/styles/exm-markdown-editor-toolbar-css.js +20 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.js +2 -0
- package/dist/utils/configurations.d.ts +4 -0
- package/dist/utils/configurations.js +14 -0
- package/dist/validator/exm-markdown-editor-validator.d.ts +23 -0
- package/dist/validator/exm-markdown-editor-validator.js +35 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Ex Machina
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
# Markdown editor element.
|
|
3
|
+
### An out of the box customizable Markdown Editor for Exmachina
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
<exm-markdown-editor markdown="# Header 1"></exm-markdown-editor>
|
|
7
|
+
```
|
|
8
|
+
## Styling
|
|
9
|
+
|
|
10
|
+
Custom property | Description | Default
|
|
11
|
+
----------------|-------------|----------
|
|
12
|
+
`--exm-markdown-editor-code-color` | Editor's text color | --md-sys-color-on-surface
|
|
13
|
+
`--exm-markdown-editor-code-cursor-color` | Editor's cursor color | --md-sys-color-on-surface
|
|
14
|
+
`--exm-markdown-editor-code-header-color` | H1 color in editor | #4a8fc0;
|
|
15
|
+
`--exm-markdown-editor-code-inline-code-color` | Inline code color | #ea881f
|
|
16
|
+
`--exm-markdown-editor-code-list-color` | Lists color | rgb(25, 165, 28)
|
|
17
|
+
`--exm-markdown-editor-selected-code-color` | Selected text color | rgb(140, 140, 140)
|
|
18
|
+
`--exm-markdown-editor-border-color` | Editor's border color | --md-sys-color-primary
|
|
19
|
+
`--exm-markdown-editor-background-color` | Toolbar and default preview background | --md-sys-color-surface-container-high
|
|
20
|
+
`--exm-markdown-editor-code-background-color` | Editor's background color when focused | --md-sys-color-surface-container-high
|
|
21
|
+
|
|
22
|
+
## Events:
|
|
23
|
+
- change - where detail is current markdown value
|
|
24
|
+
- insert-image - if the Editor is set to upload, it will trigger this event when the insert-image is clicked
|
|
25
|
+
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
export const toolbarActions = [
|
|
2
|
+
{
|
|
3
|
+
name: 'undo',
|
|
4
|
+
icon: 'undo',
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
name: 'redo',
|
|
8
|
+
icon: 'redo',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'header_one',
|
|
12
|
+
icon: 'headerone',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'header_two',
|
|
16
|
+
icon: 'headertwo',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'header_three',
|
|
20
|
+
icon: 'headerthree',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'unordered_list',
|
|
24
|
+
icon: 'format_list_bulleted',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'ordered_list',
|
|
28
|
+
icon: 'format_list_numbered',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'bold',
|
|
32
|
+
icon: 'format_bold',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'italic',
|
|
36
|
+
icon: 'format_italic',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'strikethrough',
|
|
40
|
+
icon: 'format_strikethrough',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'quote',
|
|
44
|
+
icon: 'format_quote',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'code',
|
|
48
|
+
icon: 'data_object',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'link',
|
|
52
|
+
icon: 'insert_link',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'image',
|
|
56
|
+
icon: 'insert_photo',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'hr',
|
|
60
|
+
icon: 'hr',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'table',
|
|
64
|
+
icon: 'table_chart',
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
export const markdownActions = {
|
|
68
|
+
undo: (editor) => {
|
|
69
|
+
editor.getDoc().undo();
|
|
70
|
+
editor.focus();
|
|
71
|
+
},
|
|
72
|
+
redo: (editor) => {
|
|
73
|
+
editor.getDoc().redo();
|
|
74
|
+
editor.focus();
|
|
75
|
+
},
|
|
76
|
+
header_one: (editor) => header(editor, '#'),
|
|
77
|
+
header_two: (editor) => header(editor, '##'),
|
|
78
|
+
header_three: (editor) => header(editor, '###'),
|
|
79
|
+
bold: (editor) => emphasis(editor, ['**', '__']),
|
|
80
|
+
italic: (editor) => emphasis(editor, ['*', '_']),
|
|
81
|
+
strikethrough: (editor) => emphasis(editor, ['~~']),
|
|
82
|
+
quote: (editor) => emphasis(editor, ['`']),
|
|
83
|
+
link: (editor) => insertLink(editor),
|
|
84
|
+
image: (editor, url) => insertImage(editor, url),
|
|
85
|
+
code: (editor) => insertCode(editor),
|
|
86
|
+
hr: (editor) => insertHr(editor),
|
|
87
|
+
table: (editor) => insertTable(editor),
|
|
88
|
+
ordered_list: (editor) => insertList(editor, '#'),
|
|
89
|
+
unordered_list: (editor) => insertList(editor, '*'),
|
|
90
|
+
};
|
|
91
|
+
const header = (editor, symbol) => {
|
|
92
|
+
const selection = editor.getDoc().getSelection().trim();
|
|
93
|
+
let text = selection;
|
|
94
|
+
const regex = new RegExp(`(?<sa>[#]{1,3})(?<text>.*)`, 'g');
|
|
95
|
+
text = processText(text, regex, symbol);
|
|
96
|
+
if (text === selection) {
|
|
97
|
+
text = `${symbol} ${text.length > 0 ? text : 'header'}`;
|
|
98
|
+
editor.getDoc().replaceSelection(text);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
editor.getDoc().replaceSelection(text);
|
|
102
|
+
text.startsWith(symbol) &&
|
|
103
|
+
editor.getDoc().setCursor({
|
|
104
|
+
line: editor.getDoc().getCursor().line + 1,
|
|
105
|
+
ch: editor.getDoc().getCursor().ch,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const emphasis = (editor, symbols) => {
|
|
110
|
+
const selection = editor.getDoc().getSelection();
|
|
111
|
+
let text = selection;
|
|
112
|
+
symbols.forEach((symbol) => {
|
|
113
|
+
const regex = new RegExp(`(?<sa>[\\${symbol.slice(0, 1)}]{${symbol.length}})(?<text>.*?)(?<sb>[\\${symbol.slice(0, 1)}]{1,${symbol.length}})`, 'g');
|
|
114
|
+
text = processText(text, regex, symbol);
|
|
115
|
+
});
|
|
116
|
+
if (text === selection) {
|
|
117
|
+
text = `${symbols[0]}${text.length > 0 ? text : symbols[0] === '`' ? 'quote' : 'emphasis'}${symbols[0]}`;
|
|
118
|
+
}
|
|
119
|
+
editor.getDoc().replaceSelection(text);
|
|
120
|
+
};
|
|
121
|
+
const insertLink = (editor) => {
|
|
122
|
+
const selection = editor.getSelection();
|
|
123
|
+
let text = selection;
|
|
124
|
+
if (text) {
|
|
125
|
+
text = `[${text}]()`;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
text = `[Markdown Live Preview](Your link here)`;
|
|
129
|
+
}
|
|
130
|
+
editor.getDoc().replaceSelection(text);
|
|
131
|
+
const cursor = editor.getDoc().getCursor();
|
|
132
|
+
if (text.split('')[text.length - 2] === 'e') {
|
|
133
|
+
editor.setSelection({
|
|
134
|
+
line: cursor.line,
|
|
135
|
+
ch: cursor.ch - 15,
|
|
136
|
+
}, { ...cursor, ch: cursor.ch - 1 });
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
editor.setCursor({ ...cursor, ch: cursor.ch - 1 });
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const insertImage = (editor, url) => {
|
|
143
|
+
const selection = editor.getSelection();
|
|
144
|
+
let text = selection;
|
|
145
|
+
if (text) {
|
|
146
|
+
text = ``;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
text = ``;
|
|
150
|
+
}
|
|
151
|
+
editor.getDoc().replaceSelection(text);
|
|
152
|
+
const cursor = editor.getDoc().getCursor();
|
|
153
|
+
if (text.startsWith('![A')) {
|
|
154
|
+
editor.setSelection({
|
|
155
|
+
line: cursor.line,
|
|
156
|
+
ch: cursor.ch - 4 - text.length,
|
|
157
|
+
}, {
|
|
158
|
+
line: cursor.line,
|
|
159
|
+
ch: cursor.ch - 18 - text.length,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
editor.setSelection({
|
|
164
|
+
line: cursor.line,
|
|
165
|
+
ch: cursor.ch - 25,
|
|
166
|
+
}, {
|
|
167
|
+
line: cursor.line,
|
|
168
|
+
ch: cursor.ch - 2,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const insertCode = (editor) => {
|
|
173
|
+
const selection = editor.getSelection();
|
|
174
|
+
let text = selection;
|
|
175
|
+
if (text.trim().startsWith('```')) {
|
|
176
|
+
const textArray = text.split('```');
|
|
177
|
+
textArray.pop();
|
|
178
|
+
textArray.reverse().pop();
|
|
179
|
+
text = textArray.reverse().join('');
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
text = `\`\`\`${text}\n\`\`\``;
|
|
183
|
+
}
|
|
184
|
+
editor.replaceSelection(text);
|
|
185
|
+
};
|
|
186
|
+
const insertHr = (editor) => {
|
|
187
|
+
const selection = editor.getSelection();
|
|
188
|
+
editor.replaceSelection(`${selection}\n___\n`);
|
|
189
|
+
};
|
|
190
|
+
const insertTable = (editor) => {
|
|
191
|
+
const selection = editor.getSelection();
|
|
192
|
+
editor.replaceSelection(`${selection}
|
|
193
|
+
| Left columns | Center columns | Right columns |
|
|
194
|
+
| ------------- |:-------------: |--------------:|
|
|
195
|
+
| left foo | right foo | right foo |
|
|
196
|
+
| left bar | right bar | right foo |
|
|
197
|
+
| left baz | right baz | right foo |
|
|
198
|
+
\n`);
|
|
199
|
+
};
|
|
200
|
+
const insertList = (editor, symbol) => {
|
|
201
|
+
const selection = editor.getSelection();
|
|
202
|
+
let text = '';
|
|
203
|
+
for (let i = 0; i < 3; i++) {
|
|
204
|
+
const bullet = `${symbol === '*' ? symbol : `${i + 1}.`}`;
|
|
205
|
+
const item = i === 0 && selection.length > 0 ? selection : `Item ${i + 1}`;
|
|
206
|
+
text += `${bullet} ${item}\n`;
|
|
207
|
+
}
|
|
208
|
+
editor.replaceSelection(`${text}\n`);
|
|
209
|
+
editor.setSelection({
|
|
210
|
+
ch: symbol === '*' ? 2 : 3,
|
|
211
|
+
line: editor.getCursor().line - 4,
|
|
212
|
+
}, {
|
|
213
|
+
ch: selection.length > 0 ? (selection.length + symbol === '*' ? 2 : 3) : 8,
|
|
214
|
+
line: editor.getCursor().line - 4,
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
const processText = (text, regex, symbol) => {
|
|
218
|
+
const matchList = regex.exec(text);
|
|
219
|
+
if (matchList) {
|
|
220
|
+
text = text
|
|
221
|
+
.split(regex)
|
|
222
|
+
.filter((v) => v !== symbol)
|
|
223
|
+
.join('');
|
|
224
|
+
}
|
|
225
|
+
return text.trim();
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { PropertyValueMap } from 'lit';
|
|
2
|
+
import { Editor, EditorConfiguration } from 'codemirror';
|
|
3
|
+
import { ExmgElement } from '@exmg/lit-base';
|
|
4
|
+
import { MarkdownActions, ToolbarIcons, ToolbarItem } from './types.js';
|
|
5
|
+
import { TokenizerAndRendererExtension } from 'marked';
|
|
6
|
+
import { getFormValue } from '@material/web/labs/behaviors/form-associated.js';
|
|
7
|
+
import { MardownEditorValidator } from './validator/exm-markdown-editor-validator.js';
|
|
8
|
+
import { createValidator, getValidityAnchor } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
9
|
+
import './exm-markdown-editor-toolbar.js';
|
|
10
|
+
declare const MarkdownBaseClass: import("@material/web/labs/behaviors/mixin.js").MixinReturn<import("@material/web/labs/behaviors/mixin.js").MixinReturn<(abstract new (...args: any[]) => import("@material/web/labs/behaviors/element-internals.js").WithElementInternals) & typeof ExmgElement & import("@material/web/labs/behaviors/form-associated.js").FormAssociatedConstructor, import("@material/web/labs/behaviors/form-associated.js").FormAssociated>, import("@material/web/labs/behaviors/constraint-validation.js").ConstraintValidation>;
|
|
11
|
+
/**
|
|
12
|
+
* Markdown editor element.
|
|
13
|
+
* An out of the box customizable Markdown Editor for Exmachina
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* <exm-markdown-editor markdown="# Header 1"></exm-markdown-editor>
|
|
17
|
+
* ```
|
|
18
|
+
* ### Styling
|
|
19
|
+
*
|
|
20
|
+
* Custom property | Description | Default
|
|
21
|
+
* ----------------|-------------|----------
|
|
22
|
+
* `--exm-markdown-editor-code-color` | Editor's text color | --md-sys-color-on-surface
|
|
23
|
+
* `--exm-markdown-editor-code-cursor-color` | Editor's cursor color | --md-sys-color-on-surface
|
|
24
|
+
* `--exm-markdown-editor-code-header-color` | H1 color in editor | #4a8fc0;
|
|
25
|
+
* `--exm-markdown-editor-code-inline-code-color` | Inline code color | #ea881f
|
|
26
|
+
* `--exm-markdown-editor-code-list-color` | Lists color | rgb(25, 165, 28)
|
|
27
|
+
* `--exm-markdown-editor-selected-code-color` | Selected text color | rgb(140, 140, 140)
|
|
28
|
+
* `--exm-markdown-editor-border-color` | Editor's border color | --md-sys-color-primary
|
|
29
|
+
* `--exm-markdown-editor-background-color` | Toolbar and default preview background | --md-sys-color-surface-container-high
|
|
30
|
+
* `--exm-markdown-editor-code-background-color` | Editor's background color when focused | --md-sys-color-surface-container-high
|
|
31
|
+
*
|
|
32
|
+
* # Events:
|
|
33
|
+
* - change - where detail is current markdown value
|
|
34
|
+
* - insert-image - if the Editor is set to upload, it will trigger this event when the insert-image is clicked
|
|
35
|
+
*
|
|
36
|
+
* @customElement
|
|
37
|
+
* @element exm-markdown-editor
|
|
38
|
+
* @memberof Exmg
|
|
39
|
+
* @extends ExmgElement
|
|
40
|
+
* @summary Markdown editor element
|
|
41
|
+
*/
|
|
42
|
+
export declare class MarkdownEditorElementBase extends MarkdownBaseClass {
|
|
43
|
+
value: string;
|
|
44
|
+
html?: string;
|
|
45
|
+
label: string;
|
|
46
|
+
upload: boolean;
|
|
47
|
+
toolbarActions?: ToolbarItem[];
|
|
48
|
+
toolbarIcons?: ToolbarIcons;
|
|
49
|
+
markedExtension?: TokenizerAndRendererExtension[];
|
|
50
|
+
required: boolean;
|
|
51
|
+
editorElement?: HTMLDivElement;
|
|
52
|
+
previewElement?: Element | null;
|
|
53
|
+
height: number;
|
|
54
|
+
preview: boolean;
|
|
55
|
+
editorConfiguration: EditorConfiguration;
|
|
56
|
+
editorActions: MarkdownActions;
|
|
57
|
+
codeMirrorEditor?: Editor;
|
|
58
|
+
internalTextarea?: HTMLTextAreaElement;
|
|
59
|
+
blurHandlerBinding: any;
|
|
60
|
+
heightStyleMap?: any;
|
|
61
|
+
reset(): void;
|
|
62
|
+
protected updated(changedProps: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
63
|
+
[getFormValue](): string;
|
|
64
|
+
[createValidator](): MardownEditorValidator;
|
|
65
|
+
[getValidityAnchor](): HTMLTextAreaElement;
|
|
66
|
+
formResetCallback(): void;
|
|
67
|
+
formStateRestoreCallback(state: string): void;
|
|
68
|
+
focus(): void;
|
|
69
|
+
protected firstUpdated(): void;
|
|
70
|
+
protected disconectedCallback(): void;
|
|
71
|
+
protected getPreviewElement(): Element | null;
|
|
72
|
+
codeMirrorSetup(): void;
|
|
73
|
+
handleFocus(): void;
|
|
74
|
+
handleBlur(): void;
|
|
75
|
+
handleAction(e: CustomEvent): void;
|
|
76
|
+
handleInsertImage(url: string): void;
|
|
77
|
+
updateValue(newValue?: string): void;
|
|
78
|
+
disablePreview(): void;
|
|
79
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
80
|
+
}
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html, nothing } from 'lit';
|
|
3
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
4
|
+
import { query, property, state } from 'lit/decorators.js';
|
|
5
|
+
import { defaultConfiguration } from './utils/configurations.js';
|
|
6
|
+
import { ExmgElement, observer } from '@exmg/lit-base';
|
|
7
|
+
import { markdownActions, toolbarActions } from './actions.js';
|
|
8
|
+
import { marked } from 'marked';
|
|
9
|
+
import { mixinElementInternals } from '@material/web/labs/behaviors/element-internals.js';
|
|
10
|
+
import { getFormValue, mixinFormAssociated } from '@material/web/labs/behaviors/form-associated.js';
|
|
11
|
+
import { toolbarIcons } from './icons.js';
|
|
12
|
+
import { MardownEditorValidator } from './validator/exm-markdown-editor-validator.js';
|
|
13
|
+
import { createValidator, getValidityAnchor, mixinConstraintValidation, } from '@material/web/labs/behaviors/constraint-validation.js';
|
|
14
|
+
import './exm-markdown-editor-toolbar.js';
|
|
15
|
+
const MarkdownBaseClass = mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(ExmgElement)));
|
|
16
|
+
/**
|
|
17
|
+
* Markdown editor element.
|
|
18
|
+
* An out of the box customizable Markdown Editor for Exmachina
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* <exm-markdown-editor markdown="# Header 1"></exm-markdown-editor>
|
|
22
|
+
* ```
|
|
23
|
+
* ### Styling
|
|
24
|
+
*
|
|
25
|
+
* Custom property | Description | Default
|
|
26
|
+
* ----------------|-------------|----------
|
|
27
|
+
* `--exm-markdown-editor-code-color` | Editor's text color | --md-sys-color-on-surface
|
|
28
|
+
* `--exm-markdown-editor-code-cursor-color` | Editor's cursor color | --md-sys-color-on-surface
|
|
29
|
+
* `--exm-markdown-editor-code-header-color` | H1 color in editor | #4a8fc0;
|
|
30
|
+
* `--exm-markdown-editor-code-inline-code-color` | Inline code color | #ea881f
|
|
31
|
+
* `--exm-markdown-editor-code-list-color` | Lists color | rgb(25, 165, 28)
|
|
32
|
+
* `--exm-markdown-editor-selected-code-color` | Selected text color | rgb(140, 140, 140)
|
|
33
|
+
* `--exm-markdown-editor-border-color` | Editor's border color | --md-sys-color-primary
|
|
34
|
+
* `--exm-markdown-editor-background-color` | Toolbar and default preview background | --md-sys-color-surface-container-high
|
|
35
|
+
* `--exm-markdown-editor-code-background-color` | Editor's background color when focused | --md-sys-color-surface-container-high
|
|
36
|
+
*
|
|
37
|
+
* # Events:
|
|
38
|
+
* - change - where detail is current markdown value
|
|
39
|
+
* - insert-image - if the Editor is set to upload, it will trigger this event when the insert-image is clicked
|
|
40
|
+
*
|
|
41
|
+
* @customElement
|
|
42
|
+
* @element exm-markdown-editor
|
|
43
|
+
* @memberof Exmg
|
|
44
|
+
* @extends ExmgElement
|
|
45
|
+
* @summary Markdown editor element
|
|
46
|
+
*/
|
|
47
|
+
export class MarkdownEditorElementBase extends MarkdownBaseClass {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(...arguments);
|
|
50
|
+
this.value = '';
|
|
51
|
+
this.label = '';
|
|
52
|
+
this.upload = false;
|
|
53
|
+
this.toolbarActions = toolbarActions;
|
|
54
|
+
this.toolbarIcons = toolbarIcons;
|
|
55
|
+
this.required = false;
|
|
56
|
+
this.height = 400;
|
|
57
|
+
this.preview = true;
|
|
58
|
+
this.editorConfiguration = defaultConfiguration;
|
|
59
|
+
this.editorActions = markdownActions;
|
|
60
|
+
}
|
|
61
|
+
reset() {
|
|
62
|
+
var _a;
|
|
63
|
+
this.value = (_a = this.getAttribute('value')) !== null && _a !== void 0 ? _a : '';
|
|
64
|
+
}
|
|
65
|
+
updated(changedProps) {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
if (changedProps.has('value') && ((_a = this.codeMirrorEditor) === null || _a === void 0 ? void 0 : _a.getValue()) !== this.value) {
|
|
68
|
+
(_b = this.codeMirrorEditor) === null || _b === void 0 ? void 0 : _b.setValue(this.value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
[getFormValue]() {
|
|
72
|
+
return this.value;
|
|
73
|
+
}
|
|
74
|
+
[createValidator]() {
|
|
75
|
+
return new MardownEditorValidator(() => this);
|
|
76
|
+
}
|
|
77
|
+
[getValidityAnchor]() {
|
|
78
|
+
return this.internalTextarea;
|
|
79
|
+
}
|
|
80
|
+
formResetCallback() {
|
|
81
|
+
this.reset();
|
|
82
|
+
}
|
|
83
|
+
formStateRestoreCallback(state) {
|
|
84
|
+
this.value = state;
|
|
85
|
+
}
|
|
86
|
+
focus() {
|
|
87
|
+
var _a;
|
|
88
|
+
(_a = this.codeMirrorEditor) === null || _a === void 0 ? void 0 : _a.focus();
|
|
89
|
+
}
|
|
90
|
+
firstUpdated() {
|
|
91
|
+
var _a;
|
|
92
|
+
this.codeMirrorSetup();
|
|
93
|
+
this.previewElement = this.getPreviewElement();
|
|
94
|
+
/* Height */
|
|
95
|
+
if (this.previewElement.hasAttribute('slot')) {
|
|
96
|
+
(_a = this.shadowRoot.querySelector('#preview')) === null || _a === void 0 ? void 0 : _a.remove();
|
|
97
|
+
this.previewElement.setAttribute('style', `height: ${this.height}px;`);
|
|
98
|
+
}
|
|
99
|
+
this.heightStyleMap = styleMap({ height: `${this.height}px` });
|
|
100
|
+
this.codeMirrorEditor.getWrapperElement().style.height = `${this.height}px`;
|
|
101
|
+
/** Blur global handling */
|
|
102
|
+
this.blurHandlerBinding = this.handleBlur.bind(this);
|
|
103
|
+
this.addEventListener('blur', this.handleBlur);
|
|
104
|
+
if (this.value) {
|
|
105
|
+
this.updateValue(this.value);
|
|
106
|
+
this.codeMirrorEditor.refresh();
|
|
107
|
+
}
|
|
108
|
+
if (this.label && this.required) {
|
|
109
|
+
this.setAttribute('aria-required', 'true');
|
|
110
|
+
this.label += ' *';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
disconectedCallback() {
|
|
114
|
+
this.removeEventListener('blur', this.handleBlur);
|
|
115
|
+
}
|
|
116
|
+
getPreviewElement() {
|
|
117
|
+
return this.shadowRoot.querySelector('#preview');
|
|
118
|
+
}
|
|
119
|
+
codeMirrorSetup() {
|
|
120
|
+
if (!this.editorElement) {
|
|
121
|
+
console.log('Error');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (this.markedExtension) {
|
|
125
|
+
marked.use({ extensions: this.markedExtension });
|
|
126
|
+
}
|
|
127
|
+
// eslint-disable-next-line
|
|
128
|
+
this.codeMirrorEditor = window.CodeMirror(this.editorElement, {
|
|
129
|
+
...this.editorConfiguration,
|
|
130
|
+
value: this.value || '',
|
|
131
|
+
});
|
|
132
|
+
this.codeMirrorEditor.on('focus', (_editor) => {
|
|
133
|
+
this.handleFocus();
|
|
134
|
+
});
|
|
135
|
+
this.codeMirrorEditor.on('change', (editor) => {
|
|
136
|
+
this.updateValue(editor.getValue());
|
|
137
|
+
});
|
|
138
|
+
this.internalTextarea = this.codeMirrorEditor.getInputField();
|
|
139
|
+
}
|
|
140
|
+
handleFocus() {
|
|
141
|
+
this.codeMirrorEditor.refresh();
|
|
142
|
+
}
|
|
143
|
+
handleBlur() {
|
|
144
|
+
this.previewElement.innerHTML = `
|
|
145
|
+
<main class="preview-content">
|
|
146
|
+
${this.html}
|
|
147
|
+
</main>
|
|
148
|
+
`;
|
|
149
|
+
this.preview = true;
|
|
150
|
+
}
|
|
151
|
+
handleAction(e) {
|
|
152
|
+
var _a;
|
|
153
|
+
const actionName = e.detail;
|
|
154
|
+
if (!this.editorActions[actionName]) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.editorActions[actionName](this.codeMirrorEditor);
|
|
158
|
+
this.preview = false;
|
|
159
|
+
(_a = this.codeMirrorEditor) === null || _a === void 0 ? void 0 : _a.focus();
|
|
160
|
+
}
|
|
161
|
+
handleInsertImage(url) {
|
|
162
|
+
this.editorActions['image'](this.codeMirrorEditor, url);
|
|
163
|
+
}
|
|
164
|
+
updateValue(newValue) {
|
|
165
|
+
this.html = marked.parse(newValue !== null && newValue !== void 0 ? newValue : '');
|
|
166
|
+
if (this.previewElement) {
|
|
167
|
+
this.previewElement.innerHTML = `
|
|
168
|
+
<main class="preview-content">
|
|
169
|
+
${this.html}
|
|
170
|
+
</main>
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
if (newValue === this.value) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.value = newValue || '';
|
|
177
|
+
this.fire('change', this.value);
|
|
178
|
+
}
|
|
179
|
+
disablePreview() {
|
|
180
|
+
this.preview = false;
|
|
181
|
+
}
|
|
182
|
+
render() {
|
|
183
|
+
return html `
|
|
184
|
+
<div
|
|
185
|
+
id="markdowdEditorContainer"
|
|
186
|
+
class="container"
|
|
187
|
+
@click=${this.disablePreview}
|
|
188
|
+
style=${this.heightStyleMap}
|
|
189
|
+
?label=${this.label}
|
|
190
|
+
?preview=${this.preview}
|
|
191
|
+
>
|
|
192
|
+
${this.label
|
|
193
|
+
? html `<label ?preview=${this.preview} for="markdowdEditorContainer">${this.label}</label>`
|
|
194
|
+
: nothing}
|
|
195
|
+
<exm-markdown-editor-toolbar
|
|
196
|
+
?label=${this.label}
|
|
197
|
+
?preview=${this.preview}
|
|
198
|
+
?upload=${this.upload}
|
|
199
|
+
@action=${this.handleAction}
|
|
200
|
+
.actions=${this.toolbarActions || []}
|
|
201
|
+
.icons=${this.toolbarIcons || []}
|
|
202
|
+
></exm-markdown-editor-toolbar>
|
|
203
|
+
<div id="editor" ?preview=${this.preview} style=${this.heightStyleMap}></div>
|
|
204
|
+
<div id="preview" ?preview=${this.preview} style=${this.heightStyleMap}></div>
|
|
205
|
+
<slot name="preview" ?preview=${this.preview}></slot>
|
|
206
|
+
</div>
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
__decorate([
|
|
211
|
+
property({ type: String })
|
|
212
|
+
], MarkdownEditorElementBase.prototype, "value", void 0);
|
|
213
|
+
__decorate([
|
|
214
|
+
property()
|
|
215
|
+
], MarkdownEditorElementBase.prototype, "html", void 0);
|
|
216
|
+
__decorate([
|
|
217
|
+
property({ type: String, attribute: 'label' })
|
|
218
|
+
], MarkdownEditorElementBase.prototype, "label", void 0);
|
|
219
|
+
__decorate([
|
|
220
|
+
property({ type: Boolean })
|
|
221
|
+
], MarkdownEditorElementBase.prototype, "upload", void 0);
|
|
222
|
+
__decorate([
|
|
223
|
+
property({ type: Array })
|
|
224
|
+
], MarkdownEditorElementBase.prototype, "toolbarActions", void 0);
|
|
225
|
+
__decorate([
|
|
226
|
+
property({ type: Object })
|
|
227
|
+
], MarkdownEditorElementBase.prototype, "toolbarIcons", void 0);
|
|
228
|
+
__decorate([
|
|
229
|
+
property({ type: Array })
|
|
230
|
+
], MarkdownEditorElementBase.prototype, "markedExtension", void 0);
|
|
231
|
+
__decorate([
|
|
232
|
+
property({ type: Boolean, reflect: true })
|
|
233
|
+
], MarkdownEditorElementBase.prototype, "required", void 0);
|
|
234
|
+
__decorate([
|
|
235
|
+
query('#editor')
|
|
236
|
+
], MarkdownEditorElementBase.prototype, "editorElement", void 0);
|
|
237
|
+
__decorate([
|
|
238
|
+
state()
|
|
239
|
+
], MarkdownEditorElementBase.prototype, "previewElement", void 0);
|
|
240
|
+
__decorate([
|
|
241
|
+
property({ type: Number })
|
|
242
|
+
], MarkdownEditorElementBase.prototype, "height", void 0);
|
|
243
|
+
__decorate([
|
|
244
|
+
state(),
|
|
245
|
+
observer(function (preview) {
|
|
246
|
+
var _a;
|
|
247
|
+
if (preview === false) {
|
|
248
|
+
(_a = this.codeMirrorEditor) === null || _a === void 0 ? void 0 : _a.focus();
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
], MarkdownEditorElementBase.prototype, "preview", void 0);
|
|
252
|
+
//# sourceMappingURL=exm-markdown-editor-base.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ToolbarIcons, ToolbarItem } from './types.js';
|
|
2
|
+
import { ExmgElement } from '@exmg/lit-base/index.js';
|
|
3
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
4
|
+
import '@material/web/icon/icon.js';
|
|
5
|
+
export declare class MarkdownEditorToolbarBase extends ExmgElement {
|
|
6
|
+
upload: boolean;
|
|
7
|
+
actions: ToolbarItem[];
|
|
8
|
+
icons: ToolbarIcons;
|
|
9
|
+
renderActionButtons(): import("lit-html").TemplateResult<1>[];
|
|
10
|
+
action(action: string): void;
|
|
11
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { toolbarActions } from './actions.js';
|
|
4
|
+
import { toolbarIcons } from './icons.js';
|
|
5
|
+
import { ExmgElement } from '@exmg/lit-base/index.js';
|
|
6
|
+
import { property } from 'lit/decorators.js';
|
|
7
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
8
|
+
import '@material/web/icon/icon.js';
|
|
9
|
+
export class MarkdownEditorToolbarBase extends ExmgElement {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.upload = false;
|
|
13
|
+
this.actions = toolbarActions;
|
|
14
|
+
this.icons = toolbarIcons;
|
|
15
|
+
}
|
|
16
|
+
renderActionButtons() {
|
|
17
|
+
const buttons = this.actions.map((button) => {
|
|
18
|
+
const displayedName = button.name.replace('_', '').toLocaleUpperCase();
|
|
19
|
+
return html ` <md-icon-button
|
|
20
|
+
aria-label=${displayedName}
|
|
21
|
+
title=${displayedName}
|
|
22
|
+
@click=${(e) => {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
this.action(button.name);
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<md-icon> ${this.icons[button.icon] ? this.icons[button.icon] : button.icon} </md-icon>
|
|
28
|
+
</md-icon-button>`;
|
|
29
|
+
});
|
|
30
|
+
return buttons;
|
|
31
|
+
}
|
|
32
|
+
action(action) {
|
|
33
|
+
if (action === 'image' && this.upload) {
|
|
34
|
+
this.fire('insert-image', undefined, true);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.fire('action', action, true);
|
|
38
|
+
}
|
|
39
|
+
render() {
|
|
40
|
+
return html ` <div class="toolbar-container">${this.renderActionButtons()}</div> `;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
__decorate([
|
|
44
|
+
property({ type: Boolean })
|
|
45
|
+
], MarkdownEditorToolbarBase.prototype, "upload", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
property({ type: Array })
|
|
48
|
+
], MarkdownEditorToolbarBase.prototype, "actions", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
property({ type: Object })
|
|
51
|
+
], MarkdownEditorToolbarBase.prototype, "icons", void 0);
|
|
52
|
+
//# sourceMappingURL=exm-markdown-editor-toolbar-base.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MarkdownEditorToolbarBase } from './exm-markdown-editor-toolbar-base.js';
|
|
2
|
+
export declare class MarkdownEditorToolbar extends MarkdownEditorToolbarBase {
|
|
3
|
+
static styles: import("lit").CSSResult[];
|
|
4
|
+
}
|
|
5
|
+
declare global {
|
|
6
|
+
interface HTMLElementTagNameMap {
|
|
7
|
+
'exm-markdown-editor-toolbar': MarkdownEditorToolbar;
|
|
8
|
+
}
|
|
9
|
+
}
|