UNPKG

@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
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 }) })] })); }