mirror of
https://github.com/penpot/penpot.git
synced 2025-08-01 19:28:25 +02:00
🐛 Fix copy/paste not working on follow up pastes
This commit is contained in:
parent
098fd9fb0f
commit
af5b942e05
7 changed files with 121 additions and 88 deletions
|
@ -66,7 +66,7 @@
|
||||||
:font-family family
|
:font-family family
|
||||||
:font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
|
:font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
|
||||||
:font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
|
:font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
|
||||||
:font-weight (d/nilv (obj/get variant "fontWeight") (:wegith default-variant))}]
|
:font-weight (d/nilv (obj/get variant "fontWeight") (:weight default-variant))}]
|
||||||
(st/emit! (dwt/update-attrs id values)))))
|
(st/emit! (dwt/update-attrs id values)))))
|
||||||
|
|
||||||
:applyToRange
|
:applyToRange
|
||||||
|
|
|
@ -224,8 +224,8 @@
|
||||||
|
|
||||||
(defn add-emoji-font
|
(defn add-emoji-font
|
||||||
[fonts]
|
[fonts]
|
||||||
(conj fonts {:font-id "gfont-noto-color-emoji"
|
(conj fonts {:font-id " gfont-noto-color-emoji "
|
||||||
:font-variant-id "regular"
|
:font-variant-id " regular "
|
||||||
:style 0
|
:style 0
|
||||||
:weight 400
|
:weight 400
|
||||||
:is-emoji true}))
|
:is-emoji true}))
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
mapContentFragmentFromHTML,
|
mapContentFragmentFromHTML,
|
||||||
mapContentFragmentFromString,
|
mapContentFragmentFromString,
|
||||||
} from "./content/dom/Content.js";
|
} from "./content/dom/Content.js";
|
||||||
|
import { resetInertElement } from "./content/dom/Style.js";
|
||||||
import { createRoot, createEmptyRoot } from "./content/dom/Root.js";
|
import { createRoot, createEmptyRoot } from "./content/dom/Root.js";
|
||||||
import { createParagraph } from "./content/dom/Paragraph.js";
|
import { createParagraph } from "./content/dom/Paragraph.js";
|
||||||
import { createEmptyInline, createInline } from "./content/dom/Inline.js";
|
import { createEmptyInline, createInline } from "./content/dom/Inline.js";
|
||||||
|
@ -264,7 +265,7 @@ export class TextEditor extends EventTarget {
|
||||||
#onCopy = (e) => {
|
#onCopy = (e) => {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("clipboardchange", {
|
new CustomEvent("clipboardchange", {
|
||||||
detail: this.#selectionController.currentStyle,
|
detail: this.currentStyle,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -520,6 +521,7 @@ export function createRootFromHTML(html, style) {
|
||||||
const fragment = mapContentFragmentFromHTML(html, style);
|
const fragment = mapContentFragmentFromHTML(html, style);
|
||||||
const root = createRoot([], style);
|
const root = createRoot([], style);
|
||||||
root.replaceChildren(fragment);
|
root.replaceChildren(fragment);
|
||||||
|
resetInertElement();
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,8 @@ export function mapContentFragmentFromDocument(document, root, styleDefaults) {
|
||||||
if (!fontSize) console.warn("font-size", fontSize);
|
if (!fontSize) console.warn("font-size", fontSize);
|
||||||
const fontFamily = inline.style.getPropertyValue("font-family");
|
const fontFamily = inline.style.getPropertyValue("font-family");
|
||||||
if (!fontFamily) console.warn("font-family", fontFamily);
|
if (!fontFamily) console.warn("font-family", fontFamily);
|
||||||
|
const fontWeight = inline.style.getPropertyValue("font-weight");
|
||||||
|
if (!fontWeight) console.warn("font-weight", fontWeight);
|
||||||
currentParagraph.appendChild(inline);
|
currentParagraph.appendChild(inline);
|
||||||
|
|
||||||
currentNode = nodeIterator.nextNode();
|
currentNode = nodeIterator.nextNode();
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { getFills } from "./Color.js";
|
||||||
|
|
||||||
const DEFAULT_FONT_SIZE = "16px";
|
const DEFAULT_FONT_SIZE = "16px";
|
||||||
const DEFAULT_LINE_HEIGHT = "1.2";
|
const DEFAULT_LINE_HEIGHT = "1.2";
|
||||||
|
const DEFAULT_FONT_WEIGHT = "400";
|
||||||
/**
|
/**
|
||||||
* Merges two style declarations. `source` -> `target`.
|
* Merges two style declarations. `source` -> `target`.
|
||||||
*
|
*
|
||||||
|
@ -55,7 +55,7 @@ let inertElement = null;
|
||||||
* Resets the style declaration of the inert
|
* Resets the style declaration of the inert
|
||||||
* element.
|
* element.
|
||||||
*/
|
*/
|
||||||
function resetInertElement() {
|
export function resetInertElement() {
|
||||||
if (!inertElement) throw new Error("Invalid inert element");
|
if (!inertElement) throw new Error("Invalid inert element");
|
||||||
resetStyleDeclaration(inertElement.style);
|
resetStyleDeclaration(inertElement.style);
|
||||||
return inertElement;
|
return inertElement;
|
||||||
|
@ -94,11 +94,21 @@ function getStyleDefaultsDeclaration() {
|
||||||
* @returns {CSSStyleDeclaration}
|
* @returns {CSSStyleDeclaration}
|
||||||
*/
|
*/
|
||||||
export function getComputedStyle(element) {
|
export function getComputedStyle(element) {
|
||||||
|
if (typeof window !== "undefined" && window.getComputedStyle) {
|
||||||
|
const inertElement = getInertElement();
|
||||||
|
const computedStyle = window.getComputedStyle(element);
|
||||||
|
inertElement.style = computedStyle;
|
||||||
|
|
||||||
|
return inertElement.style;
|
||||||
|
}
|
||||||
|
return getComputedStylePolyfill(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getComputedStylePolyfill(element) {
|
||||||
const inertElement = getInertElement();
|
const inertElement = getInertElement();
|
||||||
|
|
||||||
let currentElement = element;
|
let currentElement = element;
|
||||||
while (currentElement) {
|
while (currentElement) {
|
||||||
// This is better but it doesn't work in JSDOM.
|
|
||||||
// for (const styleName of currentElement.style) {
|
|
||||||
for (let index = 0; index < currentElement.style.length; index++) {
|
for (let index = 0; index < currentElement.style.length; index++) {
|
||||||
const styleName = currentElement.style.item(index);
|
const styleName = currentElement.style.item(index);
|
||||||
const currentValue = inertElement.style.getPropertyValue(styleName);
|
const currentValue = inertElement.style.getPropertyValue(styleName);
|
||||||
|
@ -159,6 +169,11 @@ export function normalizeStyles(
|
||||||
styleDeclaration.setProperty("font-size", DEFAULT_FONT_SIZE);
|
styleDeclaration.setProperty("font-size", DEFAULT_FONT_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fontWeight = styleDeclaration.getPropertyValue("font-weight");
|
||||||
|
if (!fontWeight || fontWeight === "0") {
|
||||||
|
styleDeclaration.setProperty("font-weight", DEFAULT_FONT_WEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
const lineHeight = styleDeclaration.getPropertyValue("line-height");
|
const lineHeight = styleDeclaration.getPropertyValue("line-height");
|
||||||
if (!lineHeight || lineHeight === "" || !lineHeight.endsWith("px")) {
|
if (!lineHeight || lineHeight === "" || !lineHeight.endsWith("px")) {
|
||||||
// TODO: Podríamos convertir unidades en decimales.
|
// TODO: Podríamos convertir unidades en decimales.
|
||||||
|
|
|
@ -39,7 +39,11 @@ import {
|
||||||
insertInto,
|
insertInto,
|
||||||
removeSlice,
|
removeSlice,
|
||||||
} from "../content/Text.js";
|
} from "../content/Text.js";
|
||||||
import { getTextNodeLength, getClosestTextNode, isTextNode } from "../content/dom/TextNode.js";
|
import {
|
||||||
|
getTextNodeLength,
|
||||||
|
getClosestTextNode,
|
||||||
|
isTextNode,
|
||||||
|
} from "../content/dom/TextNode.js";
|
||||||
import TextNodeIterator from "../content/dom/TextNodeIterator.js";
|
import TextNodeIterator from "../content/dom/TextNodeIterator.js";
|
||||||
import TextEditor from "../TextEditor.js";
|
import TextEditor from "../TextEditor.js";
|
||||||
import CommandMutations from "../commands/CommandMutations.js";
|
import CommandMutations from "../commands/CommandMutations.js";
|
||||||
|
@ -240,7 +244,7 @@ export class SelectionController extends EventTarget {
|
||||||
for (const [name, value] of Object.entries(this.#styleDefaults)) {
|
for (const [name, value] of Object.entries(this.#styleDefaults)) {
|
||||||
this.#currentStyle.setProperty(
|
this.#currentStyle.setProperty(
|
||||||
name,
|
name,
|
||||||
value + (name === "font-size" ? "px" : "")
|
value + (name === "font-size" ? "px" : ""),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,10 +360,11 @@ export class SelectionController extends EventTarget {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("stylechange", {
|
new CustomEvent("stylechange", {
|
||||||
detail: this.#currentStyle,
|
detail: this.#currentStyle,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const firstInline = this.#textEditor.root?.firstElementChild?.firstElementChild;
|
const firstInline =
|
||||||
|
this.#textEditor.root?.firstElementChild?.firstElementChild;
|
||||||
if (firstInline) {
|
if (firstInline) {
|
||||||
this.#updateCurrentStyle(firstInline);
|
this.#updateCurrentStyle(firstInline);
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
|
@ -452,13 +457,16 @@ export class SelectionController extends EventTarget {
|
||||||
|
|
||||||
if (this.#savedSelection.anchorNode && this.#savedSelection.focusNode) {
|
if (this.#savedSelection.anchorNode && this.#savedSelection.focusNode) {
|
||||||
if (this.#savedSelection.anchorNode === this.#savedSelection.focusNode) {
|
if (this.#savedSelection.anchorNode === this.#savedSelection.focusNode) {
|
||||||
this.#selection.setPosition(this.#savedSelection.focusNode, this.#savedSelection.focusOffset);
|
this.#selection.setPosition(
|
||||||
|
this.#savedSelection.focusNode,
|
||||||
|
this.#savedSelection.focusOffset,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.#selection.setBaseAndExtent(
|
this.#selection.setBaseAndExtent(
|
||||||
this.#savedSelection.anchorNode,
|
this.#savedSelection.anchorNode,
|
||||||
this.#savedSelection.anchorOffset,
|
this.#savedSelection.anchorOffset,
|
||||||
this.#savedSelection.focusNode,
|
this.#savedSelection.focusNode,
|
||||||
this.#savedSelection.focusOffset
|
this.#savedSelection.focusOffset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +499,7 @@ export class SelectionController extends EventTarget {
|
||||||
*/
|
*/
|
||||||
selectAll() {
|
selectAll() {
|
||||||
if (this.#textEditor.isEmpty) {
|
if (this.#textEditor.isEmpty) {
|
||||||
return this
|
return this;
|
||||||
}
|
}
|
||||||
this.#selection.selectAllChildren(this.#textEditor.root);
|
this.#selection.selectAllChildren(this.#textEditor.root);
|
||||||
return this;
|
return this;
|
||||||
|
@ -516,16 +524,12 @@ export class SelectionController extends EventTarget {
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
*/
|
*/
|
||||||
collapse(node, offset) {
|
collapse(node, offset) {
|
||||||
const nodeOffset = (node.nodeType === Node.TEXT_NODE && offset >= node.nodeValue.length)
|
const nodeOffset =
|
||||||
? node.nodeValue.length
|
node.nodeType === Node.TEXT_NODE && offset >= node.nodeValue.length
|
||||||
: offset
|
? node.nodeValue.length
|
||||||
|
: offset;
|
||||||
|
|
||||||
return this.setSelection(
|
return this.setSelection(node, nodeOffset, node, nodeOffset);
|
||||||
node,
|
|
||||||
nodeOffset,
|
|
||||||
node,
|
|
||||||
nodeOffset
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -536,12 +540,17 @@ export class SelectionController extends EventTarget {
|
||||||
* @param {Node} [focusNode=anchorNode]
|
* @param {Node} [focusNode=anchorNode]
|
||||||
* @param {number} [focusOffset=anchorOffset]
|
* @param {number} [focusOffset=anchorOffset]
|
||||||
*/
|
*/
|
||||||
setSelection(anchorNode, anchorOffset, focusNode = anchorNode, focusOffset = anchorOffset) {
|
setSelection(
|
||||||
|
anchorNode,
|
||||||
|
anchorOffset,
|
||||||
|
focusNode = anchorNode,
|
||||||
|
focusOffset = anchorOffset,
|
||||||
|
) {
|
||||||
if (!anchorNode.isConnected) {
|
if (!anchorNode.isConnected) {
|
||||||
throw new Error('Invalid anchorNode')
|
throw new Error("Invalid anchorNode");
|
||||||
}
|
}
|
||||||
if (!focusNode.isConnected) {
|
if (!focusNode.isConnected) {
|
||||||
throw new Error('Invalid focusNode')
|
throw new Error("Invalid focusNode");
|
||||||
}
|
}
|
||||||
if (this.#savedSelection) {
|
if (this.#savedSelection) {
|
||||||
this.#savedSelection.isCollapsed =
|
this.#savedSelection.isCollapsed =
|
||||||
|
@ -578,7 +587,7 @@ export class SelectionController extends EventTarget {
|
||||||
anchorNode,
|
anchorNode,
|
||||||
anchorOffset,
|
anchorOffset,
|
||||||
focusNode,
|
focusNode,
|
||||||
focusOffset
|
focusOffset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,8 +720,7 @@ export class SelectionController extends EventTarget {
|
||||||
if (this.#savedSelection) {
|
if (this.#savedSelection) {
|
||||||
return this.#savedSelection.focusNode;
|
return this.#savedSelection.focusNode;
|
||||||
}
|
}
|
||||||
if (!this.#focusNode)
|
if (!this.#focusNode) console.trace("focusNode", this.#focusNode);
|
||||||
console.trace("focusNode", this.#focusNode);
|
|
||||||
return this.#focusNode;
|
return this.#focusNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,7 +971,7 @@ export class SelectionController extends EventTarget {
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
get isRootFocus() {
|
get isRootFocus() {
|
||||||
return isRoot(this.focusNode)
|
return isRoot(this.focusNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1044,27 +1052,25 @@ export class SelectionController extends EventTarget {
|
||||||
* @param {DocumentFragment} fragment
|
* @param {DocumentFragment} fragment
|
||||||
*/
|
*/
|
||||||
insertPaste(fragment) {
|
insertPaste(fragment) {
|
||||||
if (fragment.children.length === 1
|
if (
|
||||||
&& fragment.firstElementChild?.dataset?.inline === "force"
|
fragment.children.length === 1 &&
|
||||||
|
fragment.firstElementChild?.dataset?.inline === "force"
|
||||||
) {
|
) {
|
||||||
const collapseNode = fragment.lastElementChild.firstChild
|
const collapseNode = fragment.lastElementChild.firstChild;
|
||||||
if (this.isInlineStart) {
|
if (this.isInlineStart) {
|
||||||
this.focusInline.before(...fragment.firstElementChild.children)
|
this.focusInline.before(...fragment.firstElementChild.children);
|
||||||
} else if (this.isInlineEnd) {
|
} else if (this.isInlineEnd) {
|
||||||
this.focusInline.after(...fragment.firstElementChild.children);
|
this.focusInline.after(...fragment.firstElementChild.children);
|
||||||
} else {
|
} else {
|
||||||
const newInline = splitInline(
|
const newInline = splitInline(this.focusInline, this.focusOffset);
|
||||||
this.focusInline,
|
this.focusInline.after(
|
||||||
this.focusOffset
|
...fragment.firstElementChild.children,
|
||||||
)
|
newInline,
|
||||||
this.focusInline.after(...fragment.firstElementChild.children, newInline)
|
);
|
||||||
}
|
}
|
||||||
return this.collapse(
|
return this.collapse(collapseNode, collapseNode.nodeValue.length);
|
||||||
collapseNode,
|
|
||||||
collapseNode.nodeValue.length
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const collapseNode = fragment.lastElementChild.lastElementChild.firstChild
|
const collapseNode = fragment.lastElementChild.lastElementChild.firstChild;
|
||||||
if (this.isParagraphStart) {
|
if (this.isParagraphStart) {
|
||||||
const a = fragment.lastElementChild;
|
const a = fragment.lastElementChild;
|
||||||
const b = this.focusParagraph;
|
const b = this.focusParagraph;
|
||||||
|
@ -1079,7 +1085,7 @@ export class SelectionController extends EventTarget {
|
||||||
const newParagraph = splitParagraph(
|
const newParagraph = splitParagraph(
|
||||||
this.focusParagraph,
|
this.focusParagraph,
|
||||||
this.focusInline,
|
this.focusInline,
|
||||||
this.focusOffset
|
this.focusOffset,
|
||||||
);
|
);
|
||||||
this.focusParagraph.after(fragment, newParagraph);
|
this.focusParagraph.after(fragment, newParagraph);
|
||||||
}
|
}
|
||||||
|
@ -1115,7 +1121,7 @@ export class SelectionController extends EventTarget {
|
||||||
|
|
||||||
const removedData = removeForward(
|
const removedData = removeForward(
|
||||||
this.focusNode.nodeValue,
|
this.focusNode.nodeValue,
|
||||||
this.focusOffset
|
this.focusOffset,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.focusNode.nodeValue !== removedData) {
|
if (this.focusNode.nodeValue !== removedData) {
|
||||||
|
@ -1155,7 +1161,7 @@ export class SelectionController extends EventTarget {
|
||||||
// Remove the character from the string.
|
// Remove the character from the string.
|
||||||
const removedData = removeBackward(
|
const removedData = removeBackward(
|
||||||
this.focusNode.nodeValue,
|
this.focusNode.nodeValue,
|
||||||
this.focusOffset
|
this.focusOffset,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.focusNode.nodeValue !== removedData) {
|
if (this.focusNode.nodeValue !== removedData) {
|
||||||
|
@ -1187,7 +1193,10 @@ export class SelectionController extends EventTarget {
|
||||||
inline.childNodes.length === 0
|
inline.childNodes.length === 0
|
||||||
) {
|
) {
|
||||||
inline.remove();
|
inline.remove();
|
||||||
return this.collapse(previousTextNode, getTextNodeLength(previousTextNode));
|
return this.collapse(
|
||||||
|
previousTextNode,
|
||||||
|
getTextNodeLength(previousTextNode),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.collapse(this.focusNode, this.focusOffset - 1);
|
return this.collapse(this.focusNode, this.focusOffset - 1);
|
||||||
|
@ -1202,7 +1211,7 @@ export class SelectionController extends EventTarget {
|
||||||
this.focusNode.nodeValue = insertInto(
|
this.focusNode.nodeValue = insertInto(
|
||||||
this.focusNode.nodeValue,
|
this.focusNode.nodeValue,
|
||||||
this.focusOffset,
|
this.focusOffset,
|
||||||
newText
|
newText,
|
||||||
);
|
);
|
||||||
this.#mutations.update(this.focusInline);
|
this.#mutations.update(this.focusInline);
|
||||||
return this.collapse(this.focusNode, this.focusOffset + newText.length);
|
return this.collapse(this.focusNode, this.focusOffset + newText.length);
|
||||||
|
@ -1219,14 +1228,14 @@ export class SelectionController extends EventTarget {
|
||||||
this.focusNode.nodeValue = insertInto(
|
this.focusNode.nodeValue = insertInto(
|
||||||
this.focusNode.nodeValue,
|
this.focusNode.nodeValue,
|
||||||
this.focusOffset,
|
this.focusOffset,
|
||||||
newText
|
newText,
|
||||||
);
|
);
|
||||||
} else if (this.isLineBreakFocus) {
|
} else if (this.isLineBreakFocus) {
|
||||||
const textNode = new Text(newText);
|
const textNode = new Text(newText);
|
||||||
this.focusNode.replaceWith(textNode);
|
this.focusNode.replaceWith(textNode);
|
||||||
this.collapse(textNode, newText.length);
|
this.collapse(textNode, newText.length);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown node type');
|
throw new Error("Unknown node type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1243,22 +1252,18 @@ export class SelectionController extends EventTarget {
|
||||||
this.focusNode.nodeValue,
|
this.focusNode.nodeValue,
|
||||||
startOffset,
|
startOffset,
|
||||||
endOffset,
|
endOffset,
|
||||||
newText
|
newText,
|
||||||
);
|
);
|
||||||
} else if (this.isLineBreakFocus) {
|
} else if (this.isLineBreakFocus) {
|
||||||
this.focusNode.replaceWith(new Text(newText));
|
this.focusNode.replaceWith(new Text(newText));
|
||||||
} else if (this.isRootFocus) {
|
} else if (this.isRootFocus) {
|
||||||
const newTextNode = new Text(newText);
|
const newTextNode = new Text(newText);
|
||||||
const newInline = createInline(newTextNode, this.#currentStyle);
|
const newInline = createInline(newTextNode, this.#currentStyle);
|
||||||
const newParagraph = createParagraph([
|
const newParagraph = createParagraph([newInline], this.#currentStyle);
|
||||||
newInline
|
this.focusNode.replaceChildren(newParagraph);
|
||||||
], this.#currentStyle)
|
|
||||||
this.focusNode.replaceChildren(
|
|
||||||
newParagraph
|
|
||||||
);
|
|
||||||
return this.collapse(newTextNode, newText.length + 1);
|
return this.collapse(newTextNode, newText.length + 1);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown node type');
|
throw new Error("Unknown node type");
|
||||||
}
|
}
|
||||||
this.#mutations.update(this.focusInline);
|
this.#mutations.update(this.focusInline);
|
||||||
return this.collapse(this.focusNode, startOffset + newText.length);
|
return this.collapse(this.focusNode, startOffset + newText.length);
|
||||||
|
@ -1282,7 +1287,7 @@ export class SelectionController extends EventTarget {
|
||||||
) {
|
) {
|
||||||
const newTextNode = new Text(newText);
|
const newTextNode = new Text(newText);
|
||||||
currentParagraph.replaceChildren(
|
currentParagraph.replaceChildren(
|
||||||
createInline(newTextNode, this.anchorInline.style)
|
createInline(newTextNode, this.anchorInline.style),
|
||||||
);
|
);
|
||||||
return this.collapse(newTextNode, newTextNode.nodeValue.length);
|
return this.collapse(newTextNode, newTextNode.nodeValue.length);
|
||||||
}
|
}
|
||||||
|
@ -1363,7 +1368,7 @@ export class SelectionController extends EventTarget {
|
||||||
const newParagraph = splitParagraph(
|
const newParagraph = splitParagraph(
|
||||||
this.focusParagraph,
|
this.focusParagraph,
|
||||||
this.focusInline,
|
this.focusInline,
|
||||||
this.#focusOffset
|
this.#focusOffset,
|
||||||
);
|
);
|
||||||
this.focusParagraph.after(newParagraph);
|
this.focusParagraph.after(newParagraph);
|
||||||
this.#mutations.update(currentParagraph);
|
this.#mutations.update(currentParagraph);
|
||||||
|
@ -1396,7 +1401,7 @@ export class SelectionController extends EventTarget {
|
||||||
const newParagraph = splitParagraph(
|
const newParagraph = splitParagraph(
|
||||||
currentParagraph,
|
currentParagraph,
|
||||||
currentInline,
|
currentInline,
|
||||||
this.focusOffset
|
this.focusOffset,
|
||||||
);
|
);
|
||||||
currentParagraph.after(newParagraph);
|
currentParagraph.after(newParagraph);
|
||||||
|
|
||||||
|
@ -1542,7 +1547,7 @@ export class SelectionController extends EventTarget {
|
||||||
const newNodeValue = removeSlice(
|
const newNodeValue = removeSlice(
|
||||||
startNode.nodeValue,
|
startNode.nodeValue,
|
||||||
startOffset,
|
startOffset,
|
||||||
endOffset
|
endOffset,
|
||||||
);
|
);
|
||||||
if (newNodeValue === "") {
|
if (newNodeValue === "") {
|
||||||
const lineBreak = createLineBreak();
|
const lineBreak = createLineBreak();
|
||||||
|
@ -1588,9 +1593,10 @@ export class SelectionController extends EventTarget {
|
||||||
currentNode.nodeValue = currentNode.nodeValue.slice(0, startOffset);
|
currentNode.nodeValue = currentNode.nodeValue.slice(0, startOffset);
|
||||||
}
|
}
|
||||||
} else if (currentNode === endNode) {
|
} else if (currentNode === endNode) {
|
||||||
if (isLineBreak(endNode)
|
if (
|
||||||
|| (isTextNode(endNode)
|
isLineBreak(endNode) ||
|
||||||
&& endOffset === endNode.nodeValue.length)) {
|
(isTextNode(endNode) && endOffset === endNode.nodeValue.length)
|
||||||
|
) {
|
||||||
// We should remove this node completely.
|
// We should remove this node completely.
|
||||||
shouldRemoveNodeCompletely = true;
|
shouldRemoveNodeCompletely = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1623,7 +1629,6 @@ export class SelectionController extends EventTarget {
|
||||||
if (currentNode === endNode) {
|
if (currentNode === endNode) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (this.#textNodeIterator.currentNode);
|
} while (this.#textNodeIterator.currentNode);
|
||||||
|
|
||||||
if (startParagraph !== endParagraph) {
|
if (startParagraph !== endParagraph) {
|
||||||
|
@ -1635,22 +1640,31 @@ export class SelectionController extends EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startInline.childNodes.length === 0
|
if (
|
||||||
&& endInline.childNodes.length > 0) {
|
startInline.childNodes.length === 0 &&
|
||||||
|
endInline.childNodes.length > 0
|
||||||
|
) {
|
||||||
startInline.remove();
|
startInline.remove();
|
||||||
return this.collapse(endNode, 0);
|
return this.collapse(endNode, 0);
|
||||||
} else if (startInline.childNodes.length > 0
|
} else if (
|
||||||
&& endInline.childNodes.length === 0) {
|
startInline.childNodes.length > 0 &&
|
||||||
|
endInline.childNodes.length === 0
|
||||||
|
) {
|
||||||
endInline.remove();
|
endInline.remove();
|
||||||
return this.collapse(startNode, startOffset);
|
return this.collapse(startNode, startOffset);
|
||||||
} else if (startInline.childNodes.length === 0
|
} else if (
|
||||||
&& endInline.childNodes.length === 0) {
|
startInline.childNodes.length === 0 &&
|
||||||
|
endInline.childNodes.length === 0
|
||||||
|
) {
|
||||||
const previousInline = startInline.previousElementSibling;
|
const previousInline = startInline.previousElementSibling;
|
||||||
const nextInline = endInline.nextElementSibling;
|
const nextInline = endInline.nextElementSibling;
|
||||||
startInline.remove();
|
startInline.remove();
|
||||||
endInline.remove();
|
endInline.remove();
|
||||||
if (previousInline) {
|
if (previousInline) {
|
||||||
return this.collapse(previousInline.firstChild, previousInline.firstChild.nodeValue.length);
|
return this.collapse(
|
||||||
|
previousInline.firstChild,
|
||||||
|
previousInline.firstChild.nodeValue.length,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (nextInline) {
|
if (nextInline) {
|
||||||
return this.collapse(nextInline.firstChild, 0);
|
return this.collapse(nextInline.firstChild, 0);
|
||||||
|
@ -1790,7 +1804,7 @@ export class SelectionController extends EventTarget {
|
||||||
this.startOffset,
|
this.startOffset,
|
||||||
this.endContainer,
|
this.endContainer,
|
||||||
this.endOffset,
|
this.endOffset,
|
||||||
newStyles
|
newStyles,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ const debug = searchParams.has("debug")
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const textEditorSelectionImposterElement = document.getElementById(
|
const textEditorSelectionImposterElement = document.getElementById(
|
||||||
"text-editor-selection-imposter"
|
"text-editor-selection-imposter",
|
||||||
);
|
);
|
||||||
|
|
||||||
const textEditorElement = document.querySelector(".text-editor-content");
|
const textEditorElement = document.querySelector(".text-editor-content");
|
||||||
|
@ -25,18 +25,18 @@ const textEditor = new TextEditor(textEditorElement, {
|
||||||
styleDefaults: {
|
styleDefaults: {
|
||||||
"font-family": "sourcesanspro",
|
"font-family": "sourcesanspro",
|
||||||
"font-size": "14",
|
"font-size": "14",
|
||||||
"font-weight": "400",
|
"font-weight": "500",
|
||||||
"font-style": "normal",
|
"font-style": "normal",
|
||||||
"line-height": "1.2",
|
"line-height": "1.2",
|
||||||
"letter-spacing": "0",
|
"letter-spacing": "0",
|
||||||
"direction": "ltr",
|
direction: "ltr",
|
||||||
"text-align": "left",
|
"text-align": "left",
|
||||||
"text-transform": "none",
|
"text-transform": "none",
|
||||||
"text-decoration": "none",
|
"text-decoration": "none",
|
||||||
"--typography-ref-id": '["~#\'",null]',
|
"--typography-ref-id": '["~#\'",null]',
|
||||||
"--typography-ref-file": '["~#\'",null]',
|
"--typography-ref-file": '["~#\'",null]',
|
||||||
"--font-id": '["~#\'","sourcesanspro"]',
|
"--font-id": '["~#\'","sourcesanspro"]',
|
||||||
"--fills": '[["^ ","~:fill-color","#000000","~:fill-opacity",1]]'
|
"--fills": '[["^ ","~:fill-color","#000000","~:fill-opacity",1]]',
|
||||||
},
|
},
|
||||||
selectionImposterElement: textEditorSelectionImposterElement,
|
selectionImposterElement: textEditorSelectionImposterElement,
|
||||||
debug: new SelectionControllerDebug({
|
debug: new SelectionControllerDebug({
|
||||||
|
@ -87,7 +87,7 @@ function onDirectionChange(e) {
|
||||||
}
|
}
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
textEditor.applyStylesToSelection({
|
textEditor.applyStylesToSelection({
|
||||||
"direction": e.target.value
|
direction: e.target.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ function onTextAlignChange(e) {
|
||||||
}
|
}
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
textEditor.applyStylesToSelection({
|
textEditor.applyStylesToSelection({
|
||||||
"text-align": e.target.value
|
"text-align": e.target.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,18 +143,18 @@ lineHeightElement.addEventListener("change", (e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
textEditor.applyStylesToSelection({
|
textEditor.applyStylesToSelection({
|
||||||
"line-height": e.target.value
|
"line-height": e.target.value,
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
letterSpacingElement.addEventListener("change", (e) => {
|
letterSpacingElement.addEventListener("change", (e) => {
|
||||||
if (debug.includes("events")) {
|
if (debug.includes("events")) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
textEditor.applyStylesToSelection({
|
textEditor.applyStylesToSelection({
|
||||||
"letter-spacing": e.target.value
|
"letter-spacing": e.target.value,
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
fontStyleElement.addEventListener("change", (e) => {
|
fontStyleElement.addEventListener("change", (e) => {
|
||||||
if (debug.includes("events")) {
|
if (debug.includes("events")) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue