Giter Site home page Giter Site logo

react-prosemirror's Introduction

react-prosemirror

A React component for ProseMirror.

Note This package provides a solution for efficiently integrating ProseMirror with React, without creating a new API. If you're looking for something easy to use, you might like to try Tiptap or Remirror.

Packages

Demo

  • demo - a fully-configured editor

Usage

Quickstart: HTML Editor

import { useState } from 'react'
import { HtmlEditor, Toolbar, Editor } from '@aeaton/react-prosemirror'
import { plugins, schema, toolbar } from '@aeaton/react-prosemirror-config-default'

const initialValue = '<p></p>'

export const App = () => {
  const [value, setValue] = useState(initialValue)

  console.log({ value })

  return (
    <HtmlEditor
      schema={schema}
      plugins={plugins}
      value={initialValue}
      handleChange={setValue}
      debounce={250}
    >
      <Toolbar toolbar={toolbar} />
      <Editor autoFocus />
    </HtmlEditor>
  )
}

Custom Editor

Create a schema:

import { Schema } from 'prosemirror-model'

const schema = new Schema({
  nodes: {
    // a text node
    text: {},
    // a top-level doc node, which can contain at least one paragraph
    doc: { 
      content: 'paragraph+'
    },
    // a paragraph node, which can contain some text nodes, represented in HTML as `<p>`
    paragraph: { 
      content: 'text*',
      parseDOM: [{ tag: 'p' }],
      toDOM: () => ['p', 0],
    },
  },
  marks: {
    // a strong mark, represented in HTML as `<strong>`
    strong: {
      parseDOM: [{ tag: 'strong' }],
      toDOM: () => ['strong', 0],
    },
    // an emphasis mark, represented in HTML as `<em>`
    emphasis: {
      parseDOM: [{ tag: 'em' }],
      toDOM: () => ['em', 0],
    }
  }
})

Create some commands:

import { toggleMark } from 'prosemirror-commands'

const toggleMarkStrong = toggleMark(schema.marks.strong)
const toggleMarkEmphasis = toggleMark(schema.marks.emphasis)

Create plugins for handling history and key presses:

import { baseKeymap } from 'prosemirror-commands'
import { keymap } from 'prosemirror-keymap'
import { history, undo, redo } from 'prosemirror-history'

const plugins = [
  history(),
  keymap({
    'Mod-z': undo,
    'Shift-Mod-z': redo,
    'Meta-b': toggleMarkStrong,
    'Meta-i': toggleMarkEmphasis,
  }),
  keymap(baseKeymap),
]

Create a toolbar definition:

import { isMarkActive } from '@aeaton/prosemirror-commands'

const toolbar = [
  {
    id: 'marks',
    items: [
      {
        id: 'toggle-strong',
        content: icons.strong,
        action: toggleMarkStrong,
        enable: toggleMarkStrong,
        active: isMarkActive(schema.marks.strong),
      },
      {
        id: 'toggle-emphasis',
        title: 'Toggle emphasis',
        content: icons.emphasis,
        action: toggleMarkEmphasis,
        enable: toggleMarkEmphasis,
        active: isMarkActive(schema.marks.emphasis),
      },
    ]
  }
]

Create a doc by parsing some HTML:

import { createHTMLTransformer } from '@aeaton/prosemirror-transformers'

const transformer = createHTMLTransformer(schema)

const doc = transformer.parse('<p>Hello World!</p>')

Connect everything together to make your editor:

const CustomEditor = () => {
  return (
    <EditorProvider doc={doc} plugins={plugins}>
      <Toolbar toolbar={toolbar} />
      <Editor />
    </EditorProvider>
  )
}

The editor state is available in descendants of EditorProvider via a useEditorState hook:

import { useEditorState } from '@aeaton/react-prosemirror'

const ExampleComponent = () => {
  const state = useEditorState()
  
  // do something with the current state
}

The editor view is available in descendants of EditorProvider via a useEditorView hook:

import { useEditorView } from '@aeaton/react-prosemirror'

const ExampleComponent = () => {
  const view = useEditorView()
  
  // do something with the view
}

Components

EditorProvider

The EditorProvider component takes optional schema (or doc), plugins and editorProps props, uses them to create a new ProseMirror state and view, then makes the state and view available in React's context via useEditorState and useEditorView hooks.

Editor

The Editor component renders the ProseMirror view into a DOM element.

Toolbar

The Toolbar component takes a toolbar prop describing the toolbar (an array of grouped toolbar items) and renders a toolbar.

Each toolbar item has content (e.g. an icon), action (a command that's run when the button is pressed), plus optional active (whether the item is currently active) and enable (whether the item is currently enabled) props.

ChangeHandler

The ChangeHandler component makes it easy to listen for changes to the ProseMirror document.

When the document changes, after an optional debounce, it's run through the transformer then passed to the handleChange callback.

Styles

Each component has a class so it can be styled with CSS, and several of the styles can also be altered using CSS variables.

See the demo styles for examples of this.

react-prosemirror's People

Contributors

dependabot[bot] avatar hubgit avatar mjadobson avatar sapegin avatar shrunyan 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  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  avatar  avatar

react-prosemirror's Issues

[Question] Consider configurable debounce time?

Hi,

We were wondering if you would consider allowing the debounce time to be configurable?

We've a situations where users are clicking save buttons before the debounce has triggered an onChange which is causing users to lose data :(

Thanks!

Typescript support?

Hi,

I'd like to try using your lib using Typescript, but there's no type declaration file (index.d.ts), or a corresponding @types library on NPM.

Do you have any plan/would you add type declarations?

Cheers

Support for nodes those are not under document.body

Hey guys,

when EditorView.updateState called, scrollRectIntoView() raising following error:
image

As I've seen the issue is caused that our editor is not under document.body.
image

A quick fix is to break scroll loop if parent is document itself.

We are excited to use this editor, can you please add support for such a case.

I've opened a PR on official repo
ProseMirror/prosemirror-view#33

Prosemirror Global CSS cannot be imported from within node_modules

I am getting following error, not able to build my project

error - ./node_modules/@aeaton/prosemirror-placeholder/style/placeholder.css
Global CSS cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-npm
Location: node_modules/@aeaton/prosemirror-placeholder/placeholder.js
/Users/biplab/Desktop/H2M/h2m-landing/node_modules/@aeaton/react-prosemirror/index.js:1
export * from './ChangeHandler';
^^^^^^

SyntaxError: Unexpected token 'export'

"@aeaton/react-prosemirror": "^2.0.3",
"@aeaton/react-prosemirror-config-default": "^2.0.3",
"next": "11.0.1",
"react": "17.0.2",

Code I wrriten

import { HtmlEditor, Toolbar, Editor } from "@aeaton/react-prosemirror";
import {
plugins,
schema,
toolbar,
} from "@aeaton/react-prosemirror-config-default";




As per the mentioned in document: https://github.com/hubgit/react-prosemirror

Update release.

Can you please update npm release so that we can use nodeViews.

Strange op with underline.

Dear @hubgit you work is amazing! Thanks a lot!

I'm using this editor in my React app.

But I have this strange operation:

when I write a phrase I use the underline menu button.

If I inspect the editor value I can see this:

Some normal text and <span style="text-decoration: underline;">some words in underline</span>.</p>.

But if I re-open the editor with ... value={propText} saved before it doesn't recognize anymore the underline in editor:

  • Before save:

image

<p>Some normal text and <span style="text-decoration: underline;">some words in underline</span>.</p>

  • After save, editor re-opened with prop:

image

<p>Some normal text and <span style="text-decoration: underline;">some words in underline</span>.</p>

The prop code value is the same as you can see.

Where is the problem?

TextArea size

How can I change the size of the text are? styling the input doesn't work

Refactor configuration

Export constants from react-prosemirror-config-base and use them in react-prosemirror-config-full (an example of everything possible) and react-prosemirror-config-minimal (a minimal example).

render on update for redux state

Somehow value inside the htmlEditor tag does to reflect updated Redux state although it re-renders on new state? I might be a little tired here, but I just can' seam to get the new state to display in the editor window

Project status

Looks like the project is in active development but there’s no docs except the demo. Is it ready for production use? Would be nice to have a Readme with some instructions.

P. S. It’s awesome that you use Styleguidist for the docs ❤️

Own Schema does not fill storedMarks / toggle Mark not working

I was trying to create a new Schema. I just copied the default schema from react-prosemirror/packages/config-default/schema.ts to get started with something. When using this exact same schema in my own component, marks of the initialValue won't be generated. This means you cannot toggle those marks as they are not generated.

Excerpt:

import {
  plugins,
  toolbar,
  schema as defaultSchema
} from "@aeaton/react-prosemirror-config-default";
(...)
// copied from react-prosemirror/packages/config-default/schema.ts
export const schema = new Schema({
  marks: {
    bold,
    code,
    italic,
    link,
    strikethrough,
    subscript,
    superscript,
    underline
  },
  nodes: {
    text, // plain text node
    doc, // top-level node
    paragraph, // paragraph must be the first node type of the "block" group
    lineBreak,
    heading,
    blockquote,
    codeBlock,
    horizontalRule,
    list,
    listItem,
    table,
    tableRow,
    tableDataCell, // tableDataCell must be the first node type of the "tableCell" group
    tableHeaderCell
  }
});
(...)
return (
  <HtmlEditor
    schema={schema} // schema genereated with new Schema. Excact copy of react-prosemirror/packages/config-default/schema.ts (this IS NOT working)
    // schema={defaultSchema}     // default schema from prosemirror wrapper (this is working)
    plugins={plugins}
    value={initialValue}
    handleChange={setValue}
    debounce={250}
  >
    <Toolbar toolbar={toolbar} />
    <Editor autoFocus />
  </HtmlEditor>
);

Maybe this is a bug, maybe I am just missing something.

I've replicated the issue as stated above using CodeSandbox:
https://codesandbox.io/s/recursing-wozniak-o8589?file=/src/App.tsx:1256-1261

If you need any more information, I am happy to provide them to you.

render html tag like string value?

i am currently working on updating form which includes updating editor data. but it's not rendering value like

test

is there any way where "test" is shown as value. and later user can edit the text.

Yarn link error in electron environment

Hi there,

I want to implement markdown editor based on this repository in electron desktop environment.

  • Step 1. clone the repository
  • Step 2. build it
  • Step 3. use Yarn link to link the local built package
  • Step 4. create a simple seed project based on https://github.com/chentsulin/electron-react-boilerplate
  • Step 5, add react-pm editor with default configuration
  • Step 6, build .... and got following error:

ERROR in /Users/johnking/repos/kings-packages/react-prosemirror/react-prosemirror-config-default/dist/index.js
Module build failed: ReferenceError: Unknown plugin "react-hot-loader/babel" specified in "base" at 2, attempted to resolve relative to "/Users/johnking/repos/kings-packages/react-prosemirror/react-prosemirror-config-default/dist"
    at /Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
    at Array.map (<anonymous>)
    at Function.normalisePlugins (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
    at OptionManager.mergeOptions (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
    at OptionManager.init (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12)
    at File.initOptions (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/index.js:212:65)
    at new File (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/index.js:135:24)
    at Pipeline.transform (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/pipeline.js:46:16)
    at transpile (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-loader/lib/index.js:50:20)
    at /Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-loader/lib/fs-cache.js:118:18
 @ ./app/components/Editor.jsx 19:37-88
 @ ./app/containers/EditorPage.jsx
 @ ./app/routes.js
 @ ./app/containers/Root.jsx
 @ ./app/index.js
 @ multi (webpack)-dev-server/client?http://localhost:1212 webpack/hot/dev-server babel-polyfill react-hot-loader/patch webpack-dev-server/client?http://localhost:1212/ webpack/hot/only-dev-server ./app/index.js

ERROR in /Users/johnking/repos/kings-packages/react-prosemirror/react-prosemirror/dist/index.js
Module build failed: ReferenceError: Unknown plugin "react-hot-loader/babel" specified in "base" at 2, attempted to resolve relative to "/Users/johnking/repos/kings-packages/react-prosemirror/react-prosemirror/dist"
    at /Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
    at Array.map (<anonymous>)
    at Function.normalisePlugins (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
    at OptionManager.mergeOptions (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
    at OptionManager.init (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12)
    at File.initOptions (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/index.js:212:65)
    at new File (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/file/index.js:135:24)
    at Pipeline.transform (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-core/lib/transformation/pipeline.js:46:16)
    at transpile (/Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-loader/lib/index.js:50:20)
    at /Users/johnking/repos/electron-based/hdr-video-react/node_modules/babel-loader/lib/fs-cache.js:118:18
 @ ./app/components/Editor.jsx 17:24-60
 @ ./app/containers/EditorPage.jsx
 @ ./app/routes.js
 @ ./app/containers/Root.jsx
 @ ./app/index.js
 @ multi (webpack)-dev-server/client?http://localhost:1212 webpack/hot/dev-server babel-polyfill react-hot-loader/patch webpack-dev-server/client?http://localhost:1212/ webpack/hot/only-dev-server ./app/index.js


I have to use workaround: yarn add path/to/local/package/folder, but the trouble is that I need to reinstall it every time I modified the source.

I hope someone can help me on it.

thanks

-John

Clicking on menu button submits the form.

When the editor is put withing HTML5 form tag clicking on a button in editor's menu submits the form.
This is because the type attribute is missing here and according to the HTML5 button specification:

The missing value default is the Submit Button state.

All is needed to fix is to add the type attribute.

Left Align text

I would love to have the ability to set text left aligned. Is this potentially in the works?

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.