diff --git a/demo/Menu.tsx b/demo/Menu.tsx new file mode 100644 index 00000000..2c719d5d --- /dev/null +++ b/demo/Menu.tsx @@ -0,0 +1,73 @@ +import { toggleMark } from "prosemirror-commands"; +import type { MarkType } from "prosemirror-model"; +import type { EditorState } from "prosemirror-state"; +import React, { ReactNode } from "react"; + +import { useEditorEventCallback, useEditorState } from "../src/index.js"; + +// lifted from: +// https://github.com/ProseMirror/prosemirror-example-setup/blob/master/src/menu.ts#L58 +function isMarkActive(mark: MarkType, state: EditorState): boolean { + const { from, $from, to, empty } = state.selection; + return empty + ? !!mark.isInSet(state.storedMarks || $from.marks()) + : state.doc.rangeHasMark(from, to, mark); +} + +export function Button(props: { + className?: string; + children?: ReactNode; + isActive: boolean; + title: string; + onClick: () => void; +}) { + return ( + + ); +} + +export default function Menu() { + const state = useEditorState(); + + const toggleBold = useEditorEventCallback((view) => { + if (!view) return; + const toggleBoldMark = toggleMark(view.state.schema.marks["strong"]); + toggleBoldMark(view.state, view.dispatch, view); + }); + + const toggleItalic = useEditorEventCallback((view) => { + if (!view) return; + const toggleItalicMark = toggleMark(view.state.schema.marks["em"]); + toggleItalicMark(view.state, view.dispatch, view); + }); + + return ( +
+ + +
+ ); +} diff --git a/demo/main.css b/demo/main.css index f419ecc3..fc18c35e 100644 --- a/demo/main.css +++ b/demo/main.css @@ -1,5 +1,5 @@ .ProseMirror { - border: thin solid black; + border: thin solid #ccc; border-radius: 0.25rem; padding: 1rem; outline: none; @@ -11,3 +11,46 @@ main { width: 80%; max-width: 700px; } + +.menu { + display: flex; + margin-bottom: 5px; +} + +.button { + cursor: pointer; + width: 36px; + height: 36px; + margin-right: 5px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: none; + border: thin solid #ccc; + border-radius: 0.25rem; + color: black; + background-color: white; + + &[aria-pressed="true"] { + background-color: #ddd; + color: blue; + } +} + +.button.bold { + font-weight: 700; +} + +.button.italic { + font-style: italic; +} + +.visually-hidden { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} diff --git a/demo/main.tsx b/demo/main.tsx index 10d94ad9..34ff93c1 100644 --- a/demo/main.tsx +++ b/demo/main.tsx @@ -5,6 +5,7 @@ import { liftEmptyBlock, newlineInCode, splitBlock, + toggleMark, } from "prosemirror-commands"; import { keymap } from "prosemirror-keymap"; import { Schema } from "prosemirror-model"; @@ -22,6 +23,7 @@ import { import { ReactNodeViewConstructor } from "../src/nodeViews/createReactNodeViewConstructor.js"; import { react } from "../src/plugins/react.js"; +import Menu from "./Menu.js"; import "./main.css"; const schema = new Schema({ @@ -32,6 +34,20 @@ const schema = new Schema({ list_item: { content: "paragraph+", toDOM: () => ["li", 0] }, text: { group: "inline" }, }, + marks: { + em: { + parseDOM: [{ tag: "em" }], + toDOM() { + return ["em", 0]; + }, + }, + strong: { + parseDOM: [{ tag: "strong" }], + toDOM() { + return ["strong", 0]; + }, + }, + }, }); const editorState = EditorState.create({ @@ -54,6 +70,8 @@ const editorState = EditorState.create({ ), "Shift-Enter": baseKeymap.Enter, "Shift-Tab": liftListItem(schema.nodes.list_item), + "Mod-b": toggleMark(schema.marks["strong"]), + "Mod-i": toggleMark(schema.marks["em"]), }), react(), ], @@ -101,13 +119,13 @@ function DemoEditor() { return (
-

React ProseMirror Demo

+
{renderNodeViews()}