leksy-editor 1.0.4 → 1.0.6

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/constant.js CHANGED
@@ -1,4 +1,4 @@
1
- const WHILE_LISTED_TAG = "br|p|div|pre|blockquote|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|thead|tbody|tr|th|td|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary|font|article|section|main|header|footer|aside|mark|cite|abbr|dfn|nav|caption|picture|dl|dt|dd"
1
+ const WHILE_LISTED_TAG = "br|p|div|pre|blockquote|h|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|thead|tbody|tr|th|td|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary|font|article|section|main|header|footer|aside|mark|cite|abbr|dfn|nav|caption|picture|dl|dt|dd|time|label|kbd|samp|wbr";
2
2
  const WHILE_LISTED_ATTRIBUTES = "align|alt|bgcolor|border|cellpadding|cellspacing|color|colspan|contenteditable|dir|download|face|height|href|lang|name|rowspan|size|src|style|target|title|valign|width|background|media|sizes|srcset";
3
3
 
4
4
  const REGEX = {
package/index.js CHANGED
@@ -20,6 +20,7 @@ class LeksyEditor {
20
20
  * @property {String} pexelsApiKey
21
21
  * @property {String} tenorApiKey
22
22
  * @property {Object} cssVariables
23
+ * @property {Boolean} disablePastedColorStyles
23
24
  */
24
25
  /**
25
26
  *
@@ -457,7 +458,7 @@ class LeksyEditor {
457
458
  CSS_VARIABLES[key] = cssVariable[key];
458
459
  }
459
460
  });
460
-
461
+
461
462
  CSS.IFRAME_VARIABLES = `
462
463
  :root {
463
464
  --primary: ${CSS_VARIABLES.primary};
@@ -601,12 +602,12 @@ class LeksyEditor {
601
602
  }
602
603
 
603
604
  const pastedData = clipboardData.getData('text/html') || clipboardData.getData('text/plain');
604
- core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(pastedData))
605
+ core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(pastedData, options, core))
605
606
  };
606
607
  contentEditableDiv.ondrop = (e) => {
607
608
  e.preventDefault();
608
609
  const droppedData = e.dataTransfer.getData('text/html') || e.dataTransfer.getData('text/plain');
609
- core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(droppedData))
610
+ core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(droppedData, options, core))
610
611
  };
611
612
  contentEditableDiv.onblur = (e) => {
612
613
  core.onBlur(e.target.innerHTML)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leksy-editor",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Leksy Editor is an alternative to traditional WYSIWYG editors, designed primarily for creating mail templates, blogs, and documents without any content manipulation.",
5
5
  "main": "index.js",
6
6
  "directories": {
package/plugin.js CHANGED
@@ -136,12 +136,12 @@ const PLUGINS = {
136
136
  title: 'Code View',
137
137
  icon: SVG.CODE_VIEW,
138
138
  type: 'button',
139
- click: (event, core) => {
139
+ click: (event, core, options) => {
140
140
  if (core.state.isCodeViewOpen) {
141
141
  core.elements.codeview.style.display = 'none'
142
142
  core.elements.iframeContainer.style.display = 'block'
143
143
  if (core.elements.stepper) core.elements.stepper.style.display = 'flex'
144
- const _cleanHTML = cleanHTML(core.elements.codeview.value)
144
+ const _cleanHTML = cleanHTML(core.elements.codeview.value, options, core)
145
145
  core.onChange(_cleanHTML);
146
146
  core.elements.editor.innerHTML = _cleanHTML
147
147
  core.elements.editor.focus()
@@ -314,6 +314,42 @@ const PLUGINS = {
314
314
  core.updateCaretPosition()
315
315
  }
316
316
  },
317
+ 'remove_font_color': {
318
+ title: 'Remove Font Color',
319
+ icon: `${SVG.REMOVE_FORMAT}${SVG.FONT_COLOR}`,
320
+ type: "button",
321
+ click: (event, core, options) => {
322
+ if (!core.state.range) return;
323
+ core.elements.editor.focus();
324
+ const container = document.createElement('div');
325
+ const clonedContents = core.state.range.cloneContents();
326
+ container.appendChild(clonedContents);
327
+ if (container.innerHTML === container.innerText) {
328
+ const parentEl = core.state.range.startContainer.parentElement;
329
+ parentEl.removeAttribute('color');
330
+ parentEl.style.removeProperty('color');
331
+
332
+ } else {
333
+ container.querySelectorAll('[style]').forEach((el) => {
334
+ el.style.removeProperty('color');
335
+ if (!el.getAttribute('style')) {
336
+ el.removeAttribute('style');
337
+ }
338
+ });
339
+ container.querySelectorAll('font[color]').forEach((el) => {
340
+ el.removeAttribute('color');
341
+ });
342
+ }
343
+ core.state.range.deleteContents();
344
+ const frag = document.createDocumentFragment();
345
+ Array.from(container.childNodes).forEach((node) => {
346
+ frag.appendChild(node);
347
+ });
348
+ core.state.range.insertNode(frag);
349
+ core.updateCaretPosition();
350
+ }
351
+
352
+ },
317
353
  'highlight_color': {
318
354
  title: 'Highlight Color',
319
355
  icon: SVG.HIGHLIGHT_COLOR,
@@ -324,6 +360,15 @@ const PLUGINS = {
324
360
  core.updateCaretPosition()
325
361
  }
326
362
  },
363
+ 'remove_highlight_color': {
364
+ title: 'Remove Highlight Color',
365
+ icon: `${SVG.REMOVE_FORMAT}${SVG.HIGHLIGHT_COLOR}`,
366
+ type: "button",
367
+ click: (event, core, options) => {
368
+ core.elements.iframeWindow.execCommand('hiliteColor', false, "transparent"); // Apply color to selected text
369
+ core.updateCaretPosition()
370
+ }
371
+ },
327
372
  'text_style': {
328
373
  title: 'Text Style',
329
374
  icon: SVG.TEXT_STYLE,
package/utilities.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CLASSES, FONT_SIZES, FONTS, FORMATS, GIPHY_POWERED_IMAGE, REGEX, RESIZER_PLUGINS, SOCIAL_MEDIA_BASEURLS, SOCIAL_MEDIA_PATTERNS, SVG, TABLE_PLUGINS } from "./constant";
2
2
 
3
- const traverseAndClean = (element) => {
3
+ const traverseAndClean = (element, options, core) => {
4
4
  if (!element?.childNodes) return;
5
5
 
6
6
  // Iterate through child nodes
@@ -22,22 +22,58 @@ const traverseAndClean = (element) => {
22
22
  attributesToRemove.push(attr.name);
23
23
  }
24
24
  }
25
+ if (options?.disablePastedColorStyles) {
26
+ const nodeStyles = node.style
27
+ let bgColor = nodeStyles.backgroundColor;
28
+ let textColor = nodeStyles.color;
29
+ let currentNode = core?.state.range.startContainer;
30
+ let bodyBg = getEffectiveBackgroundColor(currentNode);
31
+ if (bodyBg && bodyBg?.startsWith('rgb')) {
32
+ bodyBg = rgbToHex(bodyBg)
33
+ }
34
+ if (bgColor && bgColor?.startsWith('rgb')) {
35
+ bgColor = rgbToHex(bgColor)
36
+ }
37
+ if (textColor && textColor?.startsWith('rgb')) {
38
+ textColor = rgbToHex(textColor)
39
+ }
40
+
41
+ if (bodyBg === bgColor) node.style.removeProperty('background-color');
42
+ if (bodyBg === textColor) node.style.removeProperty('color');
43
+ }
25
44
  attributesToRemove.forEach(attr => node.removeAttribute(attr));
26
45
 
27
46
  // Process child nodes
28
- traverseAndClean(node);
47
+ traverseAndClean(node, options,core);
29
48
  }
30
49
  }
31
50
  }
32
51
  }
33
52
 
34
- const cleanHTML = (html) => {
53
+ const getEffectiveBackgroundColor = (element) => {
54
+ let el = element;
55
+ while (el) {
56
+ const bgColor = window.getComputedStyle(el).backgroundColor;
57
+
58
+ if (bgColor !== 'rgba(0, 0, 0, 0)' &&
59
+ bgColor !== 'transparent' &&
60
+ bgColor !== '') {
61
+ return bgColor;
62
+ }
63
+
64
+ el = el !== document.body ? el.parentElement : null;
65
+ }
66
+ const bodyBg = window.getComputedStyle(document.body).backgroundColor;
67
+ return bodyBg
68
+ }
69
+
70
+ const cleanHTML = (html, options, core) => {
35
71
  // Create a temporary div to manipulate the HTML
36
72
  const tempDiv = document.createElement('div');
37
73
  tempDiv.innerHTML = html;
38
74
 
39
75
  // Traverse through all elements and clean attributes
40
- traverseAndClean(tempDiv);
76
+ traverseAndClean(tempDiv, options, core);
41
77
 
42
78
  // Return manipulated HTML
43
79
  return tempDiv.innerHTML;
@@ -142,6 +178,7 @@ const makeToolbarColor = (_plugin, options, core) => {
142
178
 
143
179
  const pluginIcon = document.createElement('div');
144
180
  pluginIcon.innerHTML = _plugin.icon;
181
+ pluginIcon.onclick = () => pluginButton.click()
145
182
  pluginContainer.append(pluginButton, pluginIcon)
146
183
  return pluginContainer
147
184
  }