Skip to content
File

Blob: src/client/components/editor/editor-metrics.tsx

typescript40 lines
1import { useTiptap, useTiptapState } from "@tiptap/react";
2import type { Editor } from "@tiptap/core";
3import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
4import { collectEditorTextMetrics, type EditorTextMetrics } from "@/shared/editor/schema";
5import { EditorMetricsPresentation } from "@/shared/editor/presentation/metrics";
6 
7type CharacterCountStorage = {
8 words: (options?: { node?: ProseMirrorNode }) => number;
9 characters: (options?: { node?: ProseMirrorNode; mode?: "textSize" | "nodeSize" }) => number;
10};
11 
12export interface EditorMetricsProps {
13 className?: string;
14}
15 
16function getEditorMetrics(editor: Editor): EditorTextMetrics {
17 const storage = editor.storage as { characterCount?: CharacterCountStorage | undefined };
18 const characterCount = storage.characterCount;
19 
20 if (characterCount?.words && characterCount.characters) {
21 return {
22 words: characterCount.words({ node: editor.state.doc }),
23 characters: characterCount.characters({ node: editor.state.doc, mode: "textSize" }),
24 };
25 }
26 
27 return collectEditorTextMetrics(editor.state.doc);
28}
29 
30export function EditorMetrics({ className }: EditorMetricsProps) {
31 const { editor } = useTiptap();
32 const metrics = useTiptapState(({ editor: currentEditor }) => getEditorMetrics(currentEditor));
33 
34 if (!editor || !metrics) {
35 return null;
36 }
37 
38 return <EditorMetricsPresentation metrics={metrics} className={className} />;
39}