diff --git a/src/Draftable.js b/src/Draftable.js new file mode 100644 index 0000000..36a6087 --- /dev/null +++ b/src/Draftable.js @@ -0,0 +1,114 @@ +// @flow +import React, { useState } from 'react'; +import { + Editor, + EditorState, + RichUtils, + Modifier, + getDefaultKeyBinding, +} from 'draft-js'; +import './styles.css'; +import Toolbar, { defaultToolbarConfig, type ToolbarConfigType } from './toolbar'; +import type { ToolbarButtonType } from './toolbarButton'; +import changeBlockDepth from './lib/changeBlockDepth'; + +type DraftableProps = { + initialState?: EditorState, + onChange?: (editorState: EditorState) => void, + toolbarConfig?: ToolbarConfigType, +}; + +const Draftable = ( + { + initialState = EditorState.createEmpty(), + onChange, + toolbarConfig = defaultToolbarConfig, + }:DraftableProps, +) => { + const [editorState, setEditorState] = useState(initialState); + + const handleChange = (stateChange:EditorState) => { + setEditorState(stateChange); + + if (onChange) { + // Send the changes to the parent + onChange(stateChange); + } + }; + + // Handle keyboard shortcuts (i.e. - Cmd + B, Cmd + z) + const handleKeyCommand = (command:string, keyCommandState:EditorState) => { + const newState = RichUtils.handleKeyCommand(keyCommandState, command); + if (newState) { + handleChange(newState); + return 'handled'; + } + return 'not-handled'; + }; + + const handleTab = (event: SyntheticKeyboardEvent<*>) => { + event.preventDefault(); + const selection = editorState.getSelection(); + const blockType = editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType(); + + if (blockType === 'unordered-list-item' || blockType === 'ordered-list-item') { + setEditorState(RichUtils.onTab(event, editorState, 3)); + } else { + const newContentState = Modifier.replaceText( + editorState.getCurrentContent(), + editorState.getSelection(), + ' ', + ); + setEditorState(EditorState.push(editorState, newContentState, 'insert-characters')); + } + }; + + const handleKeyBinding = (event: SyntheticKeyboardEvent<*>) => { + if (event.keyCode === 9) { + handleTab(event); + } + + return getDefaultKeyBinding(event); + }; + + const handleToolbarButton = (item:ToolbarButtonType) => { + if (item.type === 'style') { + switch (item.toggle) { + case 'inline': + setEditorState(RichUtils.toggleInlineStyle(editorState, item.style)); + return; + case 'block': + setEditorState(RichUtils.toggleBlockType(editorState, item.style)); + return; + case 'indent': { + const selection = editorState.getSelection(); + if (selection.isCollapsed()) { + const content = editorState.getCurrentContent(); + const blockKey = selection.getStartKey(); + const block = content.getBlockForKey(blockKey); + const depth = block.getDepth(); + const newState = changeBlockDepth(editorState, blockKey, depth + (item.style === 'indent' ? 1 : -1)); + setEditorState(newState); + } + break; + } + default: + return; + } + } + }; + + return ( + <> + + + + ); +}; + +export default Draftable; diff --git a/src/index.js b/src/index.js index 0a1e052..8525b22 100644 --- a/src/index.js +++ b/src/index.js @@ -1,116 +1,7 @@ // @flow -import React, { useState } from 'react'; -import { - Editor, - EditorState, - RichUtils, - Modifier, - getDefaultKeyBinding, -} from 'draft-js'; -import './styles.css'; -import './lib/DraftableState'; -import Toolbar, { defaultToolbarConfig, type ToolbarConfigType } from './toolbar'; -import type { ToolbarButtonType } from './toolbarButton'; -import changeBlockDepth from './lib/changeBlockDepth'; +import Draftable from './Draftable'; +import DraftableState, { FORMAT_HTML, FORMAT_MARKDOWN } from './lib/DraftableState'; - -type DraftableProps = { - initialState?: EditorState, - onChange?: (editorState: EditorState) => void, - toolbarConfig?: ToolbarConfigType, +export { + Draftable, DraftableState, FORMAT_HTML, FORMAT_MARKDOWN, }; - -const Draftable = ( - { - initialState = EditorState.createEmpty(), - onChange, - toolbarConfig = defaultToolbarConfig, - }:DraftableProps, -) => { - const [editorState, setEditorState] = useState(initialState); - - const handleChange = (stateChange:EditorState) => { - setEditorState(stateChange); - - if (onChange) { - // Send the changes to the parent - onChange(stateChange); - } - }; - - // Handle keyboard shortcuts (i.e. - Cmd + B, Cmd + z) - const handleKeyCommand = (command:string, keyCommandState:EditorState) => { - const newState = RichUtils.handleKeyCommand(keyCommandState, command); - if (newState) { - handleChange(newState); - return 'handled'; - } - return 'not-handled'; - }; - - const handleTab = (event: SyntheticKeyboardEvent<*>) => { - event.preventDefault(); - const selection = editorState.getSelection(); - const blockType = editorState.getCurrentContent().getBlockForKey(selection.getStartKey()).getType(); - - if (blockType === 'unordered-list-item' || blockType === 'ordered-list-item') { - setEditorState(RichUtils.onTab(event, editorState, 3)); - } else { - const newContentState = Modifier.replaceText( - editorState.getCurrentContent(), - editorState.getSelection(), - ' ', - ); - setEditorState(EditorState.push(editorState, newContentState, 'insert-characters')); - } - }; - - const handleKeyBinding = (event: SyntheticKeyboardEvent<*>) => { - if (event.keyCode === 9) { - handleTab(event); - } - - return getDefaultKeyBinding(event); - }; - - const handleToolbarButton = (item:ToolbarButtonType) => { - if (item.type === 'style') { - switch (item.toggle) { - case 'inline': - setEditorState(RichUtils.toggleInlineStyle(editorState, item.style)); - return; - case 'block': - setEditorState(RichUtils.toggleBlockType(editorState, item.style)); - return; - case 'indent': { - const selection = editorState.getSelection(); - if (selection.isCollapsed()) { - const content = editorState.getCurrentContent(); - const blockKey = selection.getStartKey(); - const block = content.getBlockForKey(blockKey); - const depth = block.getDepth(); - const newState = changeBlockDepth(editorState, blockKey, depth + (item.style === 'indent' ? 1 : -1)); - setEditorState(newState); - } - break; - } - default: - return; - } - } - }; - - return ( - <> - - - - ); -}; - -export default Draftable; diff --git a/stories/index.stories.js b/stories/index.stories.js index 1c3bd14..c4ee8c3 100644 --- a/stories/index.stories.js +++ b/stories/index.stories.js @@ -1,6 +1,6 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; -import Draftable from '../src'; +import { Draftable } from '../src'; import BoldIcon from '../src/icons/TextBold'; storiesOf('Draftable', module) diff --git a/test/Draftable.test.jsx b/test/Draftable.test.jsx index 90e5c7a..c775d3a 100644 --- a/test/Draftable.test.jsx +++ b/test/Draftable.test.jsx @@ -1,11 +1,10 @@ // @flow import React from 'react'; -import Draftable from '../src'; +import { Draftable } from '../src'; import BoldIcon from '../src/icons/TextBold'; import sinon from 'sinon'; import { render, fireEvent } from 'react-testing-library'; import { BLOCK_TYPES_INDENT, BLOCK_TYPES_INLINE, BLOCK_TYPES_LISTS } from '../src/lib/BlockTypes'; -import DraftableState, { FORMAT_HTML } from '../src/lib/DraftableState'; describe('Draftable', () => {