@prisma-cms/front-editor
Version:
214 lines • 9.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = require("react");
const react_2 = require("react");
// import CSSTransform from '../../../Tag/HtmlTag/CSSTransform'
const nodeToEditorComponentObject_1 = require("../helpers/nodeToEditorComponentObject");
const interfaces_1 = require("../interfaces");
// TODO make generic
// type P = EditorComponentProps
// let updateCount = 0;
/**
* Хук для отслеживания изменений на HTML-элементе.
* В какой момент должен срабатывать? Главное условие: выбрасываемые им изменения
* не должны вызывать повторный апдейт этой же ноды, чтобы не возникало рекурсии.
* То есть нужен флаг, который бы сигнализировал когда можно отслеживать DOM и отдавать изменения.
*/
const useMutationObserver = (container,
// updateObject: EditorComponent["updateObject"],
// setNewContent: React.Dispatch<React.SetStateAction<HtmlTagProps['object']['components']>>
setNewContent, editMode) => {
// const [newContent, setNewContent] = useState<HtmlTagProps['object']['components']>([]);
// const context = useContext(Context);
// console.log('useMutationObserver context', context);
// console.log('newContent', newContent);
// const saveChanges = useCallback(() => {
// console.log('saveChanges newContent', newContent);
// console.log('saveChanges updateObject', updateObject);
// updateObject({
// components: newContent,
// });
// setNewContent([]);
// return true;
// }, [newContent, updateObject]);
const [contentEditable, setContentEditable] = react_1.useState(false);
// const [selection, setSelection] = useState<Selection | null>(global.document?.getSelection() ?? null);
// const [selection, setSelection] = useState<Selection | null>(null);
// const closestInSelection = useCallback(<T extends HTMLElement>(selector: string): T | null => {
// if (!selection?.focusNode) {
// return null
// }
// let node: Node | null | undefined = selection.focusNode
// if (node.nodeType === Node.TEXT_NODE) {
// node = selection.focusNode?.parentNode
// }
// if (node && node instanceof Element) {
// return node.closest(selector)
// }
// return null
// }, [selection]);
// TODO Надо будет перепроверить логику
const onClick = react_2.useCallback((event) => {
// const container = ref.current;
/**
* Prevent if already in edit mode
*/
if (contentEditable || !container) {
return;
}
const composedPath = event.composedPath();
let canBeEditable = true;
composedPath.every((path) => {
// console.log("composedPath forEach path", path);
var _a;
// console.log("composedPath forEach path contenteditable", path instanceof HTMLElement && path.attributes.getNamedItem("contenteditable")?.value);
/**
* Break reduce on current container
*/
if (path === container) {
return false;
}
// if (path instanceof HTMLElement && path.attributes.getNamedItem("contenteditable")?.value === "false") {
if (path instanceof HTMLElement) {
const contenteditable = (_a = path.attributes.getNamedItem('contenteditable')) === null || _a === void 0 ? void 0 : _a.value;
if (contenteditable === 'false') {
canBeEditable = false;
return false;
}
}
return true;
});
/**
* Prevent edit if not editable
*/
if (!canBeEditable) {
return;
}
setContentEditable(true);
event.currentTarget instanceof HTMLElement && event.currentTarget.focus();
// event.currentTarget.focus();
}, [container, contentEditable]);
/**
* Manager selection
*/
// const onSelectionChange = useCallback((_event) => {
// console.log('onSelectionChange _event', _event);
// // const container = ref.current;
// const selection2 = document.getSelection()
// if (selection2 && container && selection2.containsNode(container, true)) {
// setSelection(selection2);
// }
// else {
// setSelection(null);
// }
// }, [container]);
// useEffect(() => {
// document.addEventListener('selectionchange', onSelectionChange);
// return () => {
// document.removeEventListener('selectionchange', onSelectionChange);
// }
// }, [onSelectionChange]);
react_2.useEffect(() => {
// const container = ref.current;
/**
* Пока отключаем Этот блок
*/
// TODO Перепроверить логику
if (editMode !== interfaces_1.ContentProxyEditMode.HTML) {
return;
}
container === null || container === void 0 ? void 0 : container.addEventListener('click', onClick);
return () => {
container === null || container === void 0 ? void 0 : container.removeEventListener('click', onClick);
};
}, [container, contentEditable, editMode, onClick]);
react_2.useEffect(() => {
// const container = ref.current;
const onBlur = (_event) => {
// TODO Add update handler
// setContentEditable(false);
};
container === null || container === void 0 ? void 0 : container.addEventListener('blur', onBlur);
return () => {
container === null || container === void 0 ? void 0 : container.removeEventListener('blur', onBlur);
};
}, [container]);
// const updateContent = useCallback(nodeToEditorComponentObject, [])
const makeNewContent = react_2.useCallback(nodeToEditorComponentObject_1.nodeChildsToEditorComponentObjectComponents, []);
const onChangeDom = react_2.useCallback((container) => {
// updateCount++;
// if (updateCount > 10) {
// return;
// }
const content = makeNewContent(container);
const { components } = content;
// components && updateObject({ components })
// console.log('onChangeDom makeNewContent context', context);
// console.log('onChangeDom makeNewContent context?.setNewContent', context?.setNewContent);
components && setNewContent(components);
}, [makeNewContent, setNewContent]);
react_2.useEffect(() => {
if (!container || editMode !== interfaces_1.ContentProxyEditMode.HTML) {
return;
}
const config = {
attributes: true,
childList: true,
subtree: true,
characterData: true,
};
// Create an observer instance linked to the callback function
// const observer = new MutationObserver(this.onDOMSubtreeModified);
const observer = new MutationObserver((changes) => {
// console.log('onDomChanged changes', changes);
// console.log('onDomChanged container.outerHTML', container.outerHTML);
/**
* Filter changes. Get only from editable container
*/
const nodes = changes.filter(({ target }) => {
// if (target === container) {
// return false;
// }
var _a;
let node = target;
// console.log('onDomChanged node', node);
let success = false;
// let parent: (Node & ParentNode) | null = target.parentNode;
let i = 0;
// eslint-disable-next-line no-cond-assign
while (node && i < 100) {
i++;
if (!node) {
break;
}
if (node === container) {
success = true;
break;
}
if (node instanceof HTMLElement &&
((_a = node.attributes.getNamedItem('contenteditable')) === null || _a === void 0 ? void 0 : _a.value) === 'false') {
// console.log("while parent", i, node, node.attributes.getNamedItem("contenteditable")?.value);
break;
}
node = node === null || node === void 0 ? void 0 : node.parentNode;
// break;
}
// console.log("parent", parent);
return success;
});
/**
* If have changes on content, update state
*/
if (nodes.length) {
onChangeDom(container);
}
});
// Start observing the target node for configured mutations
observer.observe(container, config);
return () => {
observer.disconnect();
};
}, [container, editMode, onChangeDom]);
};
exports.default = useMutationObserver;
//# sourceMappingURL=useMutationObserver.js.map