@lesjoursfr/edith 2.0.1 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesjoursfr/edith",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Simple WYSIWYG editor.",
5
5
  "license": "MIT",
6
6
  "repository": "lesjoursfr/edith",
@@ -32,7 +32,7 @@
32
32
  "lint-js": "eslint . --fix --ext .js",
33
33
  "lint-sass": "stylelint **/*.scss --fix",
34
34
  "format": "prettier --write .",
35
- "test": "npx ava",
35
+ "test": "npx mocha",
36
36
  "builddeps": "webpack --mode production --config server/deps/codemirror.webpack.js --progress",
37
37
  "server": "webpack serve --mode development --config server/editor.webpack.js --hot --open"
38
38
  },
@@ -44,35 +44,35 @@
44
44
  "editor"
45
45
  ],
46
46
  "devDependencies": {
47
- "@babel/core": "^7.22.5",
48
- "@babel/preset-env": "^7.22.5",
49
- "@codemirror/lang-html": "^6.4.5",
50
- "@fortawesome/fontawesome-free": "^6.4.0",
47
+ "@babel/core": "^7.22.11",
48
+ "@babel/preset-env": "^7.22.14",
49
+ "@codemirror/lang-html": "^6.4.6",
50
+ "@fortawesome/fontawesome-free": "^6.4.2",
51
51
  "@popperjs/core": "^2.11.8",
52
- "ava": "^5.3.1",
53
- "babel-loader": "^9.1.2",
52
+ "babel-loader": "^9.1.3",
54
53
  "codemirror": "^6.0.1",
55
54
  "css-loader": "^6.8.1",
56
- "eslint": "^8.44.0",
57
- "eslint-config-prettier": "^8.8.0",
55
+ "eslint": "^8.48.0",
56
+ "eslint-config-prettier": "^9.0.0",
58
57
  "jsdom": "^22.1.0",
59
58
  "mini-css-extract-plugin": "^2.7.6",
60
- "postcss": "^8.4.24",
61
- "prettier": "^2.8.8",
62
- "sass": "^1.63.6",
59
+ "mocha": "^10.2.0",
60
+ "postcss": "^8.4.29",
61
+ "prettier": "^3.0.3",
62
+ "sass": "^1.66.1",
63
63
  "sass-loader": "^13.3.2",
64
64
  "style-loader": "^3.3.3",
65
- "stylelint": "^15.9.0",
65
+ "stylelint": "^15.10.3",
66
66
  "stylelint-config-sass-guidelines": "^10.0.0",
67
- "webpack": "^5.88.1",
67
+ "webpack": "^5.88.2",
68
68
  "webpack-cli": "^5.1.4",
69
69
  "webpack-dev-server": "^4.15.1"
70
70
  },
71
71
  "peerDependencies": {
72
- "@codemirror/lang-html": "^6.4.5",
73
- "@fortawesome/fontawesome-free": "^6.4.0",
72
+ "@codemirror/lang-html": "^6.4.6",
73
+ "@fortawesome/fontawesome-free": "^6.4.2",
74
74
  "@popperjs/core": "^2.11.8",
75
75
  "codemirror": "^6.0.1"
76
76
  },
77
- "packageManager": "yarn@3.6.1"
77
+ "packageManager": "yarn@3.6.3"
78
78
  }
package/src/core/dom.js CHANGED
@@ -76,7 +76,7 @@ export function replaceNodeWith(node, replacement) {
76
76
  * @returns {Array} its child nodes
77
77
  */
78
78
  export function unwrapNode(node) {
79
- const newNodes = node.childNodes;
79
+ const newNodes = [...node.childNodes];
80
80
  node.replaceWith(...newNodes);
81
81
  return newNodes;
82
82
  }
package/src/core/edit.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getSelection, moveCursorInsideNode, moveCursorAfterNode, selectNodeContents } from "./range.js";
1
+ import { getSelection, moveCursorInsideNode, moveCursorAfterNode, selectNodeContents, selectNodes } from "./range.js";
2
2
  import {
3
3
  hasClass,
4
4
  hasTagName,
@@ -43,6 +43,48 @@ function splitNodeAtCaret(range, node) {
43
43
  return textNode;
44
44
  }
45
45
 
46
+ /**
47
+ * Extract the selection from the node.
48
+ * @param {Range} range the selection to extract
49
+ * @param {Node} node the node to split
50
+ * @param {String} tag the tag to remove
51
+ * @returns {Node} the created node
52
+ */
53
+ function extractSelectionFromNode(range, node) {
54
+ // Get the node's parent
55
+ const parent = node.parentNode;
56
+
57
+ // Clone the current range & move the starting point to the beginning of the parent's node
58
+ const beforeSelection = new Range();
59
+ beforeSelection.selectNodeContents(parent);
60
+ beforeSelection.setEnd(range.startContainer, range.startOffset);
61
+ const afterSelection = new Range();
62
+ afterSelection.selectNodeContents(parent);
63
+ afterSelection.setStart(range.endContainer, range.endOffset);
64
+
65
+ // Extract the content of the selection
66
+ const fragBefore = beforeSelection.extractContents();
67
+ const fragAfter = afterSelection.extractContents();
68
+
69
+ // Add back the content into the node's parent
70
+ parent.prepend(fragBefore);
71
+ parent.append(fragAfter);
72
+
73
+ // Remove the parent from the selection
74
+ let current = range.commonAncestorContainer;
75
+ while (current.tagName !== node.tagName) {
76
+ // Take the parent
77
+ current = current.parentNode;
78
+ }
79
+ let innerNodes = unwrapNode(current);
80
+
81
+ // Preserve the selection
82
+ selectNodes(innerNodes);
83
+
84
+ // Return the inserted TextNode
85
+ return range.commonAncestorContainer;
86
+ }
87
+
46
88
  /**
47
89
  * Create a node at the caret position.
48
90
  * @param {Range} range the caret position
@@ -116,9 +158,11 @@ export function wrapInsideTag(tag, options = {}) {
116
158
  const { sel, range } = getSelection();
117
159
 
118
160
  // Check if the user has selected something
119
- if (range === undefined) return false;
161
+ if (range === undefined) {
162
+ return;
163
+ }
120
164
 
121
- // Check if the range is collapsed
165
+ // Check if there is a Selection
122
166
  if (range.collapsed) {
123
167
  // Check if a parent element has the same tag name
124
168
  let parent = sel.anchorNode.parentNode;
@@ -142,8 +186,9 @@ export function wrapInsideTag(tag, options = {}) {
142
186
  let parent = range.commonAncestorContainer;
143
187
  while (!hasClass(parent, "edith-visual")) {
144
188
  if (hasTagName(parent, tag)) {
145
- // One of the parent has the same tag name : unwrap it
146
- return unwrapNode(parent);
189
+ // One of the parent has the same tag name
190
+ // Extract the selection from the parent
191
+ return extractSelectionFromNode(range, parent);
147
192
  }
148
193
 
149
194
  // Take the parent
@@ -151,18 +196,18 @@ export function wrapInsideTag(tag, options = {}) {
151
196
  }
152
197
 
153
198
  // Try to replace all elements with the same tag name in the selection
154
- let replaced = false;
155
199
  for (const el of [...parent.getElementsByTagName(tag)]) {
156
200
  // Check if the the Element Intersect the Selection
157
201
  if (sel.containsNode(el, true)) {
158
- unwrapNode(el);
159
- replaced = true;
202
+ // Unwrap the node
203
+ let innerNodes = unwrapNode(el);
204
+
205
+ // Return the node
206
+ selectNodes(innerNodes);
207
+ parent.normalize();
208
+ return parent;
160
209
  }
161
210
  }
162
- if (replaced) {
163
- parent.normalize();
164
- return parent;
165
- }
166
211
 
167
212
  // Nothing was replaced
168
213
  // Wrap the selection inside the given tag
package/src/core/range.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { isSelfClosing } from "./dom.js";
2
+
1
3
  /**
2
4
  * @typedef {Object} CurrentSelection
3
5
  * @property {Selection} sel the current selection
@@ -63,6 +65,25 @@ export function selectNodeContents(target) {
63
65
  sel.addRange(range);
64
66
  }
65
67
 
68
+ /**
69
+ * Select the given Nodes.
70
+ * @param {Array<Node>} nodes The list of Nodes to select.
71
+ */
72
+ export function selectNodes(nodes) {
73
+ // Check if we just have a self-closing tag
74
+ if (nodes.length === 1 && isSelfClosing(nodes[0].tagName)) {
75
+ moveCursorAfterNode(nodes[0]); // Move the cursor after the Node
76
+ return;
77
+ }
78
+ // Select Nodes
79
+ const range = document.createRange();
80
+ const sel = window.getSelection();
81
+ range.setStartBefore(nodes[0]);
82
+ range.setEndAfter(nodes[nodes.length - 1]);
83
+ sel.removeAllRanges();
84
+ sel.addRange(range);
85
+ }
86
+
66
87
  /**
67
88
  * Check if the current selection is inside the given node.
68
89
  * @param {Node} node the targeted node
package/src/ui/editor.js CHANGED
@@ -50,6 +50,10 @@ EdithEditor.prototype.render = function () {
50
50
  this.editors.visual = document.createElement("div");
51
51
  this.editors.visual.setAttribute("class", "edith-visual");
52
52
  this.editors.visual.setAttribute("contenteditable", "true");
53
+ this.editors.visual.setAttribute(
54
+ "style",
55
+ this.resizable ? `min-height: ${this.height - 10}px` : `height: ${this.height - 10}px`
56
+ );
53
57
  this.editors.visual.innerHTML = this.content;
54
58
  this.editors.wrapper.append(this.editors.visual);
55
59
 
@@ -334,7 +338,7 @@ EdithEditor.prototype.onPasteEvent = function (e) {
334
338
 
335
339
  // Add the content as text nodes with a <br> node between each line
336
340
  for (let i = 0; i < lines.length; i++) {
337
- if (frag.length !== 0) {
341
+ if (i !== 0) {
338
342
  frag.append(document.createElement("br"));
339
343
  }
340
344
  frag.append(document.createTextNode(lines[i]));