diff --git a/README.md b/README.md index 3725fcc..a69146c 100644 --- a/README.md +++ b/README.md @@ -146,9 +146,9 @@ import { EditorState } from 'draft-js' You can define autocomplete strategies to present suggested content lists based on the text input. Just set your trigger character, add some search keys and the content to insert and the editor will do everything for you. You can navigate through suggestions using the keyboard arrows and finally press 'Enter' to insert your content into the editor. -### Emoji strategy example +### Simple strategy example -This is a simple example to present emoji suggestions when the user start typing a text like ':face', ':joy', or ':grin': +This is an example to show emoji suggestions when the user start typing a text like ':face', ':joy', or ':grin': ```js import MUIRichTextEditor from 'mui-rte' @@ -183,7 +183,11 @@ const emojis = [ /> ``` -Check [this sample](https://github.com/niuware/mui-rte/blob/master/examples/autocomplete/index.tsx) that shows how to add multiple autocomplete strategies. +Check [this sample](https://github.com/niuware/mui-rte/blob/master/examples/autocomplete/index.tsx) that shows how to add multiple autocomplete strategies to a single editor. + +### Atomic strategy example + +Check [this sample](https://github.com/niuware/mui-rte/blob/master/examples/autocomplete/index.tsx) that shows how to combine atomic custom controls with the autocomplete strategy feature. ## Custom Decorators @@ -365,6 +369,7 @@ Object.assign(defaultTheme, { |triggerChar|`string`|required|A single character that triggers the autocomplete strategy.| |items|`TAutocompleteItem[]`|required|List of autocomplete suggestion items.| |insertSpaceAfter|`boolean`|optional|If `false` it won't add an space after inserting the content into the editor. Default is `true`.| +|atomicBlockName|`string`|optional|Use an *atomic* custom control type to add the content to the editor.|
@@ -373,7 +378,7 @@ Object.assign(defaultTheme, { |Property|Type||description| |---|---|---|---| |keys|`string[]`|required|The list of keys that the user needs to type to reveal this item suggestion.| -|value|`string`|required|The value to insert into the editor when the item is selected.| +|value|`any`|required|The value to insert into the editor when the item is selected.| |content|`string | JSX.Element`|required|The content presented in the autocomplete suggestion list for this item. Note that this content is render under a `ListItem` component.|
diff --git a/examples/autocomplete-atomic/index.tsx b/examples/autocomplete-atomic/index.tsx new file mode 100644 index 0000000..e3bd874 --- /dev/null +++ b/examples/autocomplete-atomic/index.tsx @@ -0,0 +1,88 @@ +import React, { FunctionComponent } from 'react' +import Avatar from '@material-ui/core/Avatar' +import Chip from '@material-ui/core/Chip' +import MUIRichTextEditor from '../../' +import { TAutocompleteItem } from '../../src/components/Autocomplete' + +const save = (data: string) => { + console.log(data) +} + +const cities: TAutocompleteItem[] = [ + { + keys: ["mexico"], + value: { + name: "Mexico City", + image: "🇲🇽" + }, + content: "Mexico City", + }, + { + keys: ["mexico", "beach"], + value: { + name: "Cancun", + image: "🚩" + }, + content: "Cancun", + }, + { + keys: ["japan", "olympics"], + value: { + name: "Tokyo", + image: "🇯🇵" + }, + content: "Tokyo", + }, + { + keys: ["japan"], + value: { + name: "Osaka", + image: "🏁" + }, + content: "Osaka", + } +] + +const CityChip: FunctionComponent = (props) => { + const { blockProps } = props + const { value } = blockProps // Get the value provided in the TAutocompleteItem[] + + const handleClick = () => { + console.log(value.name) + } + + return ( + {value.image}} + label={value.name} + onClick={handleClick} + /> + ) +} + +const AutocompleteAtomic = () => { + return ( + + ) +} + +export default AutocompleteAtomic \ No newline at end of file diff --git a/examples/main.tsx b/examples/main.tsx index 128756b..a833547 100644 --- a/examples/main.tsx +++ b/examples/main.tsx @@ -15,6 +15,7 @@ import AtomicCustomBlock from './atomic-custom-block' import KeyBindings from './key-bindings' import MaxLength from './max-length' import Autocomplete from './autocomplete' +import AutocompleteAtomic from './autocomplete-atomic' const App = () => { @@ -42,6 +43,7 @@ const App = () => { +
diff --git a/package.json b/package.json index 5e32827..b0a1713 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mui-rte", - "version": "1.17.2", + "version": "1.17.3", "description": "Material-UI Rich Text Editor and Viewer", "keywords": [ "material-ui", diff --git a/src/MUIRichTextEditor.tsx b/src/MUIRichTextEditor.tsx index b93ee00..529d98f 100644 --- a/src/MUIRichTextEditor.tsx +++ b/src/MUIRichTextEditor.tsx @@ -95,6 +95,7 @@ export type TAutocompleteStrategy = { triggerChar: string items: TAutocompleteItem[] insertSpaceAfter?: boolean + atomicBlockName?: string } export type TAutocomplete = { @@ -386,20 +387,37 @@ const MUIRichTextEditor: RefForwardingComponent = 'focusOffset': currentSelection.getFocusOffset() + searchTerm.length + 1 }) const currentContentState = editorState.getCurrentContent() - const entityKey = currentContentState.createEntity('AC_ITEM', 'IMMUTABLE').getLastCreatedEntityKey(); - const contentState = Modifier.replaceText(editorStateRef.current!.getCurrentContent(), - newSelection, - item.value, - editorStateRef.current!.getCurrentInlineStyle(), - entityKey) - const newEditorState = EditorState.push(editorStateRef.current!, contentState, "insert-characters") - if (currentAutocompleteRef.current!.insertSpaceAfter === false) { - handleChange(newEditorState) + if (currentAutocompleteRef.current!.atomicBlockName) { + const name = currentAutocompleteRef.current!.atomicBlockName + const block = atomicBlockExists(name, props.customControls) + if (block) { + const contentState = Modifier.removeRange(editorStateRef.current!.getCurrentContent(), + newSelection, + "forward") + const newEditorState = EditorState.push(editorStateRef.current!, contentState, "remove-range") + const withAtomicBlock = insertAtomicBlock(newEditorState, name.toUpperCase(), { + value: item.value + }, { + selection: newEditorState.getCurrentContent().getSelectionAfter() + }) + handleChange(withAtomicBlock) + } } else { - const addSpaceState = Modifier.insertText(newEditorState.getCurrentContent(), - newEditorState.getSelection(), " ", - newEditorState.getCurrentInlineStyle()) - handleChange(EditorState.push(newEditorState, addSpaceState, "insert-characters")) + const entityKey = currentContentState.createEntity("AC_ITEM", 'IMMUTABLE').getLastCreatedEntityKey(); + const contentState = Modifier.replaceText(editorStateRef.current!.getCurrentContent(), + newSelection, + item.value, + editorStateRef.current!.getCurrentInlineStyle(), + entityKey) + const newEditorState = EditorState.push(editorStateRef.current!, contentState, "insert-characters") + if (currentAutocompleteRef.current!.insertSpaceAfter === false) { + handleChange(newEditorState) + } else { + const addSpaceState = Modifier.insertText(newEditorState.getCurrentContent(), + newEditorState.getSelection(), " ", + newEditorState.getCurrentInlineStyle()) + handleChange(EditorState.push(newEditorState, addSpaceState, "insert-characters")) + } } } handleAutocompleteClosed() @@ -476,7 +494,7 @@ const MUIRichTextEditor: RefForwardingComponent = if (!block) { return } - const newEditorState = insertAtomicBlock(block.name.toUpperCase(), data, { + const newEditorState = insertAtomicBlock(editorState, block.name.toUpperCase(), data, { selection: editorState.getCurrentContent().getSelectionAfter() }) updateStateForPopover(newEditorState) @@ -711,7 +729,7 @@ const MUIRichTextEditor: RefForwardingComponent = updateStateForPopover(EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter())) } else { - const newEditorState = insertAtomicBlock("IMAGE", data) + const newEditorState = insertAtomicBlock(editorState, "IMAGE", data) updateStateForPopover(EditorState.forceSelection(newEditorState, newEditorState.getCurrentContent().getSelectionAfter())) } setFocusMediaKey("") @@ -794,7 +812,7 @@ const MUIRichTextEditor: RefForwardingComponent = return null } - const insertAtomicBlock = (type: string, data: any, options?: any) => { + const insertAtomicBlock = (editorState: EditorState, type: string, data: any, options?: any) => { const contentState = editorState.getCurrentContent() const contentStateWithEntity = contentState.createEntity(type, 'IMMUTABLE', data) const entityKey = contentStateWithEntity.getLastCreatedEntityKey() diff --git a/src/components/Autocomplete.tsx b/src/components/Autocomplete.tsx index 3a47ec6..c422579 100644 --- a/src/components/Autocomplete.tsx +++ b/src/components/Autocomplete.tsx @@ -6,7 +6,7 @@ import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles' export type TAutocompleteItem = { keys: string[] - value: string + value: any content: string | JSX.Element }