@exmg/exm-wysiwyg-editor 1.1.23 → 1.1.25

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.
@@ -1,5 +1,5 @@
1
1
  import './dialog/exm-wysiwyg-toolbar-link-dialog.js';
2
- import { sleep } from '../../utils.js';
2
+ import { sleep } from '../../utils/index.js';
3
3
  /**
4
4
  * Open the dialog and wait until the close or submit button has been pressed
5
5
  * @param url The initial url for the input
@@ -1,8 +1,13 @@
1
1
  import { Editor } from '@tiptap/core';
2
+ import { getFormValue } from '@material/web/labs/behaviors/form-associated.js';
3
+ import { WysiwygEditorValidator } from './validator/exm-wysiwyg-editor-validator.js';
4
+ import { createValidator, getValidityAnchor } from '@material/web/labs/behaviors/constraint-validation.js';
2
5
  import { ExmgElement } from '@exmg/lit-base';
3
6
  import { EditorActions } from './types.js';
4
7
  import './exm-wysiwyg-editor-toolbar.js';
5
8
  import { WysiwygEditorToolbar } from './exm-wysiwyg-editor-toolbar.js';
9
+ declare const WysiwygBaseClass: 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>;
10
+ type WysiwygMode = 'md' | 'html';
6
11
  /**
7
12
  * # Events:
8
13
  * - change - where detail is current html value
@@ -10,13 +15,23 @@ import { WysiwygEditorToolbar } from './exm-wysiwyg-editor-toolbar.js';
10
15
  * - focus - on editor focus
11
16
  *
12
17
  * @customElement
13
- * @element exm-markdown-editor
18
+ * @element exm-wysiwyg-editor
14
19
  * @memberof Exmg
15
20
  * @extends ExmgElement
16
- * @summary Markdown editor element
21
+ * @summary Wysiwyg editor element
17
22
  */
18
- export declare class WysiwygEditorElementBase extends ExmgElement {
23
+ export declare class WysiwygEditorElementBase extends WysiwygBaseClass {
24
+ /**
25
+ * Input value. Can be either a markdown string or html. Don't forget to set the mode
26
+ */
19
27
  value: string;
28
+ /**
29
+ * The value type of the Editor. Can de either `md` for Markdown, of `html` for HTML.
30
+ * Internally the editor uses html, so when set to markdown, the values will be converted
31
+ *
32
+ * @default html
33
+ */
34
+ mode: WysiwygMode;
20
35
  label: string;
21
36
  upload: boolean;
22
37
  editorActions: EditorActions[];
@@ -25,10 +40,22 @@ export declare class WysiwygEditorElementBase extends ExmgElement {
25
40
  supportingText: string;
26
41
  editorElement?: HTMLDivElement;
27
42
  toolbar?: WysiwygEditorToolbar;
43
+ /**
44
+ * The actual content used in the editor. This is always the html content of the editor
45
+ */
46
+ content: string;
28
47
  editor?: Editor;
48
+ internalTextarea?: HTMLTextAreaElement;
29
49
  reset(): void;
50
+ [getFormValue](): string;
51
+ [createValidator](): WysiwygEditorValidator;
52
+ [getValidityAnchor](): HTMLTextAreaElement;
53
+ formResetCallback(): void;
54
+ formStateRestoreCallback(state: string): void;
55
+ focus(): void;
56
+ disconnectedCallback(): void;
30
57
  protected firstUpdated(): void;
31
- attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
58
+ attributeChangedCallback(name: string, _old: string | null, value: string | null): Promise<void>;
32
59
  private createExtensionList;
33
60
  editorSetup(): void;
34
61
  private handleEditorUpdate;
@@ -37,3 +64,4 @@ export declare class WysiwygEditorElementBase extends ExmgElement {
37
64
  private handleFocus;
38
65
  protected render(): import("lit-html").TemplateResult<1>;
39
66
  }
67
+ export {};
@@ -14,6 +14,10 @@ import Strike from '@tiptap/extension-strike';
14
14
  import Underline from '@tiptap/extension-underline';
15
15
  import Blockquote from '@tiptap/extension-blockquote';
16
16
  import Link from '@tiptap/extension-link';
17
+ import { mixinElementInternals } from '@material/web/labs/behaviors/element-internals.js';
18
+ import { getFormValue, mixinFormAssociated } from '@material/web/labs/behaviors/form-associated.js';
19
+ import { WysiwygEditorValidator } from './validator/exm-wysiwyg-editor-validator.js';
20
+ import { createValidator, getValidityAnchor, mixinConstraintValidation, } from '@material/web/labs/behaviors/constraint-validation.js';
17
21
  import { query, property, state } from 'lit/decorators.js';
18
22
  import { ExmgElement } from '@exmg/lit-base';
19
23
  import { classMap } from 'lit/directives/class-map.js';
@@ -21,6 +25,8 @@ import { EditorActions } from './types.js';
21
25
  import './exm-wysiwyg-editor-toolbar.js';
22
26
  import { linkOptions } from './actions/anchor/options.js';
23
27
  import { defaultActionList } from './toolbarActions.js';
28
+ import { md2html, html2md } from './utils/index.js';
29
+ const WysiwygBaseClass = mixinConstraintValidation(mixinFormAssociated(mixinElementInternals(ExmgElement)));
24
30
  /**
25
31
  * # Events:
26
32
  * - change - where detail is current html value
@@ -28,26 +34,62 @@ import { defaultActionList } from './toolbarActions.js';
28
34
  * - focus - on editor focus
29
35
  *
30
36
  * @customElement
31
- * @element exm-markdown-editor
37
+ * @element exm-wysiwyg-editor
32
38
  * @memberof Exmg
33
39
  * @extends ExmgElement
34
- * @summary Markdown editor element
40
+ * @summary Wysiwyg editor element
35
41
  */
36
- export class WysiwygEditorElementBase extends ExmgElement {
42
+ export class WysiwygEditorElementBase extends WysiwygBaseClass {
37
43
  constructor() {
38
44
  super(...arguments);
45
+ /**
46
+ * Input value. Can be either a markdown string or html. Don't forget to set the mode
47
+ */
39
48
  this.value = '';
49
+ /**
50
+ * The value type of the Editor. Can de either `md` for Markdown, of `html` for HTML.
51
+ * Internally the editor uses html, so when set to markdown, the values will be converted
52
+ *
53
+ * @default html
54
+ */
55
+ this.mode = 'html';
40
56
  this.label = '';
41
57
  this.upload = false;
42
58
  this.editorActions = defaultActionList;
43
59
  this.required = false;
44
60
  this.disabled = false;
45
61
  this.supportingText = '';
62
+ /**
63
+ * The actual content used in the editor. This is always the html content of the editor
64
+ */
65
+ this.content = '';
46
66
  }
47
67
  reset() {
48
68
  var _a;
49
69
  this.value = (_a = this.getAttribute('value')) !== null && _a !== void 0 ? _a : '';
50
70
  }
71
+ [getFormValue]() {
72
+ return this.value;
73
+ }
74
+ [createValidator]() {
75
+ return new WysiwygEditorValidator(() => 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.editor) === null || _a === void 0 ? void 0 : _a.commands.focus();
89
+ }
90
+ disconnectedCallback() {
91
+ this.removeEventListener('blur', this.handleBlur);
92
+ }
51
93
  firstUpdated() {
52
94
  this.editorSetup();
53
95
  if (this.label && this.required) {
@@ -55,11 +97,17 @@ export class WysiwygEditorElementBase extends ExmgElement {
55
97
  this.label += ' *';
56
98
  }
57
99
  }
58
- attributeChangedCallback(name, _old, value) {
100
+ async attributeChangedCallback(name, _old, value) {
59
101
  var _a, _b;
60
102
  this[name] = value;
61
103
  if (name === 'value') {
62
- (_a = this.editor) === null || _a === void 0 ? void 0 : _a.commands.setContent(value);
104
+ if (this.mode === 'md') {
105
+ this.content = await md2html(this.value);
106
+ }
107
+ else {
108
+ this.content = this.value;
109
+ }
110
+ (_a = this.editor) === null || _a === void 0 ? void 0 : _a.commands.setContent(this.content);
63
111
  }
64
112
  if (name === 'disabled') {
65
113
  this.disabled = value !== null;
@@ -139,8 +187,13 @@ export class WysiwygEditorElementBase extends ExmgElement {
139
187
  onFocus: this.handleFocus.bind(this),
140
188
  });
141
189
  }
142
- handleEditorUpdate({ editor }) {
143
- this.value = editor.getHTML();
190
+ async handleEditorUpdate({ editor }) {
191
+ if (this.mode === 'md') {
192
+ this.value = await html2md(editor.getHTML());
193
+ }
194
+ else {
195
+ this.value = editor.getHTML();
196
+ }
144
197
  this.fire('change', this.value);
145
198
  }
146
199
  handleEditorTransaction() {
@@ -181,6 +234,9 @@ export class WysiwygEditorElementBase extends ExmgElement {
181
234
  __decorate([
182
235
  property({ type: String })
183
236
  ], WysiwygEditorElementBase.prototype, "value", void 0);
237
+ __decorate([
238
+ property({ type: String })
239
+ ], WysiwygEditorElementBase.prototype, "mode", void 0);
184
240
  __decorate([
185
241
  property({ type: String })
186
242
  ], WysiwygEditorElementBase.prototype, "label", void 0);
@@ -205,6 +261,9 @@ __decorate([
205
261
  __decorate([
206
262
  query('exm-wysiwyg-editor-toolbar')
207
263
  ], WysiwygEditorElementBase.prototype, "toolbar", void 0);
264
+ __decorate([
265
+ state()
266
+ ], WysiwygEditorElementBase.prototype, "content", void 0);
208
267
  __decorate([
209
268
  state()
210
269
  ], WysiwygEditorElementBase.prototype, "editor", void 0);
@@ -15,7 +15,7 @@ export class WysiwygEditorToolbarBase extends ExmgElement {
15
15
  this.editorActions = [];
16
16
  }
17
17
  handleSelectionUpdate() {
18
- this.requestUpdate();
18
+ this.requestUpdate && this.requestUpdate();
19
19
  }
20
20
  firstUpdated() {
21
21
  var _a;
@@ -0,0 +1 @@
1
+ export declare const html2md: (html: string) => Promise<string>;
@@ -0,0 +1,12 @@
1
+ import TurndownService from 'turndown';
2
+ import { gfm } from 'turndown-plugin-gfm';
3
+ export const html2md = async (html) => {
4
+ const turndownService = new TurndownService({
5
+ headingStyle: 'atx',
6
+ bulletListMarker: '-',
7
+ });
8
+ turndownService.use(gfm);
9
+ const markdown = turndownService.turndown(html);
10
+ return await markdown;
11
+ };
12
+ //# sourceMappingURL=html2md.js.map
@@ -0,0 +1,3 @@
1
+ export { sleep } from './sleep.js';
2
+ export { md2html } from './md2html.js';
3
+ export { html2md } from './html2md.js';
@@ -0,0 +1,4 @@
1
+ export { sleep } from './sleep.js';
2
+ export { md2html } from './md2html.js';
3
+ export { html2md } from './html2md.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ export declare const md2html: (markdown: string) => Promise<string>;
@@ -0,0 +1,10 @@
1
+ import { marked } from 'marked';
2
+ export const md2html = async (markdown) => {
3
+ marked.use({
4
+ pedantic: false,
5
+ gfm: true,
6
+ breaks: false,
7
+ });
8
+ return await marked.parse(markdown);
9
+ };
10
+ //# sourceMappingURL=md2html.js.map
@@ -1,2 +1,2 @@
1
1
  export const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
2
- //# sourceMappingURL=utils.js.map
2
+ //# sourceMappingURL=sleep.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Validator } from '@material/web/labs/behaviors/validators/validator.js';
7
+ export interface WysiwygEditorState {
8
+ /**
9
+ * Whether the checkbox is required.
10
+ */
11
+ readonly required: boolean;
12
+ readonly value: string;
13
+ readonly internalElement?: HTMLInputElement;
14
+ }
15
+ export declare class WysiwygEditorValidator extends Validator<WysiwygEditorState> {
16
+ private inputElement?;
17
+ protected computeValidity(state: WysiwygEditorState): {
18
+ validity: ValidityState;
19
+ validationMessage: string;
20
+ };
21
+ protected equals(prevState: WysiwygEditorState, state: WysiwygEditorState): boolean;
22
+ protected copy(state: WysiwygEditorState): WysiwygEditorState;
23
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Validator } from '@material/web/labs/behaviors/validators/validator.js';
7
+ export class WysiwygEditorValidator extends Validator {
8
+ computeValidity(state) {
9
+ var _a;
10
+ if (!this.inputElement) {
11
+ this.inputElement = state.internalElement;
12
+ }
13
+ /**
14
+ * Lazily create the input element if not existing
15
+ */
16
+ if (!this.inputElement) {
17
+ this.inputElement = document.createElement('input');
18
+ this.inputElement.type = 'text';
19
+ }
20
+ /**
21
+ * Set values to the input element
22
+ */
23
+ this.inputElement.value = (_a = state.value) !== null && _a !== void 0 ? _a : '';
24
+ this.inputElement.required = state.required;
25
+ return {
26
+ validity: this.inputElement.validity,
27
+ validationMessage: this.inputElement.validationMessage,
28
+ };
29
+ }
30
+ equals(prevState, state) {
31
+ return prevState.value === state.value && prevState.required === state.required;
32
+ }
33
+ copy(state) {
34
+ return {
35
+ ...state,
36
+ };
37
+ }
38
+ }
39
+ //# sourceMappingURL=exm-wysiwyg-editor-validator.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exmg/exm-wysiwyg-editor",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,7 +47,11 @@
47
47
  "@tiptap/extension-paragraph": "^2.11.7",
48
48
  "@tiptap/extension-strike": "^2.11.7",
49
49
  "@tiptap/extension-text": "^2.11.7",
50
- "@tiptap/extension-underline": "^2.11.7"
50
+ "@tiptap/extension-underline": "^2.11.7",
51
+ "@types/turndown": "^5.0.0",
52
+ "marked": "^15.0.0",
53
+ "turndown": "^7.2.0",
54
+ "turndown-plugin-gfm": "^1.0.2"
51
55
  },
52
56
  "peerDependencies": {
53
57
  "@exmg/lit-base": "^3.0.3",
@@ -63,5 +67,5 @@
63
67
  "publishConfig": {
64
68
  "access": "public"
65
69
  },
66
- "gitHead": "7f954ebfd386f5084e7f76a04c839de66aea3102"
70
+ "gitHead": "e0e168ccfc3d87bcf73b42fe244bba2dd40fc926"
67
71
  }
File without changes