mirror of
https://github.com/docmost/docmost.git
synced 2026-05-11 00:44:07 +08:00
add nanoid by Vito0912
This commit is contained in:
@@ -16,32 +16,36 @@ const generateSlug = (text: string) =>
|
||||
|
||||
export default function HeadingView({ node }: NodeViewProps) {
|
||||
const { t } = useTranslation();
|
||||
const [slug, setSlug] = useState("");
|
||||
const [combinedId, setCombinedId] = useState("");
|
||||
const [url, setUrl] = useState("");
|
||||
const [showAnchorButton, setShowAnchorButton] = useState(false);
|
||||
|
||||
const tag: ElementType = `h${node.attrs.level}` as ElementType;
|
||||
const uid = node.attrs.uid;
|
||||
|
||||
useEffect(() => {
|
||||
const text = node.textContent || "";
|
||||
const generatedSlug = generateSlug(text);
|
||||
setSlug(generatedSlug);
|
||||
|
||||
const baseUrl = window.location.href.split("#")[0];
|
||||
setUrl(`${baseUrl}#${generatedSlug}`);
|
||||
}, [node.content]);
|
||||
if (uid) {
|
||||
const text = node.textContent || "";
|
||||
const textSlug = generateSlug(text);
|
||||
const combined = textSlug ? `${textSlug}-${uid}` : uid;
|
||||
setCombinedId(combined);
|
||||
|
||||
const baseUrl = window.location.href.split("#")[0];
|
||||
setUrl(`${baseUrl}#${combined}`);
|
||||
}
|
||||
}, [uid, node.content]);
|
||||
|
||||
return (
|
||||
<NodeViewWrapper
|
||||
as={tag}
|
||||
id={slug}
|
||||
id={combinedId}
|
||||
className={classes.anchorScrollMargin}
|
||||
onMouseEnter={() => setShowAnchorButton(true)}
|
||||
onMouseLeave={() => setShowAnchorButton(false)}
|
||||
>
|
||||
<Flex gap="sm" justify="flex-start" align="center">
|
||||
<NodeViewContent as="span" />
|
||||
{showAnchorButton && node.textContent && (
|
||||
{showAnchorButton && uid && combinedId && node.textContent && (
|
||||
<CopyButton value={url} timeout={2000}>
|
||||
{({ copied, copy }) => (
|
||||
<Tooltip
|
||||
|
||||
@@ -9,11 +9,39 @@ export function useAnchorScroll(offset = 95, maxRetries = 10, retryDelay = 500)
|
||||
let retries = maxRetries;
|
||||
|
||||
const tryScroll = () => {
|
||||
const el = document.getElementById(lastHash.current);
|
||||
let el = document.getElementById(lastHash.current);
|
||||
|
||||
if (!el) {
|
||||
const hash = lastHash.current;
|
||||
|
||||
if (hash.includes('-')) {
|
||||
const parts = hash.split('-');
|
||||
const possibleUid = parts[parts.length - 1];
|
||||
|
||||
const elements = document.querySelectorAll('[id]');
|
||||
for (const element of elements) {
|
||||
if (element.id.endsWith(`-${possibleUid}`)) {
|
||||
el = element as HTMLElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!el) {
|
||||
const elements = document.querySelectorAll('[id]');
|
||||
for (const element of elements) {
|
||||
if (element.id.endsWith(`-${hash}`) || element.id === hash) {
|
||||
el = element as HTMLElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
const y = el.getBoundingClientRect().top + window.scrollY - offset;
|
||||
window.scrollTo({ top: y, behavior: "smooth" });
|
||||
window.history.replaceState(null, "", `#${lastHash.current}`);
|
||||
window.history.replaceState(null, "", `#${el.id}`);
|
||||
} else if (retries > 0) {
|
||||
retries--;
|
||||
setTimeout(tryScroll, retryDelay);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Color } from "@tiptap/extension-color";
|
||||
import Table from "@tiptap/extension-table";
|
||||
import TableHeader from "@tiptap/extension-table-header";
|
||||
import SlashCommand from "@/features/editor/extensions/slash-command";
|
||||
import { Collaboration } from "@tiptap/extension-collaboration";
|
||||
import { Collaboration, isChangeOrigin } from "@tiptap/extension-collaboration";
|
||||
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
||||
import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||
import {
|
||||
@@ -76,6 +76,8 @@ import { CharacterCount } from "@tiptap/extension-character-count";
|
||||
import Heading from "@tiptap/extension-heading";
|
||||
import HeadingView from "../components/heading/heading-view";
|
||||
import { countWords } from "alfaaz";
|
||||
import UniqueID from '@tiptap/extension-unique-id';
|
||||
import { generateSlugId } from "../utils/nanoid";
|
||||
|
||||
const lowlight = createLowlight(common);
|
||||
lowlight.register("mermaid", plaintext);
|
||||
@@ -225,6 +227,12 @@ export const mainExtensions = [
|
||||
CharacterCount.configure({
|
||||
wordCounter: (text) => countWords(text),
|
||||
}),
|
||||
UniqueID.configure({
|
||||
types: ['heading'],
|
||||
attributeName: 'uid',
|
||||
generateID: () => generateSlugId(),
|
||||
filterTransaction: (transaction) => !isChangeOrigin(transaction),
|
||||
}),
|
||||
] as any;
|
||||
|
||||
type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[];
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
const slugIdAlphabet =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
export const generateSlugId = customAlphabet(slugIdAlphabet, 10);
|
||||
@@ -54,6 +54,7 @@
|
||||
"@tiptap/extension-text-style": "^2.10.3",
|
||||
"@tiptap/extension-typography": "^2.10.3",
|
||||
"@tiptap/extension-underline": "^2.10.3",
|
||||
"@tiptap/extension-unique-id": "^2.23.0",
|
||||
"@tiptap/extension-youtube": "^2.10.3",
|
||||
"@tiptap/html": "^2.10.3",
|
||||
"@tiptap/pm": "^2.10.3",
|
||||
|
||||
Reference in New Issue
Block a user