@explita/editor
Version:
`@explita/editor` is a versatile, modern rich-text editor built on TipTap for seamless integration into React applications. It provides extensive customization options and advanced features to cater to diverse content creation needs.
78 lines (77 loc) • 5.29 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEffect, useState } from "react";
import { useEditorStore } from "../store/useEditorState";
import { Separator } from "./ui/separator";
import { PAGE_HEIGHT } from "../lib/constants";
import { KeyboardShortcut } from "./KeyboardShortcut";
import { RiFullscreenExitLine, RiFullscreenFill } from "react-icons/ri";
import { LuMinus, LuPlus } from "react-icons/lu";
export function Footer() {
return (_jsxs("footer", { children: [_jsxs("div", { className: "explitaeditor:flex explitaeditor:items-center explitaeditor:gap-2", children: [_jsx(Chars, {}), _jsx(Words, {}), _jsx(Pages, {}), _jsx(Selected, {})] }), _jsxs("div", { className: "explitaeditor:flex explitaeditor:items-center explitaeditor:gap-2", children: [_jsx(EditorZoom, {}), _jsx(FullScreen, {}), _jsx(KeyboardShortcut, {})] })] }));
}
function Words() {
const { editor } = useEditorStore();
const words = editor?.storage.characterCount.words() || 0;
return (_jsxs(_Fragment, { children: [_jsxs("p", { children: [words?.toLocaleString(), " words"] }), _jsx(Separator, { orientation: "vertical", className: "explitaeditor:bg-neutral-200 explitaeditor:h-6" })] }));
}
function Chars() {
const { editor } = useEditorStore();
const characters = editor?.storage.characterCount.characters() || 0;
return (_jsxs(_Fragment, { children: [_jsxs("p", { children: [characters?.toLocaleString(), " characters"] }), _jsx(Separator, { orientation: "vertical", className: "explitaeditor:bg-neutral-200 explitaeditor:h-6" })] }));
}
function Pages() {
const { editor, editorOpts } = useEditorStore();
const [editorHeight, setEditorHeight] = useState(0);
useEffect(() => {
if (editor) {
const editorContainer = document.getElementsByClassName("tiptap");
if (editorContainer.length > 0) {
const element = editorContainer[0];
const height = element.offsetHeight;
setEditorHeight(height);
}
}
}, [editor?.getHTML(), editorOpts.padding]);
return (_jsxs(_Fragment, { children: [_jsxs("p", { children: [Math.ceil(editorHeight / PAGE_HEIGHT), editorHeight > PAGE_HEIGHT ? " pages" : " page"] }), _jsx(Separator, { orientation: "vertical", className: "explitaeditor:bg-neutral-200 explitaeditor:h-6" })] }));
}
function Selected() {
const { editor } = useEditorStore();
return (_jsx(_Fragment, { children: _jsxs("p", { children: ["Selected: ", countSelectedText(editor)] }) }));
}
function countSelectedText(editor) {
if (!editor)
return 0;
const { from, to } = editor.state.selection;
const selectedText = editor.state.doc.textBetween(from, to, " ");
return selectedText.trim().length;
}
function FullScreen() {
const [isFullScreen, setIsFullScreen] = useState(false);
function toggleFullScreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
}
else {
document.exitFullscreen();
}
}
useEffect(() => {
function handleFullScreenChange() {
setIsFullScreen(!!document.fullscreenElement);
}
document.addEventListener("fullscreenchange", handleFullScreenChange);
return () => {
document.removeEventListener("fullscreenchange", handleFullScreenChange);
};
}, []);
return (_jsx("button", { onClick: toggleFullScreen, children: isFullScreen ? (_jsx(RiFullscreenExitLine, { size: 16, title: "Exit Fullscreen" })) : (_jsx(RiFullscreenFill, { size: 16, title: "Enter Fullscreen" })) }));
}
function EditorZoom() {
const { editorOpts: { zoomLevel }, setEditorOpts, } = useEditorStore();
const handleZoomIn = () => setEditorOpts((prev) => ({ ...prev, zoomLevel: prev.zoomLevel + 0.1 }));
const handleZoomOut = () => setEditorOpts((prev) => ({
...prev,
zoomLevel: Math.max(prev.zoomLevel - 0.1, 0.1),
}));
return (_jsxs("div", { className: "explitaeditor:flex explitaeditor:items-center explitaeditor:gap-2", children: [_jsx("button", { onClick: handleZoomOut, className: "explitaeditor:size-5 explitaeditor:shrink-0 explitaeditor:flex explitaeditor:flex-col explitaeditor:items-center explitaeditor:justify-center explitaeditor:gap-0 explitaeditor:rounded-sm explitaeditor:hover:bg-neutral-200/80 explitaeditor:px-1.5 explitaeditor:overflow-hidden explitaeditor:text-sm explitaeditor:disabled:cursor-not-allowed", disabled: zoomLevel <= 0.2, children: _jsx(LuMinus, { size: 12 }) }), _jsx("button", { disabled: zoomLevel === 1, title: zoomLevel > 1 ? "Reset" : "", onClick: () => setEditorOpts((prev) => ({ ...prev, zoomLevel: 1 })), className: "explitaeditor:disabled:text-neutral-400", children: "Zoom" }), _jsx("button", { onClick: handleZoomIn, className: "explitaeditor:size-5 explitaeditor:shrink-0 explitaeditor:flex explitaeditor:flex-col explitaeditor:items-center explitaeditor:justify-center explitaeditor:gap-0 explitaeditor:rounded-sm explitaeditor:hover:bg-neutral-200/80 explitaeditor:px-1.5 explitaeditor:overflow-hidden explitaeditor:text-sm explitaeditor:disabled:cursor-not-allowed", disabled: zoomLevel >= 2, children: _jsx(LuPlus, { size: 12 }) })] }));
}