-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: clashing heading markdown command with mention/suggestion used w…
…ith #.
- Loading branch information
Showing
11 changed files
with
377 additions
and
4 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
demos/src/Examples/HeadingMultipleMention/React/MentionList.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import './MentionList.scss' | ||
|
||
import React, { | ||
forwardRef, useEffect, useImperativeHandle, | ||
useState, | ||
} from 'react' | ||
|
||
export default forwardRef((props, ref) => { | ||
const [selectedIndex, setSelectedIndex] = useState(0) | ||
|
||
const selectItem = index => { | ||
const item = props.items[index] | ||
|
||
if (item) { | ||
props.command({ id: item }) | ||
} | ||
} | ||
|
||
const upHandler = () => { | ||
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length) | ||
} | ||
|
||
const downHandler = () => { | ||
setSelectedIndex((selectedIndex + 1) % props.items.length) | ||
} | ||
|
||
const enterHandler = () => { | ||
selectItem(selectedIndex) | ||
} | ||
|
||
useEffect(() => setSelectedIndex(0), [props.items]) | ||
|
||
useImperativeHandle(ref, () => ({ | ||
onKeyDown: ({ event }) => { | ||
if (event.key === 'ArrowUp') { | ||
upHandler() | ||
return true | ||
} | ||
|
||
if (event.key === 'ArrowDown') { | ||
downHandler() | ||
return true | ||
} | ||
|
||
if (event.key === 'Enter') { | ||
enterHandler() | ||
return true | ||
} | ||
|
||
return false | ||
}, | ||
})) | ||
|
||
return ( | ||
<div className="items"> | ||
{props.items.length | ||
? props.items.map((item, index) => ( | ||
<button | ||
className={`item ${index === selectedIndex ? 'is-selected' : ''}`} | ||
key={index} | ||
onClick={() => selectItem(index)} | ||
> | ||
{item} | ||
</button> | ||
)) | ||
: <div className="item">No result</div> | ||
} | ||
</div> | ||
) | ||
}) |
25 changes: 25 additions & 0 deletions
25
demos/src/Examples/HeadingMultipleMention/React/MentionList.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
.items { | ||
background: #fff; | ||
border-radius: 0.5rem; | ||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0px 10px 20px rgba(0, 0, 0, 0.1); | ||
color: rgba(0, 0, 0, 0.8); | ||
font-size: 0.9rem; | ||
overflow: hidden; | ||
padding: 0.2rem; | ||
position: relative; | ||
} | ||
|
||
.item { | ||
background: transparent; | ||
border: 1px solid transparent; | ||
border-radius: 0.4rem; | ||
display: block; | ||
margin: 0; | ||
padding: 0.2rem 0.4rem; | ||
text-align: left; | ||
width: 100%; | ||
|
||
&.is-selected { | ||
border-color: #000; | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import './styles.scss' | ||
|
||
import Mention from '@tiptap/extension-mention' | ||
import { EditorContent, useEditor } from '@tiptap/react' | ||
import StarterKit from '@tiptap/starter-kit' | ||
import React from 'react' | ||
|
||
import mention from './mention.js' | ||
import pageLink from './pageLink.js' | ||
|
||
export default () => { | ||
const editor = useEditor({ | ||
extensions: [ | ||
StarterKit, | ||
Mention.extend({ name: 'mention' }).configure({ | ||
HTMLAttributes: { | ||
class: 'mention', | ||
}, | ||
suggestion: mention, | ||
}), | ||
Mention.extend({ name: 'page-link' }).configure({ | ||
HTMLAttributes: { | ||
class: 'page-link', | ||
}, | ||
suggestion: pageLink, | ||
}), | ||
], | ||
content: ` | ||
<p>Hi everyone! Don’t forget the daily stand up at 8 AM. Read <span data-type="page-link" data-id="Page about cats"></span></p> | ||
<p><span data-type="mention" data-id="Jennifer Grey"></span> Would you mind to share what you’ve been working on lately? We fear not much happened since Dirty Dancing. | ||
<p><span data-type="mention" data-id="Winona Ryder"></span> <span data-type="mention" data-id="Axl Rose"></span> Let’s go through your most important points quickly.</p> | ||
<p>I have a meeting with <span data-type="mention" data-id="Christina Applegate"></span> and don’t want to come late.</p> | ||
<p>– Thanks, your big boss</p> | ||
`, | ||
}) | ||
|
||
if (!editor) { | ||
return null | ||
} | ||
|
||
return <EditorContent editor={editor} /> | ||
} |
40 changes: 40 additions & 0 deletions
40
demos/src/Examples/HeadingMultipleMention/React/index.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
context('/src/Examples/HeadingMultipleMention/React/', () => { | ||
before(() => { | ||
cy.visit('/src/Examples/HeadingMultipleMention/React/') | ||
}) | ||
beforeEach(() => { | ||
cy.get('.tiptap').then(([{ editor }]) => { | ||
editor.commands.setContent('<p></p>') | ||
}) | ||
}) | ||
|
||
it('selecting element from page link doesn\'t trigger heading', () => { | ||
cy.get('.tiptap') | ||
.type('\n#') | ||
.type('{downarrow}') | ||
.type('{enter}') | ||
.should('contain', 'Dogs page') | ||
.get('h1') | ||
.should('not.exist') | ||
}) | ||
it('page link and user mention works together', () => { | ||
cy.get('.tiptap') | ||
.type('\n#') | ||
.type('{downarrow}') | ||
.type('{enter}') | ||
.should('contain', 'Dogs page') | ||
.type(' @') | ||
.type('{downarrow}') | ||
.type('{enter}') | ||
.should('contain', 'Cyndi Lauper') | ||
.get('h1') | ||
.should('not.exist') | ||
}) | ||
it('can still trigger heading if nothing is selected from mention', () => { | ||
cy.get('.tiptap') | ||
.type('\n# Heading!') | ||
.get('h1') | ||
.should('contain', 'Heading!') | ||
|
||
}) | ||
}) |
98 changes: 98 additions & 0 deletions
98
demos/src/Examples/HeadingMultipleMention/React/mention.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { PluginKey } from '@tiptap/pm/state' | ||
import { ReactRenderer } from '@tiptap/react' | ||
import tippy from 'tippy.js' | ||
|
||
import MentionList from './MentionList.jsx' | ||
|
||
const mentionPluginKey = new PluginKey('mention') | ||
|
||
export default { | ||
pluginKey: mentionPluginKey, | ||
char: '@', | ||
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 component | ||
let popup | ||
|
||
return { | ||
onStart: props => { | ||
component = new ReactRenderer(MentionList, { | ||
props, | ||
editor: props.editor, | ||
}) | ||
|
||
if (!props.clientRect) { | ||
return | ||
} | ||
|
||
popup = tippy('body', { | ||
getReferenceClientRect: props.clientRect, | ||
appendTo: () => document.body, | ||
content: component.element, | ||
showOnCreate: true, | ||
interactive: true, | ||
trigger: 'manual', | ||
placement: 'bottom-start', | ||
}) | ||
}, | ||
|
||
onUpdate(props) { | ||
component.updateProps(props) | ||
|
||
if (!props.clientRect) { | ||
return | ||
} | ||
|
||
popup[0].setProps({ | ||
getReferenceClientRect: props.clientRect, | ||
}) | ||
}, | ||
|
||
onKeyDown(props) { | ||
if (props.event.key === 'Escape') { | ||
popup[0].hide() | ||
|
||
return true | ||
} | ||
|
||
return component.ref?.onKeyDown(props) | ||
}, | ||
|
||
onExit() { | ||
popup[0].destroy() | ||
component.destroy() | ||
}, | ||
} | ||
}, | ||
} |
76 changes: 76 additions & 0 deletions
76
demos/src/Examples/HeadingMultipleMention/React/pageLink.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { PluginKey } from '@tiptap/pm/state' | ||
import { ReactRenderer } from '@tiptap/react' | ||
import tippy from 'tippy.js' | ||
|
||
import MentionList from './MentionList.jsx' | ||
|
||
const pageLinkKey = new PluginKey('pageLink') | ||
|
||
export default { | ||
char: '#', | ||
pluginKey: pageLinkKey, | ||
items: ({ query }) => { | ||
return [ | ||
'Page about cats', | ||
'Dogs page', | ||
'List of weird animals', | ||
] | ||
.filter(item => item.toLowerCase().startsWith(query.toLowerCase())) | ||
.slice(0, 5) | ||
}, | ||
|
||
render: () => { | ||
let component | ||
let popup | ||
|
||
return { | ||
onStart: props => { | ||
component = new ReactRenderer(MentionList, { | ||
props, | ||
editor: props.editor, | ||
}) | ||
|
||
if (!props.clientRect) { | ||
return | ||
} | ||
|
||
popup = tippy('body', { | ||
getReferenceClientRect: props.clientRect, | ||
appendTo: () => document.body, | ||
content: component.element, | ||
showOnCreate: true, | ||
interactive: true, | ||
trigger: 'manual', | ||
placement: 'bottom-start', | ||
}) | ||
}, | ||
|
||
onUpdate(props) { | ||
component.updateProps(props) | ||
|
||
if (!props.clientRect) { | ||
return | ||
} | ||
|
||
popup[0].setProps({ | ||
getReferenceClientRect: props.clientRect, | ||
}) | ||
}, | ||
|
||
onKeyDown(props) { | ||
if (props.event.key === 'Escape') { | ||
popup[0].hide() | ||
|
||
return true | ||
} | ||
|
||
return component.ref?.onKeyDown(props) | ||
}, | ||
|
||
onExit() { | ||
popup[0].destroy() | ||
component.destroy() | ||
}, | ||
} | ||
}, | ||
} |
12 changes: 12 additions & 0 deletions
12
demos/src/Examples/HeadingMultipleMention/React/styles.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
.tiptap { | ||
> * + * { | ||
margin-top: 0.75em; | ||
} | ||
} | ||
|
||
.mention,.page-link{ | ||
border: 1px solid #000; | ||
border-radius: 0.4rem; | ||
box-decoration-break: clone; | ||
padding: 0.1rem 0.3rem; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.