Fix notepad cursor and calculator linked formulas
All checks were successful
Deploy Sokko G / deploy (push) Successful in 16s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 16s
This commit is contained in:
parent
992665f4ea
commit
49ba0c4cb2
10 changed files with 223 additions and 45 deletions
|
|
@ -1,5 +1,5 @@
|
|||
// Rôle : fournit l'outil bloc-notes riche avec annotations dessinées.
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { CompactDropdown } from "../../../components/CompactDropdown.jsx";
|
||||
import { DrawingControls, FormattingColorDropdown } from "./DrawingControls.jsx";
|
||||
import { DrawingOverlay } from "./DrawingOverlay.jsx";
|
||||
|
|
@ -13,6 +13,43 @@ function getPlainText(element) {
|
|||
return (element?.innerText || "").replace(/\n{3,}/g, "\n\n").trim();
|
||||
}
|
||||
|
||||
function getSelectionTextOffset(container) {
|
||||
const selection = document.getSelection();
|
||||
if (!container || !selection?.rangeCount || !container.contains(selection.anchorNode)) return null;
|
||||
const range = selection.getRangeAt(0);
|
||||
const prefix = range.cloneRange();
|
||||
prefix.selectNodeContents(container);
|
||||
prefix.setEnd(range.startContainer, range.startOffset);
|
||||
return prefix.toString().length;
|
||||
}
|
||||
|
||||
function restoreSelectionTextOffset(container, offset) {
|
||||
if (!container || offset == null) return;
|
||||
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);
|
||||
let remaining = offset;
|
||||
let node = walker.nextNode();
|
||||
while (node) {
|
||||
const length = node.textContent.length;
|
||||
if (remaining <= length) {
|
||||
const range = document.createRange();
|
||||
range.setStart(node, remaining);
|
||||
range.collapse(true);
|
||||
const selection = document.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
return;
|
||||
}
|
||||
remaining -= length;
|
||||
node = walker.nextNode();
|
||||
}
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(container);
|
||||
range.collapse(false);
|
||||
const selection = document.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
|
||||
function isHtmlEmpty(html, text) {
|
||||
return !text && !String(html || "").replace(/<br\s*\/?>/gi, "").replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim();
|
||||
}
|
||||
|
|
@ -114,6 +151,9 @@ export function NotepadModule({ toolboxId, moduleId, context, editing = false })
|
|||
const textContent = context.moduleText?.notepad || {};
|
||||
const editorRef = useRef(null);
|
||||
const surfaceRef = useRef(null);
|
||||
const selectionOffsetRef = useRef(null);
|
||||
const localSaveUpdatedAtRef = useRef("");
|
||||
const moduleKeyRef = useRef(`${toolboxId}:${moduleId}`);
|
||||
const [html, setHtml] = useState(normalizedData.html);
|
||||
const [text, setText] = useState(normalizedData.text);
|
||||
const [updatedAt, setUpdatedAt] = useState(normalizedData.updatedAt);
|
||||
|
|
@ -137,6 +177,19 @@ export function NotepadModule({ toolboxId, moduleId, context, editing = false })
|
|||
const [temporaryStrokes, setTemporaryStrokes] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const moduleKey = `${toolboxId}:${moduleId}`;
|
||||
if (moduleKeyRef.current !== moduleKey) {
|
||||
moduleKeyRef.current = moduleKey;
|
||||
localSaveUpdatedAtRef.current = "";
|
||||
}
|
||||
if (normalizedData.updatedAt && normalizedData.updatedAt === localSaveUpdatedAtRef.current) return;
|
||||
const editor = editorRef.current;
|
||||
if (document.activeElement === editor && getPlainText(editor) === normalizedData.text) {
|
||||
setText(normalizedData.text);
|
||||
setUpdatedAt(normalizedData.updatedAt);
|
||||
setDrawingMode(normalizedData.drawingMode);
|
||||
return;
|
||||
}
|
||||
setHtml(normalizedData.html);
|
||||
setText(normalizedData.text);
|
||||
setUpdatedAt(normalizedData.updatedAt);
|
||||
|
|
@ -145,10 +198,12 @@ export function NotepadModule({ toolboxId, moduleId, context, editing = false })
|
|||
setEraseActive(false);
|
||||
}, [toolboxId, moduleId, normalizedData.html, normalizedData.text, normalizedData.updatedAt, normalizedData.drawingMode]);
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
if (!editorRef.current || editorRef.current.innerHTML === html) return;
|
||||
if (document.activeElement === editorRef.current && getPlainText(editorRef.current) === text) return;
|
||||
editorRef.current.innerHTML = html;
|
||||
}, [html]);
|
||||
if (document.activeElement === editorRef.current) restoreSelectionTextOffset(editorRef.current, selectionOffsetRef.current);
|
||||
}, [html, text]);
|
||||
|
||||
useEffect(() => {
|
||||
function updateActiveFormats() {
|
||||
|
|
@ -199,6 +254,7 @@ export function NotepadModule({ toolboxId, moduleId, context, editing = false })
|
|||
|
||||
function saveContent(nextHtml, nextText) {
|
||||
const nextUpdatedAt = new Date().toISOString();
|
||||
localSaveUpdatedAtRef.current = nextUpdatedAt;
|
||||
setHtml(nextHtml);
|
||||
setText(nextText);
|
||||
setUpdatedAt(nextUpdatedAt);
|
||||
|
|
@ -241,6 +297,7 @@ export function NotepadModule({ toolboxId, moduleId, context, editing = false })
|
|||
}
|
||||
|
||||
function handleEditorInput() {
|
||||
selectionOffsetRef.current = getSelectionTextOffset(editorRef.current);
|
||||
const nextHtml = editorRef.current?.innerHTML || "";
|
||||
const nextText = getPlainText(editorRef.current);
|
||||
saveContent(nextHtml, nextText);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue