feat: search

This commit is contained in:
Philipinho
2024-01-30 00:14:21 +01:00
parent e0e5f7c43d
commit a0ec2f30ca
22 changed files with 509 additions and 161 deletions

View File

@ -1,137 +1,158 @@
import { Mark, mergeAttributes } from '@tiptap/core';
import { commentDecoration } from './comment-decoration';
import { Mark, mergeAttributes } from "@tiptap/core";
import { commentDecoration } from "./comment-decoration";
export interface ICommentOptions {
HTMLAttributes: Record<string, any>,
HTMLAttributes: Record<string, any>;
}
export interface ICommentStorage {
activeCommentId: string | null;
}
export const commentMarkClass = 'comment-mark';
export const commentDecorationMetaKey = 'decorateComment';
export const commentMarkClass = "comment-mark";
export const commentDecorationMetaKey = "decorateComment";
declare module '@tiptap/core' {
declare module "@tiptap/core" {
interface Commands<ReturnType> {
comment: {
setCommentDecoration: () => ReturnType,
unsetCommentDecoration: () => ReturnType,
setComment: (commentId: string) => ReturnType,
unsetComment: (commentId: string) => ReturnType,
setCommentDecoration: () => ReturnType;
unsetCommentDecoration: () => ReturnType;
setComment: (commentId: string) => ReturnType;
unsetComment: (commentId: string) => ReturnType;
};
}
}
export const Comment = Mark.create<ICommentOptions, ICommentStorage>({
name: 'comment',
exitable: true,
inclusive: false,
name: "comment",
exitable: true,
inclusive: false,
addOptions() {
return {
HTMLAttributes: {},
};
},
addOptions() {
return {
HTMLAttributes: {},
};
},
addStorage() {
return {
activeCommentId: null,
};
},
addStorage() {
return {
activeCommentId: null,
};
},
addAttributes() {
return {
commentId: {
default: null,
parseHTML: element => element.getAttribute('data-comment-id'),
renderHTML: (attributes) => {
if (!attributes.commentId) return;
addAttributes() {
return {
commentId: {
default: null,
parseHTML: (element) => element.getAttribute("data-comment-id"),
renderHTML: (attributes) => {
if (!attributes.commentId) return;
return {
'data-comment-id': attributes.commentId,
};
},
return {
"data-comment-id": attributes.commentId,
};
},
};
},
},
};
},
parseHTML() {
return [
{
tag: 'span[data-comment-id]',
getAttrs: (el) => !!(el as HTMLSpanElement).getAttribute('data-comment-id')?.trim() && null,
},
];
},
parseHTML() {
return [
{
tag: "span[data-comment-id]",
getAttrs: (el) =>
!!(el as HTMLSpanElement).getAttribute("data-comment-id")?.trim() &&
null,
},
];
},
addCommands() {
return {
setCommentDecoration: () => ({ tr, dispatch }) => {
addCommands() {
return {
setCommentDecoration:
() =>
({ tr, dispatch }) => {
tr.setMeta(commentDecorationMetaKey, true);
if (dispatch) dispatch(tr);
return true;
},
unsetCommentDecoration: () => ({ tr, dispatch }) => {
unsetCommentDecoration:
() =>
({ tr, dispatch }) => {
tr.setMeta(commentDecorationMetaKey, false);
if (dispatch) dispatch(tr);
return true;
},
setComment: (commentId) => ({ commands }) => {
setComment:
(commentId) =>
({ commands }) => {
if (!commentId) return false;
return commands.setMark(this.name, { commentId });
},
unsetComment:
(commentId) =>
({ tr, dispatch }) => {
if (!commentId) return false;
unsetComment:
(commentId) =>
({ tr, dispatch }) => {
if (!commentId) return false;
tr.doc.descendants((node, pos) => {
const from = pos;
const to = pos + node.nodeSize;
tr.doc.descendants((node, pos) => {
const from = pos;
const to = pos + node.nodeSize;
const commentMark = node.marks.find(mark =>
mark.type.name === this.name && mark.attrs.commentId === commentId);
const commentMark = node.marks.find(
(mark) =>
mark.type.name === this.name &&
mark.attrs.commentId === commentId,
);
if (commentMark) {
tr = tr.removeMark(from, to, commentMark);
}
});
if (commentMark) {
tr = tr.removeMark(from, to, commentMark);
}
});
return dispatch?.(tr);
},
};
},
return dispatch?.(tr);
},
};
},
renderHTML({ HTMLAttributes }) {
const commentId = HTMLAttributes?.['data-comment-id'] || null;
const elem = document.createElement('span');
renderHTML({ HTMLAttributes }) {
const commentId = HTMLAttributes?.["data-comment-id"] || null;
Object.entries(
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
).forEach(([attr, val]) => elem.setAttribute(attr, val));
if (typeof window === "undefined" || typeof document === "undefined") {
return [
"span",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
class: 'comment-mark',
"data-comment-id": commentId,
}),
0,
];
}
elem.addEventListener('click', (e) => {
const selection = document.getSelection();
if (selection.type === 'Range') return;
const elem = document.createElement("span");
this.storage.activeCommentId = commentId;
const commentEventClick = new CustomEvent('ACTIVE_COMMENT_EVENT', {
bubbles: true,
detail: { commentId },
});
Object.entries(
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
).forEach(([attr, val]) => elem.setAttribute(attr, val));
elem.dispatchEvent(commentEventClick);
elem.addEventListener("click", (e) => {
const selection = document.getSelection();
if (selection.type === "Range") return;
this.storage.activeCommentId = commentId;
const commentEventClick = new CustomEvent("ACTIVE_COMMENT_EVENT", {
bubbles: true,
detail: { commentId },
});
return elem;
},
// @ts-ignore
addProseMirrorPlugins(): Plugin[] {
// @ts-ignore
return [commentDecoration()];
},
elem.dispatchEvent(commentEventClick);
});
return elem;
},
);
// @ts-ignore
addProseMirrorPlugins(): Plugin[] {
// @ts-ignore
return [commentDecoration()];
},
});