Coder Social home page Coder Social logo

prosemirror-menu's Introduction

prosemirror-menu

[ WEBSITE | ISSUES | FORUM | GITTER ]

This is a non-core example module for ProseMirror. ProseMirror is a well-behaved rich semantic content editor based on contentEditable, with support for collaborative editing and custom document schemas.

This module defines an abstraction for building a menu for the ProseMirror editor, along with an implementation of a menubar.

Note that this module exists mostly as an example of how you might want to approach adding a menu to ProseMirror, but is not maintained as actively as the core modules related to actual editing. If you want to extend or improve it, the recommended way is to fork it. If you are interested in maintaining a serious menu component for ProseMirror, publish your fork, and if it works for me, I'll gladly deprecate this in favor of your module.

This code is released under an MIT license. There's a forum for general discussion and support requests, and the Github bug tracker is the place to report issues.

Documentation

This module defines a number of building blocks for ProseMirror menus, along with a menu bar implementation.

When using this module, you should make sure its style/menu.css file is loaded into your page.

interface MenuElement

The types defined in this module aren't the only thing you can display in your menu. Anything that conforms to this interface can be put into a menu structure.

  • render(pm: EditorView) → {dom: HTMLElement, update: fn(state: EditorState) → boolean}
    Render the element for display in the menu. Must return a DOM element and a function that can be used to update the element to a new state. The update function must return false if the update hid the entire element.

class MenuItem

implements MenuElementAn icon or label that, when clicked, executes a command.

  • new MenuItem(spec: MenuItemSpec)
    Create a menu item.

  • spec: MenuItemSpec
    The spec used to create this item.

  • render(view: EditorView) → {dom: HTMLElement, update: fn(state: EditorState) → boolean}
    Renders the icon according to its display spec, and adds an event handler which executes the command when the representation is clicked.

interface MenuItemSpec

The configuration object passed to the MenuItem constructor.

  • run(state: EditorState, dispatch: fn(tr: Transaction), view: EditorView, event: Event)
    The function to execute when the menu item is activated.

  • select: ?fn(state: EditorState) → boolean
    Optional function that is used to determine whether the item is appropriate at the moment. Deselected items will be hidden.

  • enable: ?fn(state: EditorState) → boolean
    Function that is used to determine if the item is enabled. If given and returning false, the item will be given a disabled styling.

  • active: ?fn(state: EditorState) → boolean
    A predicate function to determine whether the item is 'active' (for example, the item for toggling the strong mark might be active then the cursor is in strong text).

  • render: ?fn(view: EditorView) → HTMLElement
    A function that renders the item. You must provide either this, icon, or label.

  • icon: ?IconSpec
    Describes an icon to show for this item.

  • label: ?string
    Makes the item show up as a text label. Mostly useful for items wrapped in a drop-down or similar menu. The object should have a label property providing the text to display.

  • title: ?string | fn(state: EditorState) → string
    Defines DOM title (mouseover) text for the item.

  • class: ?string
    Optionally adds a CSS class to the item's DOM representation.

  • css: ?string
    Optionally adds a string of inline CSS to the item's DOM representation.

  • type IconSpec = {path: string, width: number, height: number} | {text: string, css?: ?string} | {dom: Node}
    Specifies an icon. May be either an SVG icon, in which case its path property should be an SVG path spec, and width and height should provide the viewbox in which that path exists. Alternatively, it may have a text property specifying a string of text that makes up the icon, with an optional css property giving additional CSS styling for the text. Or it may contain dom property containing a DOM node.

class Dropdown

implements MenuElementA drop-down menu, displayed as a label with a downwards-pointing triangle to the right of it.

  • new Dropdown(content: readonly MenuElement[] | MenuElement, options: ?Object = {})
    Create a dropdown wrapping the elements.

  • render(view: EditorView) → {dom: HTMLElement, update: fn(state: EditorState) → boolean}
    Render the dropdown menu and sub-items.

class DropdownSubmenu

implements MenuElementRepresents a submenu wrapping a group of elements that start hidden and expand to the right when hovered over or tapped.

  • new DropdownSubmenu(content: readonly MenuElement[] | MenuElement, options: ?Object = {})
    Creates a submenu for the given group of menu elements. The following options are recognized:

  • render(view: EditorView) → {dom: HTMLElement, update: fn(state: EditorState) → boolean}
    Renders the submenu.

  • menuBar(options: Object) → Plugin
    A plugin that will place a menu bar above the editor. Note that this involves wrapping the editor in an additional <div>.

This module exports the following pre-built items or item constructors:

  • joinUpItem: MenuItem
    Menu item for the joinUp command.

  • liftItem: MenuItem
    Menu item for the lift command.

  • selectParentNodeItem: MenuItem
    Menu item for the selectParentNode command.

  • undoItem: MenuItem
    Menu item for the undo command.

  • redoItem: MenuItem
    Menu item for the redo command.

  • wrapItem(nodeType: NodeType, options: Partial & {attrs?: ?Attrs}) → MenuItem
    Build a menu item for wrapping the selection in a given node type. Adds run and select properties to the ones present in options. options.attrs may be an object that provides attributes for the wrapping node.

  • blockTypeItem(nodeType: NodeType, options: Partial & {attrs?: ?Attrs}) → MenuItem
    Build a menu item for changing the type of the textblock around the selection to the given type. Provides run, active, and select properties. Others must be given in options. options.attrs may be an object to provide the attributes for the textblock node.

To construct your own items, these icons may be useful:

  • icons: Object
    A set of basic editor-related icons. Contains the properties join, lift, selectParentNode, undo, redo, strong, em, code, link, bulletList, orderedList, and blockquote, each holding an object that can be used as the icon option to MenuItem.

  • renderGrouped(view: EditorView, content: readonly readonly MenuElement[][]) → {
      dom: DocumentFragment,
      update: fn(state: EditorState) → boolean
    }
    Render the given, possibly nested, array of menu elements into a document fragment, placing separators between them (and ensuring no superfluous separators appear when some of the groups turn out to be empty).

prosemirror-menu's People

Contributors

adamdonahue avatar adrianheine avatar alonbru avatar cjbest avatar darobin avatar dev1an avatar ericandrewlewis avatar hubgit avatar ianstormtaylor avatar kiejo avatar kofifus avatar l42y avatar linus avatar lpellegr avatar marijnh avatar necccc avatar ocavue avatar pocke avatar rexxars avatar rwatari avatar tgecho avatar timjb avatar timothygu avatar xylk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

prosemirror-menu's Issues

Uncaught TypeError: Cannot read properties of null (reading 'createElement')

My test code:

import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Schema, DOMParser, Mark } from "prosemirror-model";
import { schema } from "prosemirror-schema-basic";
import { addListNodes } from "prosemirror-schema-list";
import { exampleSetup } from "prosemirror-example-setup";

class MarkEditor {
  #schema: Schema;
  #root: HTMLElement;
  #view: EditorView;

  constructor() {
    this.#schema = new Schema({
      nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
      marks: schema.spec.marks,
    });

    this.#root = document.createElement("div");

    this.#view = new EditorView(this.#root, {
      state: EditorState.create({
        doc: DOMParser.fromSchema(this.#schema).parse(document.querySelector("#content")!),
        plugins: exampleSetup({ schema: this.#schema })
      }),
    });
  }

  mount(container: HTMLElement) {
    container.appendChild(this.#root);
  }
}

const editor = new MarkEditor();

editor.mount(document.querySelector("#app")!);

Getting this error:

index.js:17  Uncaught TypeError: Cannot read properties of null (reading 'createElement')
    at getIcon (index.js:17:20)
    at MenuItem.render (index.js:80:27)
    at renderGrouped (index.js:285:44)
    at new MenuBarView (index.js:493:31)
    at Object.view (index.js:475:35)
    at EditorView.updatePluginViews (index.js:5255:55)
    at new EditorView (index.js:5087:14)
    at new MarkEditor (main.ts:21:18)
    at main.ts:34:16

And my package.json is:

{
  "name": "markeditor",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  },
  "dependencies": {
    "prosemirror-example-setup": "^1.2.2",
    "prosemirror-model": "^1.19.3",
    "prosemirror-schema-basic": "^1.2.2",
    "prosemirror-schema-list": "^1.3.0",
    "prosemirror-state": "^1.4.3",
    "prosemirror-view": "^1.31.7"
  }
}

The documentation for the Fragment class does have information about the content property

The documentation for the Fragment class (from the prosemirror-model core module) does not contain information about the content property.

I suppose that an automated tool is used to create the documentation from the comments in the source code and in the source code there is no any comment about the content:

export class Fragment {
  constructor(content, size) {
    this.content = content                                                   <========== :: content comment is missing =====
    // :: number
    // The size of the fragment, which is the total of the size of its
    // content nodes.
    this.size = size || 0

Best regards

Docs around MenuItemSpec are incorrect

The class, css, and execEvent parameters to MenuItem are all described as optional, but aren't marked as such (i.e. by using ?). This causes errors when using generated types in TypeScript.

The extracted types are otherwise working very well!

Documentation for undoItem and redoItem is incorrect

Hello! I've just started using the TypeScript annotations for this module, which were auto-generated from the getdocs-style documentation, and I was having problems using undoItem and redoItem. I think this is because both of these symbols are defined as follows:

// :: (Object) → MenuItem

However, in reality it seems they aren't actually functions that return MenuItems, but rather that they are MenuItems themselves. Aside from being confusing to readers--the README has similar documentation, I'm not sure if it's auto-generated from getdocs too--it also trips up the auto-generated TypeScript annotations, which requires me to use a type assertion to override it (e.g. undoItem as any as MenuItem).

In any case, if this is indeed a mistake in the docs, and not some kind of noobish mistake I've made myself, I'm happy to submit a PR to fix things on this end, just let me know!

Thanks again for making this module, and ProseMirror in general.

Menu Toolbar expands as much as the height of the window after refresh

I was using Prosemirror with Bootstrap. However, due to some styles conflict, after refreshing the window, the toolbar of menu expands vertically. When I looked up in the source code, it appears that DOM sets the minHeight property of menubar dynamically according to some logic that depends on maxHeight.

A way to add multiple CSS classes to MenuItem and CSS class to outside Prosemirror-menuitem

#Hello,

We're in the process of trying to style the prosemirror menu effectively, and we've encountered a couple small usability issues with menu 0.17.x

  1. We want to add multple classes to our MenuItem without having to do anything too drastic. I found that I couldn't easy do that with the current implementation of classes being added via classList.add. Array of strings or string with spaces would not work. I patched that by just appending to the element's className property.

  2. We want to be able to add a class property to the parent 'Prosemirror-menuitem' span also, for styling purposes. I couldn't find a way with the current implementation. My solution was to add a new spec property called 'menuItemClass' that I append to the parent span's class in renderGrouped.

I don't know if what I've done fits into your framework or not, but I've submitted my minor pull request with my basic changes #4

I'd appreciate if you could comment on how I can get around this or if prosemirror can accommodate my use cases.

Thanks

List CSS file in package exports

Hello,

I've noticed that you made a commit to fix that but isn't reflected on the npm package.
11d8596

Could you publish the package to npm with the fix ?

Thanks

TypeError: Cannot read property 'replaceChild' of null

Getting this error

/node_modules/prosemirror-menu/dist/index.js:542 Uncaught (in promise) TypeError: Cannot read property 'replaceChild' of null
    at new MenuBarView (/node_modules/prosemirror-menu/dist/index.js:542)
    at Object.view (/node_modules/prosemirror-menu/dist/index.js:527)
    at EditorView.updatePluginViews (/node_modules/prosemirror-view/dist/index.js:4481)
    at new EditorView (/node_modules/prosemirror-view/dist/index.js:4344)

render isn't recognized as one of the three MenuItem spec options

image

The docs above indicate that defining either a label, an icon, or a render method should be sufficient for a MenuItem. And yet after upgrading from 0.21 to 0.24 all our renderable menu items are now resulting in an error:

image

I didn't see anything in the changelog indicating this new behavior, but I'm surprised nobody has reported it before. Is this a regression?

Pasting clipboard data raises exception in IE11

Here's the short video:

It seems like the error is happening in this peace of code:

if (!(root.body || root).contains(this.wrapper))

Transpiler converts it to:

this.scrollFunc = function () {
      if (!this$1.editor.root.contains(this$1.wrapper))
        { window.removeEventListener("scroll", this$1.scrollFunc) }
      else
        { this$1.updateFloat() }
    }

And it's not the thing IE expects

execEvent is not working

When I add {..., execEvent: "click"} it is not working. I checked the source and realized that execEvent described in spec doc but not processed at all. There is just a hardcoded "mousedown" event.

Add class option to DropdownSubmenu

Could you please add class to the DropdownSubmenu options so the following will add the class to the "Table" menu item, it's possible to do this for Dropdown and MenuItem but not DropdownSubmenu

new DropdownSubmenu(cut([
r.insertTable
]), {label: "Table",class: "menu-createtableselection"})

Icons are invisible if Prosemirror is inside Shadow DOM

Issue details

Following this example I can get ProseMirror to work fine in Light DOM, but not in Shadow DOM. Here is my code (my HTML file does nothing but embed a test-element):

import {LitElement, html, css} from 'lit-element';

import { EditorState } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import { Schema, DOMParser } from "prosemirror-model";
import { schema } from "prosemirror-schema-basic";
import { addListNodes } from "prosemirror-schema-list";
import { exampleSetup } from "prosemirror-example-setup";

customElements.define('test-element', class extends LitElement {
	static get styles() {
		return [css`
			@import 'https://prosemirror.net/css/editor.css';
		`];
	}
	render() {
		return html`
			<div id="editor"></div>
		`;
	}
	firstUpdated() {
		// Mix the nodes from prosemirror-schema-list into the basic schema to
		// create a schema with list support.
		const mySchema = new Schema({
		  nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
		  marks: schema.spec.marks
		})

		window.view = new EditorView(this.shadowRoot.querySelector("#editor"), {
		  state: EditorState.create({
		    doc: DOMParser.fromSchema(mySchema).parse(this.shadowRoot.querySelector("#editor")),
		    plugins: exampleSetup({schema: mySchema})
		  })
		})
	}
});

This is an example of how it looks:
20210312_14h25m49s_grim

ProseMirror version

I installed them from NPM, which got the following versions:

    "prosemirror-example-setup": "^1.1.2",
    "prosemirror-model": "^1.13.3",
    "prosemirror-schema-basic": "^1.1.2",
    "prosemirror-schema-list": "^1.1.4",
    "prosemirror-state": "^1.3.4",
    "prosemirror-view": "^1.18.0"

Affected platforms

  • Firefox

Floating bar in additional scrollable parent isn't placed correctly

Since merging #16, if you have an editor in a scrollable parent with a scrollable top-level document, it is very easy to make the bar appear in an incorrect place. The way updateFloat now takes a scrollParent argument doesn't seem to work very well.

(Tested on firefox, by wrapping an editor in a 400px high overflow: auto div.)

cc @cjbest

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.