Skip to content

Instantly share code, notes, and snippets.

@100ideas
Last active March 27, 2019 12:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 100ideas/9ba3aa4fffc5d44aebfbdcf1b437f286 to your computer and use it in GitHub Desktop.
Save 100ideas/9ba3aa4fffc5d44aebfbdcf1b437f286 to your computer and use it in GitHub Desktop.
playing around w/ remirror editor react component
{
"name": "dtcomponents",
"author": "100ideas",
"version": "0.1.0",
"private": true,
"license": "mit",
"dependencies": {
"@blueprintjs/core": "^3.14.1",
"@blueprintjs/datetime": "^3.7.2",
"@blueprintjs/icons": "^3.6.0",
"@blueprintjs/select": "^3.7.0",
"@blueprintjs/table": "^3.4.1",
"normalize.css": "^8.0.1",
"prop-types": "^15.7.2",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-scripts": "2.1.8",
"remirror": "^0.0.1-alpha.10"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"styles": "styleguidist server"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"devDependencies": {
"acorn": "^6.1.1",
"acorn-jsx": "^5.0.1",
"node-sass": "^4.11.0",
"react-styleguidist": "^9.0.4"
}
}
// styleguide/ThemeWrapper.js
// see
// https://react-styleguidist.js.org/docs/thirdparties.html#adding-styled-components-themeprovider
import React, { Component } from 'react'
import { FocusStyleManager } from "@blueprintjs/core";
import "normalize.css";
import "@blueprintjs/core/lib/css/blueprint.css";
import "@blueprintjs/icons/lib/css/blueprint-icons.css";
import "@blueprintjs/datetime/lib/css/blueprint-datetime.css";
import "@blueprintjs/select/lib/css/blueprint-select.css";
import "../index.scss";
if (module.hot) {
module.hot.accept();
}
// https://blueprintjs.com/docs/#core/accessibility.focus-management
FocusStyleManager.onlyShowFocusOnTabs();
export default class ThemeWrapper extends Component {
render() {
return (
<main>
{this.props.children}
</main>
)
}
}
// styleguide.config.js
const path = require('path')
module.exports = {
styleguideComponents: {
Wrapper: path.join(__dirname, 'src/styleguide-wrapper')
},
// styleguidist isn't doing HMR well?
// https://github.com/styleguidist/react-styleguidist/issues/1188#issuecomment-449657859
// BUG this is a security risk - also doesn't seem to be working.
// dangerouslyUpdateWebpackConfig(config) {
// if (!config.devServer) {
// config.devServer = {};
// }
// config.devServer.disableHostCheck = true;
// return config;
// }
}
// remirror.js hellow world example code
//
// src
// https://github.com/ifiokjr/remirror/blob/master/docs/examples/document-model.tsx
//
// discussion
// https://discuss.prosemirror.net/t/using-with-react/904/14
import React, { FunctionComponent, MouseEventHandler, useState } from 'react';
import { memoize } from '@remirror/core';
import { Bold, Italic, Underline } from '@remirror/core-extensions';
import { bubblePositioner, Remirror, RemirrorEventListener, RemirrorProps } from '@remirror/react';
import { RenderTree } from '@remirror/renderer-react';
function EditorLayout({initialJson}) {
const extensions = [new Bold(), new Italic(), new Underline()];
const [json, setJson] = useState(JSON.stringify(initialJson, null, 2));
const [displayJson, toggleJsonDisplay] = useState(false);
const onChange = ({ getJSON }) => {
const newJson = JSON.stringify(getJSON(), null, 2);
setJson(newJson);
};
// hand-compiled typescript source that was here to js like so...
// var runAction = memoize(function (method) { return function (e) {
// e.preventDefault();
// method();
// }; });
const runAction = memoize(
function (method) {
return function (e) {
e.preventDefault();
method();
};
});
// TODO figure out how to set this up - see
// /Users/100ideas/dev/da-play/1_overmind/remirror/support/types/prosemirror-view/index.d.ts#406
const handleDoubleClick = (view, pos, event) => {
console.dir(view, pos, event)
return true
}
return (
<div
style={{
gridTemplateColumns: '1fr',
gridTemplateRows: 'auto 1fr',
gridTemplateAreas: '"editor" "json"',
}}
>
<div style={{ gridArea: 'editor' }}>
<Remirror
attributes={{ 'data-test-id': 'editor-instance' }}
onChange={onChange}
placeholder='Start typing for magic...'
autoFocus={true}
extensions={extensions}
initialContent={initialJson}
handleDoubleClick={handleDoubleClick}
>
{({ getPositionerProps, actions }) => {
const props = getPositionerProps({
positionerId: 'bubble',
...bubblePositioner,
});
return (
<div>
<div
style={{
position: 'absolute',
bottom: props.isActive ? props.bottom : -9999,
left: props.isActive ? props.left : -9999,
}}
ref={props.ref}
>
<button
style={{
backgroundColor: actions.bold.isActive() ? 'white' : 'pink',
fontWeight: actions.bold.isActive() ? 600 : 300,
}}
disabled={!actions.bold.isEnabled()}
onClick={runAction(actions.bold.command)}
>
B
</button>
<button
style={{
backgroundColor: actions.italic.isActive() ? 'white' : 'pink',
fontWeight: actions.italic.isActive() ? 600 : 300,
}}
disabled={!actions.italic.isEnabled()}
onClick={runAction(actions.italic.command)}
>
i
</button>
<button
style={{
backgroundColor: actions.underline.isActive() ? 'white' : 'pink',
fontWeight: actions.underline.isActive() ? 600 : 300,
}}
disabled={!actions.underline.isEnabled()}
onClick={runAction(actions.underline.command)}
>
u
</button>
</div>
</div>
);
}}
</Remirror>
</div>
<div>
<pre
style={{
width: '100%',
height: '50%',
overflowY: 'auto',
padding: '1em',
background: 'black',
color: 'lawngreen',
}}
>
{json}
</pre>
</div>
</div>
);
};
const markMap = {
bold: 'strong',
italic: 'em',
// em: 'em',
// strong: 'strong',
code: 'code',
link: 'a',
underline: 'u',
};
// prosemirror-view ?
export function DocumentModelEditor() { return <EditorLayout initialJson={initialJson2} /> };
// prosemirror DOMserializer renders output
export function BasicRendererReact() { return <RenderTree json={initialJson1} /> };
export default function () { return <RenderTree json={initialJson2} markMap={markMap} /> };
const initialJson1 = {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Better docs to come soon...',
},
],
},
{
type: 'paragraph',
},
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'However for now it is important that something is in place.',
},
],
},
],
};
const initialJson2 = {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "Better docs to come soon..."
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "..."
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"marks": [
{
"type": "bold"
}
],
"text": "What’s the logic in your updateView method?"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "I have a similar challenge to you, and one idea I’ve been chewing on is to write a custom element that scrolls the page to bring itself into view. The idea is to do this in the connected callback, so that when it is attached to the DOM it can schedule scrolling via requestAnimationFrame. You would hook this into ProseMirror by just rendering it as a decoration."
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "There’s probably a few wrinkles I haven’t thought about, but one that does come to mind is:"
}
]
},
{
"type": "paragraph"
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"marks": [
{
"type": "bold"
}
],
"text": "After the scroll has completed, how do you stop it from repeating when the decoration is rendered again?"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "However for now it is important that something is in place."
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment