@rg-dev/tinymce-helpers 1.0.2 → 1.0.4

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 (3) hide show
  1. package/index.d.mts +29 -13
  2. package/index.mjs +141 -28
  3. package/package.json +1 -1
package/index.d.mts CHANGED
@@ -1,27 +1,43 @@
1
1
  import { Editor, RawEditorOptions } from 'tinymce';
2
2
 
3
+ declare const skinsTable: {
4
+ dark: () => Promise<{
5
+ css: string;
6
+ skin: string;
7
+ }>;
8
+ default: () => Promise<{
9
+ css: string;
10
+ skin: string;
11
+ }>;
12
+ };
3
13
  type RemoveIndexSignature<T> = {
4
14
  [K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K];
5
15
  };
16
+ type Opts = {
17
+ skin?: keyof typeof skinsTable;
18
+ direction?: 'rtl' | 'ltr';
19
+ customSetup?: (editor: Editor) => void;
20
+ overrides?: (defaultOpts: EditorOpts) => EditorOpts;
21
+ };
6
22
  type EditorOpts = Omit<RemoveIndexSignature<RawEditorOptions>, 'setup' | 'target' | 'selector'> & Record<string, unknown>;
7
23
  declare class tinymceHelpers {
8
24
  private _editor;
25
+ private _lockButton?;
26
+ private originalContextMenu;
9
27
  private constructor();
10
- static initMce(el: HTMLElement, customOpts?: {
11
- skin?: "default" | "dark" | undefined;
12
- customSetup?: ((editor: Editor) => void) | undefined;
13
- overrides?: ((defaultOpts: EditorOpts) => EditorOpts) | undefined;
14
- }): Promise<tinymceHelpers>;
15
- _initMce(el: HTMLElement, customOpts?: {
16
- skin?: "default" | "dark" | undefined;
17
- customSetup?: ((editor: Editor) => void) | undefined;
18
- overrides?: ((defaultOpts: EditorOpts) => EditorOpts) | undefined;
19
- }): Promise<this>;
20
- toggleEditable(): void;
28
+ static initMce(el: HTMLElement, customOpts?: Opts): Promise<tinymceHelpers>;
29
+ _initMce(el: HTMLElement, customOpts?: Opts): Promise<this>;
30
+ private setContextMenu;
31
+ toggleReadOnly(lock?: 'yes' | 'no'): void;
21
32
  isEditable(): boolean;
22
- setContent(content: string): void;
33
+ setContent(content: string, opts?: {
34
+ dir?: 'rtl' | 'ltr';
35
+ lock?: boolean;
36
+ }): void;
23
37
  private getCustomStyles;
24
- toggleTextDirection(): void;
38
+ toggleTextDirection(force_dir?: 'rtl' | 'ltr'): void;
39
+ private createLockButton;
40
+ private updateLockButton;
25
41
  get editor(): Editor;
26
42
  destroy(): void;
27
43
  }
package/index.mjs CHANGED
@@ -51,11 +51,11 @@ import "tinymce/plugins/preview";
51
51
  import "tinymce/plugins/insertdatetime";
52
52
 
53
53
  // src/client/custom-components/mce.css?raw
54
- var mce_default = ':where(.mce-content-body) {\n --editor-text: #000;\n --editor-link: #000;\n --editor-border: #aaa;\n --editor-table-header-text: #6e6e6e;\n --editor-table-row-even: #fafafa;\n --editor-hr: #bbb;\n}\n\nli::marker {\n color: var(--editor-link) !important;\n}\n\n::-webkit-scrollbar {\n width: 10px;\n}\n\n::-webkit-scrollbar-track {\n background: var(--editor-table-row-even);\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--editor-link);\n border-radius: 35px;\n}\n\n@media print {\n .mce-content-body {\n --editor-text: #000;\n --editor-link: #000;\n --editor-border: #000;\n --editor-table-header-text: #000;\n --editor-table-row-even: transparent;\n --editor-hr: #000;\n }\n\n .mce-content-body blockquote {\n border-inline-start: unset !important;\n background: unset !important;\n color: unset !important;\n }\n\n .mce-content-body td[data-mce-selected]::after,\n .mce-content-body th[data-mce-selected]::after {\n display: none !important;\n}\n}\n\nimg {\n max-width: 100%;\n}\n\n\n\n.mce-content-body blockquote {\n margin: 25px 0;\n display: flex;\n flex-direction: column;\n padding: 15px 20px;\n border: none !important;\n background: var(--editor-table-row-even);\n color: var(--editor-text);\n}\n\n\n\n.mce-content-body blockquote p {\n margin: 0;\n}\n\n* {\n border: 0;\n padding: 0;\n margin: 0;\n background: 0 0;\n color: inherit;\n box-sizing: border-box;\n background-repeat: no-repeat;\n background-position: 50% 50%;\n font-weight: 400;\n}\n\n:focus {\n outline: 0;\n}\n\na {\n text-decoration: none;\n cursor: pointer;\n}\n\nb,\nb *,\nstrong,\nstrong * {\n font-weight: 700;\n}\n\nol,\nul {\n list-style: none;\n}\n\npre {\n font: inherit;\n}\n\nbutton,\ninput,\ninput:not([type]),\ninput[type="button"],\ninput[type="color"],\ninput[type="date"],\ninput[type="datetime-local"],\ninput[type="datetime"],\ninput[type="email"],\ninput[type="month"],\ninput[type="number"],\ninput[type="password"],\ninput[type="reset"],\ninput[type="search"],\ninput[type="submit"],\ninput[type="tel"],\ninput[type="text"],\ninput[type="time"],\ninput[type="url"],\ninput[type="week"],\nselect,\ntextarea {\n font: inherit;\n}\n\n.mce-container textarea {\n display: inline-block !important;\n}\n\n.mce-content-body {\n font-size: 28px;\n font-family: Rubik, Helvetica, Arial, sans-serif;\n padding: 0 25px 25px;\n color: var(--editor-text);\n}\n\n.mce-content-body table {\n width: 100%;\n table-layout: fixed;\n}\n\n.mce-content-body table td,\n.mce-content-body table th {\n overflow-wrap: anywhere;\n word-break: break-word;\n \n white-space: normal;\n}\n\n.mce-content-body h1 {\n font-size: 34px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h2 {\n font-size: 30px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h3 {\n font-size: 26px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h4 {\n font-size: 22px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h5 {\n font-size: 18px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h6 {\n font-size: 14px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body p {\n margin: 25px 0;\n}\n\n.mce-content-body pre {\n font-family: monospace;\n}\n\n.mce-content-body ol,\n.mce-content-body ul {\n margin-left: 15px;\n list-style-position: outside;\n margin-bottom: 20px;\n}\n\n.mce-content-body ol li,\n.mce-content-body ul li {\n margin-left: 10px;\n margin-bottom: 10px;\n color: var(--editor-text);\n}\n\n.mce-content-body ul {\n list-style-type: disc;\n}\n\n.mce-content-body ol {\n list-style-type: decimal;\n}\n\n.mce-content-body a[href] {\n color: var(--editor-link);\n text-decoration: underline;\n}\n\n.mce-content-body table caption,\n.mce-content-body table td,\n.mce-content-body table th {\n padding: 15px 7px;\n font: inherit;\n}\n\n.mce-content-body table td,\n.mce-content-body table th {\n border: 1px solid var(--editor-border);\n border-collapse: collapse;\n}\n\n.mce-content-body table th {\n font-weight: 400;\n color: var(--editor-table-header-text);\n background-position: 100% 100%;\n background-size: 2px 10px;\n background-repeat: no-repeat;\n}\n\n.mce-content-body table {\n width: 100%;\n border-spacing: 0;\n border-collapse: separate;\n border: 1px solid var(--editor-border);\n}\n\n.mce-content-body table tr:nth-child(even) {\n background: var(--editor-table-row-even);\n}\n\n.mce-content-body hr {\n border-top: 2px solid var(--editor-hr);\n}';
54
+ var mce_default = ':where(.mce-content-body) {\n --editor-text: #000;\n --editor-link: #000;\n --editor-border: #aaa;\n --editor-table-header-text: #6e6e6e;\n --editor-table-row-even: #fafafa;\n --editor-hr: #bbb;\n}\n\n\nli::marker {\n color: var(--editor-link) !important;\n}\n\n::-webkit-scrollbar {\n width: 10px;\n}\n\n::-webkit-scrollbar-track {\n background: var(--editor-table-row-even);\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--editor-link);\n border-radius: 35px;\n}\n\n@media print {\n .mce-content-body {\n --editor-text: #000;\n --editor-link: #000;\n --editor-border: #000;\n --editor-table-header-text: #000;\n --editor-table-row-even: transparent;\n --editor-hr: #000;\n }\n\n .mce-content-body blockquote {\n border-inline-start: unset !important;\n background: unset !important;\n color: unset !important;\n }\n\n .mce-content-body td[data-mce-selected]::after,\n .mce-content-body th[data-mce-selected]::after {\n display: none !important;\n}\n}\n\nimg {\n max-width: 100%;\n}\n\n\n\n.mce-content-body blockquote {\n margin: 25px 0;\n display: flex;\n flex-direction: column;\n padding: 15px 20px;\n border: none !important;\n background: var(--editor-table-row-even);\n color: var(--editor-text);\n}\n\n\n\n.mce-content-body blockquote p {\n margin: 0;\n}\n\n* {\n border: 0;\n padding: 0;\n margin: 0;\n background: 0 0;\n color: inherit;\n box-sizing: border-box;\n background-repeat: no-repeat;\n background-position: 50% 50%;\n font-weight: 400;\n}\n\n:focus {\n outline: 0;\n}\n\na {\n text-decoration: none;\n cursor: pointer;\n}\n\nb,\nb *,\nstrong,\nstrong * {\n font-weight: 700;\n}\n\nol,\nul {\n list-style: none;\n}\n\npre {\n font: inherit;\n}\n\nbutton,\ninput,\ninput:not([type]),\ninput[type="button"],\ninput[type="color"],\ninput[type="date"],\ninput[type="datetime-local"],\ninput[type="datetime"],\ninput[type="email"],\ninput[type="month"],\ninput[type="number"],\ninput[type="password"],\ninput[type="reset"],\ninput[type="search"],\ninput[type="submit"],\ninput[type="tel"],\ninput[type="text"],\ninput[type="time"],\ninput[type="url"],\ninput[type="week"],\nselect,\ntextarea {\n font: inherit;\n}\n\n.mce-container textarea {\n display: inline-block !important;\n} \n\n\n.my-custom-styles{\n direction: ltr;\n }\n\n.mce-content-body {\n font-size: 28px;\n font-family: Rubik, Helvetica, Arial, sans-serif;\n padding: 0 25px 25px;\n color: var(--editor-text);\n}\n\n.mce-content-body table {\n width: 100%;\n table-layout: fixed;\n}\n\n.mce-content-body table td,\n.mce-content-body table th {\n overflow-wrap: anywhere;\n word-break: break-word;\n \n white-space: normal;\n}\n\n.mce-content-body h1 {\n font-size: 34px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h2 {\n font-size: 30px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h3 {\n font-size: 26px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h4 {\n font-size: 22px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h5 {\n font-size: 18px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body h6 {\n font-size: 14px;\n line-height: 1.4em;\n margin: 25px 0 15px;\n}\n\n.mce-content-body p {\n margin: 25px 0;\n}\n\n.mce-content-body pre {\n font-family: monospace;\n}\n\n.mce-content-body ol,\n.mce-content-body ul {\n margin-left: 15px;\n list-style-position: outside;\n margin-bottom: 20px;\n}\n\n.mce-content-body ol li,\n.mce-content-body ul li {\n margin-left: 10px;\n margin-bottom: 10px;\n color: var(--editor-text);\n}\n\n.mce-content-body ul {\n list-style-type: disc;\n}\n\n.mce-content-body ol {\n list-style-type: decimal;\n}\n\n.mce-content-body a[href] {\n color: var(--editor-link);\n text-decoration: underline;\n}\n\n.mce-content-body table caption,\n.mce-content-body table td,\n.mce-content-body table th {\n padding: 15px 7px;\n font: inherit;\n}\n\n.mce-content-body table td,\n.mce-content-body table th {\n border: 1px solid var(--editor-border);\n border-collapse: collapse;\n}\n\n.mce-content-body table th {\n font-weight: 400;\n color: var(--editor-table-header-text);\n background-position: 100% 100%;\n background-size: 2px 10px;\n background-repeat: no-repeat;\n}\n\n.mce-content-body table {\n width: 100%;\n border-spacing: 0;\n border-collapse: separate;\n border: 1px solid var(--editor-border);\n}\n\n.mce-content-body table tr:nth-child(even) {\n background: var(--editor-table-row-even);\n}\n\n.mce-content-body hr {\n border-top: 2px solid var(--editor-hr);\n}';
55
55
 
56
56
  // src/client/custom-components/tinymce-helpers.ts
57
57
  import { isNonEmptyString } from "@rg-dev/stdlib/lib/common-env";
58
- async function defaultSkin(document = false) {
58
+ async function defaultSkin(document2 = false) {
59
59
  const contentSkin = `default`;
60
60
  const shellSkin = "oxide";
61
61
  await Promise.all([
@@ -66,9 +66,7 @@ async function defaultSkin(document = false) {
66
66
  return {
67
67
  css: tinymce.Resource.get(`content/${contentSkin}/content.css`) + tinymce.Resource.get(`ui/${shellSkin}/content.css`) + `
68
68
 
69
- .my-custom-styles{
70
- direction: rtl;
71
- }
69
+
72
70
 
73
71
  `,
74
72
  skin: shellSkin
@@ -87,9 +85,7 @@ async function darkSkin() {
87
85
  return {
88
86
  css: tinymce.Resource.get(`content/${contentSkin}/content.css`) + tinymce.Resource.get(`ui/${shellSkin}/content.css`) + `
89
87
 
90
- .my-custom-styles{
91
- direction: rtl;
92
- }
88
+
93
89
 
94
90
  .mce-content-body {
95
91
  --editor-bg: #222f3e;
@@ -112,6 +108,8 @@ var skinsTable = {
112
108
  var tinymceHelpers = class _tinymceHelpers {
113
109
  constructor() {
114
110
  __publicField(this, "_editor");
111
+ __publicField(this, "_lockButton");
112
+ __publicField(this, "originalContextMenu");
115
113
  }
116
114
  static async initMce(el, customOpts = Object()) {
117
115
  return await new _tinymceHelpers()._initMce(el, customOpts);
@@ -127,19 +125,39 @@ var tinymceHelpers = class _tinymceHelpers {
127
125
  let opts = {
128
126
  setup(editor) {
129
127
  var _a2;
130
- editor.ui.registry.addMenuItem("create-block-below", {
131
- text: "Create block below",
132
- icon: "plus",
128
+ editor.ui.registry.addIcon("lock-icon", `<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M12 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-9h-1V6a5 5 0 0 0-10 0v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2zM9 6a3 3 0 0 1 6 0v2H9V6zm9 14H6V10h12v10z"/></svg>`);
129
+ editor.ui.registry.addButton("lockContent", {
130
+ tooltip: "Lock editing",
131
+ icon: "lock-icon",
133
132
  onAction: () => {
134
- const node = editor.selection.getNode();
135
- const body = editor.getBody();
136
- const topBlock = editor.dom.getParent(node, (n) => n.parentNode === body) || node;
137
- const newBlock = editor.dom.create("p", {}, '<br data-mce-bogus="1">');
138
- editor.dom.insertAfter(newBlock, topBlock);
139
- editor.selection.setCursorLocation(newBlock, 0);
140
- editor.focus();
133
+ that.toggleReadOnly("yes");
141
134
  }
142
135
  });
136
+ editor.on("keydown", (e) => {
137
+ if (e.key !== "Enter" || !e.shiftKey) {
138
+ return;
139
+ }
140
+ e.preventDefault();
141
+ e.stopImmediatePropagation();
142
+ editor.getDoc().execCommand("insertParagraph", false, void 0);
143
+ editor.nodeChanged();
144
+ }, true);
145
+ editor.ui.registry.addMenuItem(
146
+ "create-block-below",
147
+ {
148
+ text: "Create block below",
149
+ icon: "plus",
150
+ onAction: () => {
151
+ const node = editor.selection.getNode();
152
+ const body = editor.getBody();
153
+ const topBlock = editor.dom.getParent(node, (n) => n.parentNode === body) || node;
154
+ const newBlock = editor.dom.create("p", {}, '<br data-mce-bogus="1">');
155
+ editor.dom.insertAfter(newBlock, topBlock);
156
+ editor.selection.setCursorLocation(newBlock, 0);
157
+ editor.focus();
158
+ }
159
+ }
160
+ );
143
161
  editor.ui.registry.addMenuItem("create-block-above", {
144
162
  text: "Create block above",
145
163
  icon: "plus",
@@ -168,7 +186,7 @@ var tinymceHelpers = class _tinymceHelpers {
168
186
  {
169
187
  text: "toggle readonly",
170
188
  onAction: async () => {
171
- that.toggleEditable();
189
+ that.toggleReadOnly();
172
190
  }
173
191
  }
174
192
  );
@@ -234,12 +252,13 @@ var tinymceHelpers = class _tinymceHelpers {
234
252
  id: "tinymce",
235
253
  extended_valid_elements: "img[drawio-diagram|contenteditable|style|class|src]",
236
254
  font_family_formats: "Rubik=Rubik, Helvetica, Arial, sans-serif; Andale Mono=andale mono,times; Arial=arial,helvetica,sans-serif; Arial Black=arial black,avant garde; Book Antiqua=book antiqua,palatino; Comic Sans MS=comic sans ms,sans-serif; Courier New=courier new,courier; Georgia=georgia,palatino; Helvetica=helvetica; Impact=impact,chicago; Symbol=symbol; Tahoma=tahoma,arial,helvetica,sans-serif; Terminal=terminal,monaco; Times New Roman=times new roman,times; Trebuchet MS=trebuchet ms,geneva; Verdana=verdana,geneva; Webdings=webdings; Wingdings=wingdings,zapf dingbats; Afacad=afacad,sans-serif; Marcellus=marcellus,serif; Poppins=poppins,sans-serif; Raleway=raleway,sans-serif;",
255
+ "statusbar": false,
237
256
  skin: theSkin.skin,
238
257
  highlight_on_focus: false,
239
258
  content_style: theSkin.css,
240
259
  body_class: "my-custom-styles",
241
260
  target: el,
242
- toolbar: "undo redo | increaseFontSize decreaseFontSize bold italic underline strikethrough | fontfamily fontsize | ltr rtl alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | charmap emoticons | fullscreen preview save print | insertfile image media template link anchor codesample",
261
+ toolbar: "lockContent | undo redo | increaseFontSize decreaseFontSize bold italic underline strikethrough | fontfamily fontsize | ltr rtl alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | charmap emoticons | fullscreen preview print | image media link anchor codesample",
243
262
  toolbar_sticky: true,
244
263
  promotion: false,
245
264
  image_advtab: true,
@@ -278,8 +297,24 @@ var tinymceHelpers = class _tinymceHelpers {
278
297
  throw new Error("no skin");
279
298
  }
280
299
  const mce = this._editor = await tinymce.init(opts).then((x) => x[0]);
300
+ this.originalContextMenu = mce.options.get("contextmenu");
281
301
  const all = mce.ui.registry.getAll();
282
302
  const availableMenuItems = all.menuItems;
303
+ const availableButtons = all.buttons;
304
+ if (typeof opts.toolbar == "string") {
305
+ if (!isNonEmptyString(opts.toolbar.trim())) {
306
+ console.error("Toolbar is missing items");
307
+ } else {
308
+ const items = opts.toolbar.trim().split(/\s+/).filter((item) => item !== "|");
309
+ for (const item of items) {
310
+ if (!availableButtons[item.toLowerCase()] && !availableMenuItems[item.toLowerCase()]) {
311
+ console.error(
312
+ `Toolbar references unknown button "${item}"`
313
+ );
314
+ }
315
+ }
316
+ }
317
+ }
283
318
  if (typeof opts.contextmenu == "string") {
284
319
  if (!isNonEmptyString(opts.contextmenu.trim())) {
285
320
  console.error("Context menu is missing items");
@@ -310,20 +345,48 @@ var tinymceHelpers = class _tinymceHelpers {
310
345
  }
311
346
  }
312
347
  }
348
+ if (customOpts.direction) {
349
+ this.toggleTextDirection(customOpts.direction);
350
+ }
351
+ this.createLockButton();
313
352
  return this;
314
353
  }
315
- toggleEditable() {
316
- const curr = this.editor.getBody().isContentEditable;
317
- const toolbar = this.editor.getContainer().querySelector(".tox-toolbar-overlord");
318
- if (toolbar) {
319
- toolbar.style.display = curr ? "none" : "";
354
+ setContextMenu(enabled) {
355
+ var _a;
356
+ if (!enabled) {
357
+ this.editor.options.set("contextmenu", false);
358
+ } else {
359
+ this.editor.options.set("contextmenu", (_a = this.originalContextMenu) != null ? _a : "");
360
+ }
361
+ }
362
+ toggleReadOnly(lock) {
363
+ let curr = this.editor.getBody().isContentEditable;
364
+ if (lock) {
365
+ if (lock == "no") {
366
+ curr = false;
367
+ } else {
368
+ curr = true;
369
+ }
370
+ }
371
+ const container = this.editor.getContainer();
372
+ const editorBar = container.querySelector(".tox-editor-header");
373
+ if (editorBar) {
374
+ editorBar.style.display = curr ? "none" : "";
320
375
  }
376
+ this.setContextMenu(!curr);
321
377
  this.editor.setEditableRoot(!curr);
378
+ this.updateLockButton();
322
379
  }
323
380
  isEditable() {
324
381
  return this.editor.getBody().isContentEditable;
325
382
  }
326
- setContent(content) {
383
+ setContent(content, opts = Object()) {
384
+ if (opts.lock != void 0) {
385
+ this.toggleReadOnly(opts.lock ? "yes" : "no");
386
+ }
387
+ if (opts.dir) {
388
+ this.toggleTextDirection(opts.dir);
389
+ }
327
390
  this._editor.setContent(content);
328
391
  this._editor.undoManager.clear();
329
392
  }
@@ -337,7 +400,7 @@ var tinymceHelpers = class _tinymceHelpers {
337
400
  }
338
401
  }
339
402
  }
340
- toggleTextDirection() {
403
+ toggleTextDirection(force_dir) {
341
404
  if (!this.isEditable()) {
342
405
  return;
343
406
  }
@@ -345,7 +408,10 @@ var tinymceHelpers = class _tinymceHelpers {
345
408
  if (!styles) {
346
409
  return;
347
410
  }
348
- const dir = styles.direction == "rtl" ? "ltr" : "rtl";
411
+ let dir = styles.direction == "rtl" ? "ltr" : "rtl";
412
+ if (force_dir) {
413
+ dir = force_dir;
414
+ }
349
415
  const newDirection = dir;
350
416
  styles.direction = newDirection;
351
417
  const tds = this._editor.dom.select("td");
@@ -353,10 +419,57 @@ var tinymceHelpers = class _tinymceHelpers {
353
419
  this._editor.dom.setStyle(td, "direction", dir);
354
420
  }
355
421
  }
422
+ createLockButton() {
423
+ const container = this._editor.getContainer();
424
+ if (!container) {
425
+ return;
426
+ }
427
+ const btn = document.createElement("button");
428
+ btn.type = "button";
429
+ btn.className = "tinymce-lock-button";
430
+ btn.title = "Unlock editing";
431
+ btn.setAttribute("aria-label", "Unlock editing");
432
+ btn.innerHTML = `<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M12 17a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6-9h-1V6a5 5 0 0 0-10 0v2H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2zM9 6a3 3 0 0 1 6 0v2H9V6zm9 14H6V10h12v10z"/></svg>`;
433
+ btn.style.cssText = `
434
+ position:absolute;
435
+ top:8px;
436
+ right:8px;
437
+ z-index:1000;
438
+ display:none;
439
+ align-items:center;
440
+ justify-content:center;
441
+ width:32px;
442
+ height:32px;
443
+ padding:0;
444
+ border-radius:6px;
445
+ border:1px solid rgba(0,0,0,0.15);
446
+ background:black;
447
+ color:rgb(255, 255, 255);;
448
+ cursor:pointer;
449
+ box-shadow:0 1px 3px rgba(0,0,0,0.2);
450
+ `;
451
+ btn.addEventListener("click", () => {
452
+ this.toggleReadOnly();
453
+ });
454
+ if (getComputedStyle(container).position === "static") {
455
+ container.style.position = "relative";
456
+ }
457
+ container.appendChild(btn);
458
+ this._lockButton = btn;
459
+ this.updateLockButton();
460
+ }
461
+ updateLockButton() {
462
+ if (!this._lockButton) {
463
+ return;
464
+ }
465
+ this._lockButton.style.display = this.isEditable() ? "none" : "flex";
466
+ }
356
467
  get editor() {
357
468
  return this._editor;
358
469
  }
359
470
  destroy() {
471
+ var _a;
472
+ (_a = this._lockButton) == null ? void 0 : _a.remove();
360
473
  this._editor.destroy();
361
474
  }
362
475
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rg-dev/tinymce-helpers",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {