https://github.com/ericadamski/react-markdown-menu
A small medium-esk react component to allow editing markdown files
https://github.com/ericadamski/react-markdown-menu
Last synced: 3 months ago
JSON representation
A small medium-esk react component to allow editing markdown files
- Host: GitHub
- URL: https://github.com/ericadamski/react-markdown-menu
- Owner: ericadamski
- License: mit
- Created: 2018-02-13T13:14:12.000Z (over 7 years ago)
- Default Branch: develop
- Last Pushed: 2018-05-03T18:27:21.000Z (about 7 years ago)
- Last Synced: 2025-02-06T13:06:48.366Z (4 months ago)
- Language: JavaScript
- Homepage: https://ericadamski.github.io/react-markdown-menu/
- Size: 488 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://travis-ci.org/ericadamski/react-markdown-menu)
[](https://github.com/prettier/prettier)# react-markdown-menu
A small medium-esk react component to allow editing markdown files
# Install
`npm install react-markdown-menu`
or
`yarn add react-markdown-menu`
# Usage
While the component is intended to be used as a compound component until it has been tested the main usage will be through the base `MarkdownMenu` component
```javascript
class MarkdownMenu extends Component {
static propTypes = {
// The number of pixels from the left of the browser to place the menu
x: PropTypes.number,
// The number of pixels from the top of the browser to place the menu
y: PropTypes.number,
// A function that is called whenever a button on the menu is clicked.
// The function is of the type:
// function onChange(modifiedText: string, line: boolean): undefined
// When a button is clicked to modified text is sent to the onChange,
// and a boolean to say whether to replace the entire line or
// just the selected area
onChange: PropTypes.func.isRequired,
// The text selection of the entire line the cursor is currently on
lineSelection: PropTypes.string,
// The text selection of whatever is curretly highlighted
selection: PropTypes.string,
};/* the rest of the class */
}
```Below is an example using a textarea as the editor
```javascript
import React from 'react';
import { render, findDOMNode } from 'react-dom';
import { MarkdownMenu } from 'react-markdown-menu';class App extends React.Component {
state = {};getLineRange(value, selectionEnd) {
let length = 0;const lines = value.split('\n').map(str => (length += str.length + 1));
const max = lines.filter(l => l < selectionEnd);
const start = max[max.length - 1] || 0;
const end = lines[max.length] - 1;return [start, end];
}componentDidMount() {
// Setup event listeners
if (this.textareaRef) {
const textarea = (this.textarea = findDOMNode(this.textareaRef));
const menu = findDOMNode(this.menu);console.log(menu);
const hideMenu = () =>
this.setState({ selection: null, lineSelection: null });// Hide then menu on scroll of the window.
document.addEventListener('scroll', hideMenu);// Hide the menu on clicking outside of the menu
document.addEventListener(
'mousedown',
({ target }) => !menu.contains(target) && hideMenu()
);// Update the selected text and position of the menu
let clickEvent;
// Keep track of the click position to know where to place the menu
textarea.addEventListener('mousedown', event => (clickEvent = event));
// Handle text and line selection
textarea.addEventListener('select', event => {
const { value, selectionStart, selectionEnd } = textarea;
const { clientX, clientY } = clickEvent;
console.log(clickEvent);const [lineStart, lineEnd] = this.getLineRange(value, selectionEnd);
console.log(this);
this.setState({
x: clientY,
y: clientX,
lineRange: [lineStart, lineEnd],
selectionRange: [selectionStart, selectionEnd],
lineSelection: value.substring(lineStart, lineEnd),
selection: value.substring(selectionStart, selectionEnd),
});
});
}
}onChange(text, line) {
const { selectionRange, lineRange } = this.state;const [start, end] = line ? lineRange : selectionRange;
this.textarea.value = `${this.textarea.value.slice(
0,
start
)}${text}${this.textarea.value.slice(end)}`;this.textarea.setSelectionRange(start, end + (text.length - end - start));
}render() {
const { x, y, selection, lineSelection } = this.state;return (
(this.menu = node)}
x={x}
y={y}
onChange={this.onChange.bind(this)}
selection={selection}
lineSelection={lineSelection}
/>
(this.textareaRef = node)} />
);
}
}render(, document.getElementById('root'));
```[](https://codesandbox.io/s/4jv2612w4x)
The **experimental** compound component example that hasn't been completely tested and the API not yet refined.
```javascript
import React from 'react';
import { render } from 'react-dom';
import { Editor } from 'react-markdown-menu';class App extends React.Component {
getLineRange(value, selectionEnd) {
let length = 0;const lines = value.split('\n').map(str => (length += str.length + 1));
const max = lines.filter(l => l < selectionEnd);
const start = max[max.length - 1] || 0;
const end = lines[max.length] - 1;return [start, end];
}getSelectionRange(element) {
const { selectionStart, selectionEnd } = element;return [selectionStart, selectionEnd];
}updateText(element, text, [start, end]) {
element.value = `${element.value.slice(
0,
start
)}${text}${element.value.slice(end)}`;
}updateSelection(element, text, [start, end]) {
element.setSelectionRange(start, end + (text.length - end - start));
}render() {
return (
element.addEventListener('select', update)
}
getLineRange={this.getLineRange}
render={() => }
/>
);
}
}render(, document.getElementById('root'));
```[](https://codesandbox.io/s/52nnq4pxml)