Make the highlight selection and the marking more robust

Also cover the case when there are paragraphs and unordered lists in the
same ContentComponent
This commit is contained in:
Ramon Wenger 2024-01-30 17:10:16 +01:00
parent 8ef3f23edc
commit 2121f01a9c
3 changed files with 46 additions and 14 deletions

View File

@ -18,6 +18,7 @@
<component
v-bind="component"
:parent="parent"
class="content-component__content"
:is="componentType"
/>
</div>
@ -148,16 +149,22 @@ const componentClass = computed(() => {
}
return classes;
});
const paragraphs = computed(() => {
const childElements = computed(() => {
if (contentComponentDiv.value) {
let elements: HTMLCollection;
elements = contentComponentDiv.value.getElementsByTagName('p');
if (!elements.length) {
const uls = contentComponentDiv.value.getElementsByTagName('ul');
const ul = uls[0];
elements = ul.getElementsByTagName('li');
const parent = contentComponentDiv.value.querySelector('.content-component__content');
if (!parent) {
console.warn('Parent does not exist, this should not be possible');
// or can it be, if the children did not load yet, e.g. with a dynamic component?
return [];
}
return elements;
// make a flat list from all <li>-elements and all the others
const elements = Array.from(parent.children).reduce((acc: Element[], current: Element) => {
if (current.tagName.toLowerCase() === 'ul') {
return [...acc, ...current.children];
}
return [...acc, current];
}, []);
return elements as HTMLElement[];
}
return [];
});
@ -165,7 +172,7 @@ const paragraphs = computed(() => {
watch(
() => filteredHighlights.value.map((h) => h.color),
() => {
for (const paragraph of paragraphs.value) {
for (const paragraph of childElements.value) {
const instance = new Mark(paragraph);
instance.unmark();
}
@ -234,7 +241,7 @@ const { mutate: doDeleteHighlight } = useMutation(
const markHighlights = () => {
for (const highlight of filteredHighlights.value) {
const element = paragraphs.value[highlight.paragraphIndex];
const element = childElements.value[highlight.paragraphIndex];
const instance = new Mark(element);
const ranges: Mark.Range[] = [
{

View File

@ -95,6 +95,30 @@ const findPositionInParent = (element: HTMLElement, className: string = 'content
return children.indexOf(element);
};
const getParentElement = (element: HTMLElement): HTMLElement | null => {
if (element.tagName.toLowerCase() === 'li') {
return element.parentElement?.parentElement || null;
} else {
return element.parentElement || null;
}
};
const getSiblings = (element: HTMLElement): Element[] => {
const parent = getParentElement(element);
if (!parent) {
throw Error('element has no parent, this should not be possible');
}
let children: Element[] = [];
for (const child of parent.children) {
if (child.tagName.toLowerCase() === 'ul') {
children = [...children, ...child.children];
} else {
children = [...children, child];
}
}
return children;
};
export const getSelectionHandler =
(el: HTMLElement, contentBlock: ContentBlockNode, onUpdateHighlight: (highlight: any) => void = () => {}) =>
(_e: Event) => {
@ -123,11 +147,13 @@ export const getSelectionHandler =
if (contentComponent) {
// our selection is wholly inside the container node, we continue with it
const position = findPositionInParent(contentComponent);
const siblings = Array.from(startAncestor.parentElement?.children || []);
const uuid = contentComponent.dataset.uuid || '';
// const siblings = Array.from(startAncestor.parentElement?.children || []);
const siblings = getSiblings(startAncestor);
const positionInTextBlock = siblings.indexOf(startAncestor);
const { start, end } = range.toCharacterRange(startAncestor);
const uuid = contentComponent.dataset.uuid || '';
const highlightedText: Highlight = {
contentBlock: contentBlock.id,
contentIndex: position,
@ -137,7 +163,6 @@ export const getSelectionHandler =
selectionLength: end - start,
text: range.toString(),
};
console.log(highlightedText);
const positionOfContentBlock = el.getBoundingClientRect();
const positionOfSelection = startAncestor?.getBoundingClientRect();

View File

@ -4,7 +4,7 @@
"checkJs": false,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"lib": ["es2017", "dom"],
"lib": ["es2017", "dom", "dom.iterable"],
"target": "es6",
"module": "es2022",
"strict": true,