Add highlight query to client
This commit is contained in:
parent
a6974853ef
commit
9213d57be5
|
|
@ -83,6 +83,7 @@
|
||||||
:root="root"
|
:root="root"
|
||||||
:parent="contentBlock"
|
:parent="contentBlock"
|
||||||
:bookmarks="contentBlock.bookmarks"
|
:bookmarks="contentBlock.bookmarks"
|
||||||
|
:highlights="contentBlock.highlights"
|
||||||
:notes="contentBlock.notes"
|
:notes="contentBlock.notes"
|
||||||
:edit-mode="editMode"
|
:edit-mode="editMode"
|
||||||
v-for="component in contentBlocksWithContentLists.contents"
|
v-for="component in contentBlocksWithContentLists.contents"
|
||||||
|
|
@ -260,7 +261,9 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the listener from highlights
|
// add the listener from highlights
|
||||||
element.addEventListener('mouseup', getSelectionHandler(element, props.contentBlock));
|
element.addEventListener('mouseup', getSelectionHandler(element, props.contentBlock), (newHighlight) => {
|
||||||
|
console.log(newHighlight);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
:data-scrollto="component.id"
|
:data-scrollto="component.id"
|
||||||
:data-uuid="component.id"
|
:data-uuid="component.id"
|
||||||
data-cy="content-component"
|
data-cy="content-component"
|
||||||
|
ref="contentComponentDiv"
|
||||||
>
|
>
|
||||||
<bookmark-actions
|
<bookmark-actions
|
||||||
:bookmarked="bookmarked"
|
:bookmarked="bookmarked"
|
||||||
|
|
@ -14,84 +15,51 @@
|
||||||
@edit-note="editNote"
|
@edit-note="editNote"
|
||||||
@bookmark="bookmarkContent(!bookmarked)"
|
@bookmark="bookmarkContent(!bookmarked)"
|
||||||
/>
|
/>
|
||||||
<component v-bind="component" :parent="parent" :is="componentType" />
|
<component
|
||||||
|
v-bind="component"
|
||||||
|
:parent="parent"
|
||||||
|
:is="componentType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineAsyncComponent, ref, computed, onMounted } from "vue";
|
import { defineAsyncComponent, ref, computed, onMounted } from 'vue';
|
||||||
import { useStore } from "vuex";
|
import { useStore } from 'vuex';
|
||||||
import { constructContentComponentBookmarkMutation } from "@/helpers/update-content-bookmark-mutation";
|
import { constructContentComponentBookmarkMutation } from '@/helpers/update-content-bookmark-mutation';
|
||||||
import { useMutation } from "@vue/apollo-composable";
|
import { useMutation } from '@vue/apollo-composable';
|
||||||
|
import { HighlightNode } from '@/__generated__/graphql';
|
||||||
|
import * as Mark from 'mark.js';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
component: any;
|
component: any;
|
||||||
parent: any;
|
parent: any;
|
||||||
|
highlights: HighlightNode[];
|
||||||
bookmarks: any[];
|
bookmarks: any[];
|
||||||
notes: any[];
|
notes: any[];
|
||||||
root: string;
|
root: string;
|
||||||
editMode: boolean;
|
editMode: boolean;
|
||||||
}
|
}
|
||||||
const TextBlock = defineAsyncComponent(
|
const TextBlock = defineAsyncComponent(() => import('@/components/content-blocks/TextBlock.vue'));
|
||||||
() => import("@/components/content-blocks/TextBlock.vue")
|
const InstrumentWidget = defineAsyncComponent(() => import('@/components/content-blocks/InstrumentWidget.vue'));
|
||||||
);
|
const ImageBlock = defineAsyncComponent(() => import('@/components/content-blocks/ImageBlock.vue'));
|
||||||
const InstrumentWidget = defineAsyncComponent(
|
const ImageUrlBlock = defineAsyncComponent(() => import('@/components/content-blocks/ImageUrlBlock.vue'));
|
||||||
() => import("@/components/content-blocks/InstrumentWidget.vue")
|
const VideoBlock = defineAsyncComponent(() => import('@/components/content-blocks/VideoBlock.vue'));
|
||||||
);
|
const LinkBlock = defineAsyncComponent(() => import('@/components/content-blocks/LinkBlock.vue'));
|
||||||
const ImageBlock = defineAsyncComponent(
|
const DocumentBlock = defineAsyncComponent(() => import('@/components/content-blocks/DocumentBlock.vue'));
|
||||||
() => import("@/components/content-blocks/ImageBlock.vue")
|
const CmsDocumentBlock = defineAsyncComponent(() => import('@/components/content-blocks/CmsDocumentBlock.vue'));
|
||||||
);
|
const InfogramBlock = defineAsyncComponent(() => import('@/components/content-blocks/InfogramBlock.vue'));
|
||||||
const ImageUrlBlock = defineAsyncComponent(
|
const ThinglinkBlock = defineAsyncComponent(() => import('@/components/content-blocks/ThinglinkBlock.vue'));
|
||||||
() => import("@/components/content-blocks/ImageUrlBlock.vue")
|
const GeniallyBlock = defineAsyncComponent(() => import('@/components/content-blocks/GeniallyBlock.vue'));
|
||||||
);
|
const SubtitleBlock = defineAsyncComponent(() => import('@/components/content-blocks/SubtitleBlock.vue'));
|
||||||
const VideoBlock = defineAsyncComponent(
|
const SectionTitleBlock = defineAsyncComponent(() => import('@/components/content-blocks/SectionTitleBlock.vue'));
|
||||||
() => import("@/components/content-blocks/VideoBlock.vue")
|
const ContentListBlock = defineAsyncComponent(() => import('@/components/content-blocks/ContentListBlock.vue'));
|
||||||
);
|
const ModuleRoomSlug = defineAsyncComponent(() => import('@/components/content-blocks/ModuleRoomSlug.vue'));
|
||||||
const LinkBlock = defineAsyncComponent(
|
const Assignment = defineAsyncComponent(() => import('@/components/content-blocks/assignment/Assignment.vue'));
|
||||||
() => import("@/components/content-blocks/LinkBlock.vue")
|
const Survey = defineAsyncComponent(() => import('@/components/content-blocks/SurveyBlock.vue'));
|
||||||
);
|
const Solution = defineAsyncComponent(() => import('@/components/content-blocks/Solution.vue'));
|
||||||
const DocumentBlock = defineAsyncComponent(
|
const Instruction = defineAsyncComponent(() => import('@/components/content-blocks/Instruction.vue'));
|
||||||
() => import("@/components/content-blocks/DocumentBlock.vue")
|
const BookmarkActions = defineAsyncComponent(() => import('@/components/notes/BookmarkActions.vue'));
|
||||||
);
|
|
||||||
const CmsDocumentBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/CmsDocumentBlock.vue")
|
|
||||||
);
|
|
||||||
const InfogramBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/InfogramBlock.vue")
|
|
||||||
);
|
|
||||||
const ThinglinkBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/ThinglinkBlock.vue")
|
|
||||||
);
|
|
||||||
const GeniallyBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/GeniallyBlock.vue")
|
|
||||||
);
|
|
||||||
const SubtitleBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/SubtitleBlock.vue")
|
|
||||||
);
|
|
||||||
const SectionTitleBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/SectionTitleBlock.vue")
|
|
||||||
);
|
|
||||||
const ContentListBlock = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/ContentListBlock.vue")
|
|
||||||
);
|
|
||||||
const ModuleRoomSlug = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/ModuleRoomSlug.vue")
|
|
||||||
);
|
|
||||||
const Assignment = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/assignment/Assignment.vue")
|
|
||||||
);
|
|
||||||
const Survey = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/SurveyBlock.vue")
|
|
||||||
);
|
|
||||||
const Solution = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/Solution.vue")
|
|
||||||
);
|
|
||||||
const Instruction = defineAsyncComponent(
|
|
||||||
() => import("@/components/content-blocks/Instruction.vue")
|
|
||||||
);
|
|
||||||
const BookmarkActions = defineAsyncComponent(
|
|
||||||
() => import("@/components/notes/BookmarkActions.vue")
|
|
||||||
);
|
|
||||||
|
|
||||||
type ContentComponentType =
|
type ContentComponentType =
|
||||||
| typeof TextBlock
|
| typeof TextBlock
|
||||||
|
|
@ -119,8 +87,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
component: () => ({}),
|
component: () => ({}),
|
||||||
parent: () => ({}),
|
parent: () => ({}),
|
||||||
bookmarks: () => [],
|
bookmarks: () => [],
|
||||||
|
highlights: () => [],
|
||||||
notes: () => [],
|
notes: () => [],
|
||||||
root: "",
|
root: '',
|
||||||
editMode: false,
|
editMode: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -149,44 +118,39 @@ const components: Record<string, ContentComponentType> = {
|
||||||
assignment: Assignment,
|
assignment: Assignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const contentComponentDiv = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const componentType = computed(() => {
|
const componentType = computed(() => {
|
||||||
return components[props.component.type] || "";
|
return components[props.component.type] || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const filteredHighlights = computed(
|
||||||
|
() => props.highlights.filter((highlight) => highlight.contentUuid === props.component.id) || []
|
||||||
|
);
|
||||||
const bookmarked = computed(
|
const bookmarked = computed(
|
||||||
() =>
|
() => props.bookmarks && !!props.bookmarks.find((bookmark) => bookmark.uuid === props.component.id)
|
||||||
props.bookmarks &&
|
|
||||||
!!props.bookmarks.find((bookmark) => bookmark.uuid === props.component.id)
|
|
||||||
);
|
);
|
||||||
const note = computed(() => {
|
const note = computed(() => {
|
||||||
const bookmark =
|
const bookmark = props.bookmarks && props.bookmarks.find((bookmark) => bookmark.uuid === props.component.id);
|
||||||
props.bookmarks &&
|
|
||||||
props.bookmarks.find((bookmark) => bookmark.uuid === props.component.id);
|
|
||||||
return bookmark && bookmark.note;
|
return bookmark && bookmark.note;
|
||||||
});
|
});
|
||||||
const showBookmarkActions = computed(
|
const showBookmarkActions = computed(
|
||||||
() =>
|
() => props.component.type !== 'content_list' && props.component.type !== 'basic_knowledge' && !props.editMode
|
||||||
props.component.type !== "content_list" &&
|
|
||||||
props.component.type !== "basic_knowledge" &&
|
|
||||||
!props.editMode
|
|
||||||
);
|
);
|
||||||
const componentClass = computed(() => {
|
const componentClass = computed(() => {
|
||||||
let classes = [
|
let classes = ['content-component', `content-component--${props.component.type}`];
|
||||||
"content-component",
|
|
||||||
`content-component--${props.component.type}`,
|
|
||||||
];
|
|
||||||
if (bookmarked.value) {
|
if (bookmarked.value) {
|
||||||
classes.push("content-component--bookmarked");
|
classes.push('content-component--bookmarked');
|
||||||
}
|
}
|
||||||
return classes;
|
return classes;
|
||||||
});
|
});
|
||||||
|
|
||||||
const addNote = (id: string) => {
|
const addNote = (id: string) => {
|
||||||
const type = Object.prototype.hasOwnProperty.call(props.parent, "__typename")
|
const type = Object.prototype.hasOwnProperty.call(props.parent, '__typename')
|
||||||
? props.parent.__typename
|
? props.parent.__typename
|
||||||
: "ContentBlockNode";
|
: 'ContentBlockNode';
|
||||||
|
|
||||||
store.dispatch("addNote", {
|
store.dispatch('addNote', {
|
||||||
content: id,
|
content: id,
|
||||||
type,
|
type,
|
||||||
block: props.root,
|
block: props.root,
|
||||||
|
|
@ -194,16 +158,15 @@ const addNote = (id: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const editNote = () => {
|
const editNote = () => {
|
||||||
store.dispatch("editNote", note);
|
store.dispatch('editNote', note);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { mutation, variables, update, optimisticResponse } =
|
const { mutation, variables, update, optimisticResponse } = constructContentComponentBookmarkMutation(
|
||||||
constructContentComponentBookmarkMutation(
|
props.component.id,
|
||||||
props.component.id,
|
bookmarked.value,
|
||||||
bookmarked.value,
|
props.parent,
|
||||||
props.parent,
|
props.root
|
||||||
props.root
|
);
|
||||||
);
|
|
||||||
const { mutate: mutateBookmarkContent } = useMutation(mutation, {
|
const { mutate: mutateBookmarkContent } = useMutation(mutation, {
|
||||||
update,
|
update,
|
||||||
optimisticResponse,
|
optimisticResponse,
|
||||||
|
|
@ -219,10 +182,35 @@ const bookmarkContent = (bookmarked: boolean) => {
|
||||||
console.log(newVars);
|
console.log(newVars);
|
||||||
mutateBookmarkContent(newVars);
|
mutateBookmarkContent(newVars);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (contentComponentDiv.value) {
|
||||||
|
const paragraphs = contentComponentDiv.value.getElementsByTagName('p');
|
||||||
|
for (const highlight of filteredHighlights.value) {
|
||||||
|
console.log(paragraphs);
|
||||||
|
console.log(highlight);
|
||||||
|
|
||||||
|
const element = paragraphs[highlight.paragraphIndex];
|
||||||
|
console.log(element);
|
||||||
|
const instance = new Mark(element);
|
||||||
|
console.log(instance);
|
||||||
|
const ranges: Mark.Range[] = [
|
||||||
|
{
|
||||||
|
start: highlight.startPosition,
|
||||||
|
length: highlight.startPosition,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
console.log(ranges);
|
||||||
|
instance.markRanges(ranges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "styles/helpers";
|
@import 'styles/helpers';
|
||||||
|
|
||||||
.content-component {
|
.content-component {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import popover from '@/helpers/popover';
|
||||||
// Which start and endpoint of the selection? Or rather start and length?
|
// Which start and endpoint of the selection? Or rather start and length?
|
||||||
// Also, we probably want to store the text of the selection at least, and maybe even some more for context?
|
// Also, we probably want to store the text of the selection at least, and maybe even some more for context?
|
||||||
|
|
||||||
interface Highlight {
|
export interface Highlight {
|
||||||
contentBlock: string; // the id
|
contentBlock: string; // the id
|
||||||
contentIndex: number; // which content of the .contents array do I come from?
|
contentIndex: number; // which content of the .contents array do I come from?
|
||||||
contentUUID: string; // the wagtail UUID of the content element
|
contentUUID: string; // the wagtail UUID of the content element
|
||||||
|
|
@ -22,6 +22,7 @@ interface Highlight {
|
||||||
startPosition: number; // start of the selection
|
startPosition: number; // start of the selection
|
||||||
selectionLength: number; // length of the selection
|
selectionLength: number; // length of the selection
|
||||||
text: string; // text that was actually selected
|
text: string; // text that was actually selected
|
||||||
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOP_TRESHOLD = 0;
|
const TOP_TRESHOLD = 0;
|
||||||
|
|
@ -86,78 +87,84 @@ const findPositionInParent = (element: HTMLElement, className: string = 'content
|
||||||
return children.indexOf(element);
|
return children.indexOf(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSelectionHandler = (el: HTMLElement, contentBlock: ContentBlockNode) => (_e: Event) => {
|
export const getSelectionHandler =
|
||||||
const rect = el.getBoundingClientRect();
|
(el: HTMLElement, contentBlock: ContentBlockNode, onUpdateHighlight: (highlight: any) => void = () => {}) =>
|
||||||
if (isInsideViewport(rect.top) || isInsideViewport(rect.bottom)) {
|
(_e: Event) => {
|
||||||
// the listener only does something if the `el` is visible in the viewport, to save resources
|
const rect = el.getBoundingClientRect();
|
||||||
|
if (isInsideViewport(rect.top) || isInsideViewport(rect.bottom)) {
|
||||||
|
// the listener only does something if the `el` is visible in the viewport, to save resources
|
||||||
|
|
||||||
// now we check if the selection is inside our container
|
// now we check if the selection is inside our container
|
||||||
|
|
||||||
const selection = rangy.getSelection();
|
const selection = rangy.getSelection();
|
||||||
if (selection.rangeCount && !selection.isCollapsed) {
|
if (selection.rangeCount && !selection.isCollapsed) {
|
||||||
const range = selection.getRangeAt(0);
|
const range = selection.getRangeAt(0);
|
||||||
if (range.intersectsNode(el)) {
|
if (range.intersectsNode(el)) {
|
||||||
// the selection is inside our container, so we can do something with it
|
// the selection is inside our container, so we can do something with it
|
||||||
const { startContainer, endContainer } = range;
|
const { startContainer, endContainer } = range;
|
||||||
const paragraph = findClosestAncestorWithTag(startContainer, 'p');
|
const paragraph = findClosestAncestorWithTag(startContainer, 'p');
|
||||||
const endAncestor = findClosestAncestorWithTag(endContainer, 'p');
|
const endAncestor = findClosestAncestorWithTag(endContainer, 'p');
|
||||||
|
|
||||||
if (paragraph && paragraph === endAncestor) {
|
if (!paragraph) {
|
||||||
// todo: rename this variable, `parent` is not correct anymore
|
throw Error('This should not be happening, but there was no paragraph');
|
||||||
const contentComponent = findClosestAncestorWithClass(startContainer, 'content-component');
|
}
|
||||||
if (contentComponent) {
|
if (paragraph === endAncestor) {
|
||||||
// our selection is wholly inside the container node, we continue with it
|
const contentComponent = findClosestAncestorWithClass(startContainer, 'content-component');
|
||||||
const position = findPositionInParent(contentComponent);
|
if (contentComponent) {
|
||||||
const siblings = Array.from(paragraph.parentElement?.children || []);
|
// our selection is wholly inside the container node, we continue with it
|
||||||
const positionInTextBlock = siblings.indexOf(paragraph);
|
const position = findPositionInParent(contentComponent);
|
||||||
|
const siblings = Array.from(paragraph.parentElement?.children || []);
|
||||||
|
const positionInTextBlock = siblings.indexOf(paragraph);
|
||||||
|
|
||||||
const numOfParagraphs = Array.from(contentComponent.children[1].children).filter(
|
const numOfParagraphs = Array.from(contentComponent.children[1].children).filter(
|
||||||
(child) => child.nodeName.toLowerCase() === 'p'
|
(child) => child.nodeName.toLowerCase() === 'p'
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
const { start, end } = range.toCharacterRange(paragraph);
|
const { start, end } = range.toCharacterRange(paragraph);
|
||||||
const uuid = contentComponent.dataset.uuid || '';
|
const uuid = contentComponent.dataset.uuid || '';
|
||||||
const highlightedText: Highlight = {
|
const highlightedText: Highlight = {
|
||||||
contentBlock: contentBlock.id,
|
contentBlock: contentBlock.id,
|
||||||
contentIndex: position,
|
contentIndex: position,
|
||||||
contentUUID: uuid,
|
contentUUID: uuid,
|
||||||
startPosition: start,
|
startPosition: start,
|
||||||
paragraphIndex: positionInTextBlock,
|
paragraphIndex: positionInTextBlock,
|
||||||
selectionLength: end - start,
|
selectionLength: end - start,
|
||||||
text: range.toString(),
|
text: range.toString(),
|
||||||
};
|
};
|
||||||
console.log(highlightedText);
|
console.log(highlightedText);
|
||||||
|
|
||||||
const positionOfContentBlock = el.getBoundingClientRect();
|
const positionOfContentBlock = el.getBoundingClientRect();
|
||||||
const positionOfSelection = paragraph?.getBoundingClientRect();
|
const positionOfSelection = paragraph?.getBoundingClientRect();
|
||||||
const offsetTop = positionOfSelection.top - positionOfContentBlock.top;
|
const offsetTop = positionOfSelection.top - positionOfContentBlock.top;
|
||||||
popover
|
popover
|
||||||
.show({
|
.show({
|
||||||
wrapper: el,
|
wrapper: el,
|
||||||
offsetTop,
|
offsetTop,
|
||||||
onChooseColor: (color: string) => {
|
onChooseColor: (color: string) => {
|
||||||
console.log('chosenColor', color);
|
console.log('chosenColor', color);
|
||||||
// const newHighlight: Highlight = {
|
highlightedText.color = color;
|
||||||
// contentBlock: contentBlock.id,
|
// const newHighlight: Highlight = {
|
||||||
// contentIndex:
|
// contentBlock: contentBlock.id,
|
||||||
//
|
// contentIndex:
|
||||||
// };
|
//
|
||||||
},
|
// };
|
||||||
})
|
},
|
||||||
.then(() => {
|
})
|
||||||
console.log('confirmed');
|
.then(() => {
|
||||||
})
|
console.log('confirmed here');
|
||||||
.catch(() => {
|
onUpdateHighlight();
|
||||||
console.log('canceled');
|
})
|
||||||
});
|
.catch(() => {
|
||||||
// todo: how do we save this? maybe we need to move away from the directive and do this inside the mounted
|
console.log('canceled');
|
||||||
// and unmounted hooks of the ContentBlock
|
});
|
||||||
|
// todo: how do we save this? maybe we need to move away from the directive and do this inside the mounted
|
||||||
|
// and unmounted hooks of the ContentBlock
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const highlight = (element: HTMLElement, ranges: Mark.Range[]) => {
|
const highlight = (element: HTMLElement, ranges: Mark.Range[]) => {
|
||||||
// todo: make the ContentComponent / TextBlock component handle the highlighting
|
// todo: make the ContentComponent / TextBlock component handle the highlighting
|
||||||
|
|
@ -177,11 +184,7 @@ const mounted = (el: HTMLElement, binding: DirectiveBinding) => {
|
||||||
// code inside this setTimeout would run too early and yield no elements
|
// code inside this setTimeout would run too early and yield no elements
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const elements = el.querySelectorAll<HTMLElement>('.content-component');
|
const elements = el.querySelectorAll<HTMLElement>('.content-component');
|
||||||
// console.log(el);
|
|
||||||
// console.log(elements);
|
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
// console.log(element);
|
|
||||||
// console.log(typeof element);
|
|
||||||
highlight(element, demoRanges);
|
highlight(element, demoRanges);
|
||||||
}
|
}
|
||||||
// highlight(el, demoRanges);
|
// highlight(el, demoRanges);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,15 @@ fragment ContentBlockParts on ContentBlockNode {
|
||||||
slug
|
slug
|
||||||
userCreated
|
userCreated
|
||||||
mine
|
mine
|
||||||
|
highlights {
|
||||||
|
contentIndex
|
||||||
|
paragraphIndex
|
||||||
|
selectionLength
|
||||||
|
contentUuid
|
||||||
|
startPosition
|
||||||
|
text
|
||||||
|
color
|
||||||
|
}
|
||||||
instrumentCategory {
|
instrumentCategory {
|
||||||
id
|
id
|
||||||
foreground
|
foreground
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue