diff --git a/docs/src/components/DemoMixin/index.js b/docs/src/components/DemoMixin/index.js
index 019a80b1..54bbe6d2 100644
--- a/docs/src/components/DemoMixin/index.js
+++ b/docs/src/components/DemoMixin/index.js
@@ -86,7 +86,7 @@ export default {
.filter(item => {
return ['vue', 'ts', 'js', 'jsx', 'scss'].includes(item.extension)
})
- .sortBy(item => item.path.split('/').length && !item.path.endsWith('index.vue'))
+ .sortBy(item => item.path.split('/').length && !item.path.endsWith('index.vue') && !item.path.endsWith('index.jsx'))
.toArray()
},
}
diff --git a/docs/src/demos/Examples/Community/React/MentionList.jsx b/docs/src/demos/Examples/Community/React/MentionList.jsx
new file mode 100644
index 00000000..630485ea
--- /dev/null
+++ b/docs/src/demos/Examples/Community/React/MentionList.jsx
@@ -0,0 +1,79 @@
+import React from 'react'
+import './MentionList.scss'
+
+export class MentionList extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ selectedIndex: 0,
+ }
+ }
+
+ componentDidUpdate(oldProps) {
+ if (this.props.items !== oldProps.items) {
+ this.setState({
+ selectedIndex: 0
+ })
+ }
+ }
+
+ onKeyDown({ event }) {
+ if (event.key === 'ArrowUp') {
+ this.upHandler()
+ return true
+ }
+
+ if (event.key === 'ArrowDown') {
+ this.downHandler()
+ return true
+ }
+
+ if (event.key === 'Enter') {
+ this.enterHandler()
+ return true
+ }
+
+ return false
+ }
+
+ upHandler() {
+ this.setState({
+ selectedIndex: ((this.state.selectedIndex + this.props.items.length) - 1) % this.props.items.length
+ })
+ }
+
+ downHandler() {
+ this.setState({
+ selectedIndex: (this.state.selectedIndex + 1) % this.props.items.length
+ })
+ }
+
+ enterHandler() {
+ this.selectItem(this.state.selectedIndex)
+ }
+
+ selectItem(index) {
+ const item = this.props.items[index]
+
+ if (item) {
+ this.props.command({ id: item })
+ }
+ }
+
+ render() {
+ return (
+
+ {this.props.items.map((item, index) => (
+
+ ))}
+
+ )
+ }
+}
diff --git a/docs/src/demos/Examples/Community/React/MentionList.scss b/docs/src/demos/Examples/Community/React/MentionList.scss
new file mode 100644
index 00000000..e909db98
--- /dev/null
+++ b/docs/src/demos/Examples/Community/React/MentionList.scss
@@ -0,0 +1,27 @@
+.items {
+ position: relative;
+ border-radius: 0.25rem;
+ background: white;
+ color: rgba(black, 0.8);
+ overflow: hidden;
+ font-size: 0.9rem;
+ box-shadow:
+ 0 0 0 1px rgba(0, 0, 0, 0.1),
+ 0px 10px 20px rgba(0, 0, 0, 0.1),
+ ;
+}
+
+.item {
+ display: block;
+ width: 100%;
+ text-align: left;
+ background: transparent;
+ border: none;
+ padding: 0.2rem 0.5rem;
+
+ &.is-selected,
+ &:hover {
+ color: #A975FF;
+ background: rgba(#A975FF, 0.1);
+ }
+}
diff --git a/docs/src/demos/Examples/Community/React/index.jsx b/docs/src/demos/Examples/Community/React/index.jsx
new file mode 100644
index 00000000..05851737
--- /dev/null
+++ b/docs/src/demos/Examples/Community/React/index.jsx
@@ -0,0 +1,126 @@
+import React from 'react'
+import tippy from 'tippy.js'
+import { useEditor, EditorContent, ReactRenderer } from '@tiptap/react'
+import Document from '@tiptap/extension-document'
+import Paragraph from '@tiptap/extension-paragraph'
+import Text from '@tiptap/extension-text'
+import CharacterCount from '@tiptap/extension-character-count'
+import Mention from '@tiptap/extension-mention'
+import { MentionList } from './MentionList'
+import './styles.scss'
+
+export default () => {
+ const limit = 280
+
+ const editor = useEditor({
+ extensions: [
+ Document,
+ Paragraph,
+ Text,
+ CharacterCount.configure({
+ limit,
+ }),
+ Mention.configure({
+ HTMLAttributes: {
+ class: 'mention',
+ },
+ suggestion: {
+ items: query => {
+ return [
+ 'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
+ ].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
+ },
+ render: () => {
+ let reactRenderer
+ let popup
+
+ return {
+ onStart: props => {
+ reactRenderer = new ReactRenderer(MentionList, {
+ props,
+ editor: props.editor,
+ })
+
+ popup = tippy('body', {
+ getReferenceClientRect: props.clientRect,
+ appendTo: () => document.body,
+ content: reactRenderer.element,
+ showOnCreate: true,
+ interactive: true,
+ trigger: 'manual',
+ placement: 'bottom-start',
+ })
+ },
+ onUpdate(props) {
+ reactRenderer.updateProps(props)
+
+ popup[0].setProps({
+ getReferenceClientRect: props.clientRect,
+ })
+ },
+ onKeyDown(props) {
+ return reactRenderer.ref.onKeyDown(props)
+ },
+ onExit() {
+ popup[0].destroy()
+ reactRenderer.destroy()
+ },
+ }
+ }
+ },
+ })
+ ],
+ content: `
+
+ What do you all think about the new movie?
+
+ `,
+ })
+
+ const percentage = editor
+ ? Math.round((100 / limit) * editor.getCharacterCount())
+ : 0
+
+ return (
+
+
+ {editor &&
+
+
+
+
+ {editor.getCharacterCount()}/{limit} characters
+
+
+ }
+
+ )
+}
diff --git a/docs/src/demos/Examples/Community/React/styles.scss b/docs/src/demos/Examples/Community/React/styles.scss
new file mode 100644
index 00000000..d422be26
--- /dev/null
+++ b/docs/src/demos/Examples/Community/React/styles.scss
@@ -0,0 +1,41 @@
+/* Basic editor styles */
+.ProseMirror {
+ > * + * {
+ margin-top: 0.75em;
+ }
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ line-height: 1.1;
+ }
+}
+
+.mention {
+ color: #A975FF;
+ background-color: rgba(#A975FF, 0.1);
+ border-radius: 0.3rem;
+ padding: 0.1rem 0.3rem;
+}
+
+.character-count {
+ margin-top: 1rem;
+ display: flex;
+ align-items: center;
+ color: #68CEF8;
+
+ &--warning {
+ color: #FB5151;
+ }
+
+ &__graph {
+ margin-right: 0.5rem;
+ }
+
+ &__text {
+ color: #868e96;
+ }
+}
diff --git a/docs/src/demos/Examples/Community/MentionList.vue b/docs/src/demos/Examples/Community/Vue/MentionList.vue
similarity index 100%
rename from docs/src/demos/Examples/Community/MentionList.vue
rename to docs/src/demos/Examples/Community/Vue/MentionList.vue
diff --git a/docs/src/demos/Examples/Community/Vue/index.spec.js b/docs/src/demos/Examples/Community/Vue/index.spec.js
new file mode 100644
index 00000000..c65e99f3
--- /dev/null
+++ b/docs/src/demos/Examples/Community/Vue/index.spec.js
@@ -0,0 +1,7 @@
+context('/demos/Examples/Community/Vue', () => {
+ before(() => {
+ cy.visit('/demos/Examples/Community/Vue')
+ })
+
+ // TODO: Write tests
+})
diff --git a/docs/src/demos/Examples/Community/index.vue b/docs/src/demos/Examples/Community/Vue/index.vue
similarity index 99%
rename from docs/src/demos/Examples/Community/index.vue
rename to docs/src/demos/Examples/Community/Vue/index.vue
index 7aec67da..ddbf177f 100644
--- a/docs/src/demos/Examples/Community/index.vue
+++ b/docs/src/demos/Examples/Community/Vue/index.vue
@@ -79,7 +79,7 @@ export default {
items: query => {
return [
'Lea Thompson', 'Cyndi Lauper', 'Tom Cruise', 'Madonna', 'Jerry Hall', 'Joan Collins', 'Winona Ryder', 'Christina Applegate', 'Alyssa Milano', 'Molly Ringwald', 'Ally Sheedy', 'Debbie Harry', 'Olivia Newton-John', 'Elton John', 'Michael J. Fox', 'Axl Rose', 'Emilio Estevez', 'Ralph Macchio', 'Rob Lowe', 'Jennifer Grey', 'Mickey Rourke', 'John Cusack', 'Matthew Broderick', 'Justine Bateman', 'Lisa Bonet',
- ].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 10)
+ ].filter(item => item.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5)
},
render: () => {
let component
diff --git a/docs/src/demos/Examples/Community/index.spec.js b/docs/src/demos/Examples/Community/index.spec.js
deleted file mode 100644
index f0a0ab4f..00000000
--- a/docs/src/demos/Examples/Community/index.spec.js
+++ /dev/null
@@ -1,7 +0,0 @@
-context('/demos/Examples/Community', () => {
- before(() => {
- cy.visit('/demos/Examples/Community')
- })
-
- // TODO: Write tests
-})
diff --git a/docs/src/docPages/examples/community.md b/docs/src/docPages/examples/community.md
index 3b0233ae..bcc957d8 100644
--- a/docs/src/docPages/examples/community.md
+++ b/docs/src/docPages/examples/community.md
@@ -1,3 +1,7 @@
# Suggestions
-
+## Vue
+
+
+## React
+
diff --git a/docs/src/templates/DemoPage/style.scss b/docs/src/templates/DemoPage/style.scss
index 6ede4df0..4609b200 100644
--- a/docs/src/templates/DemoPage/style.scss
+++ b/docs/src/templates/DemoPage/style.scss
@@ -1,3 +1,4 @@
.demo-page {
padding: 1.25rem;
+ min-height: 12rem;
}