-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(text editor mentions): prototype trigger plugin with querystring…
… builder
- Loading branch information
1 parent
6e6fac5
commit 41035ce
Showing
4 changed files
with
282 additions
and
2 deletions.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
src/components/text-editor/prosemirror-adapter/mentions/mark-schema-extender.ts
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,17 @@ | ||
import { MarkSpec } from 'prosemirror-model'; | ||
|
||
export const mentionTagMark: MarkSpec = { | ||
attrs: { | ||
type: { default: 'mention' }, | ||
}, | ||
inclusive: false, | ||
parseDOM: [ | ||
{ | ||
tag: 'span[data-type]', | ||
getAttrs: (dom) => ({ | ||
type: dom.getAttribute('data-type'), | ||
}), | ||
}, | ||
], | ||
toDOM: (node) => ['span', { 'data-type': node.attrs.type }, 0], | ||
}; |
35 changes: 35 additions & 0 deletions
35
src/components/text-editor/prosemirror-adapter/mentions/node-schema-extender.ts
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,35 @@ | ||
import { NodeSpec } from 'prosemirror-model'; | ||
|
||
export const mention: NodeSpec = { | ||
inline: true, | ||
group: 'inline', | ||
selectable: true, | ||
atom: true, // Makes the node behave as a single unit | ||
|
||
attrs: { | ||
type: {}, | ||
id: {}, | ||
name: {}, | ||
}, | ||
|
||
toDOM: (node) => [ | ||
'span', | ||
{ | ||
class: 'mention', | ||
'data-type': node.attrs.type, | ||
'data-id': node.attrs.id, | ||
'data-name': node.attrs.name, | ||
}, | ||
'@' + node.attrs.name, | ||
], | ||
parseDOM: [ | ||
{ | ||
tag: 'span[data-type][data-id][data-name]', | ||
getAttrs: (dom) => ({ | ||
type: dom.getAttribute('data-type'), | ||
id: dom.getAttribute('data-id'), | ||
name: dom.getAttribute('data-name'), | ||
}), | ||
}, | ||
], | ||
}; |
125 changes: 125 additions & 0 deletions
125
src/components/text-editor/prosemirror-adapter/plugins/trigger-popup-plugin.ts
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,125 @@ | ||
import { Plugin, PluginKey, EditorState } from 'prosemirror-state'; | ||
import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform'; | ||
import { EditorView } from 'prosemirror-view'; | ||
|
||
export const triggerPluginKey = new PluginKey('triggerPlugin'); | ||
|
||
type Trigger = '@' | '#'; | ||
|
||
const triggers: Trigger[] = ['@', '#']; | ||
|
||
const isTrigger = (key: string): key is Trigger => { | ||
return triggers.includes(key as Trigger); | ||
}; | ||
|
||
const shouldTrigger = (state: EditorState): boolean => { | ||
const { $from, $to } = state.selection; | ||
|
||
const prevPos = $from.pos - 1; | ||
|
||
if (!prevPos) { | ||
return true; | ||
} | ||
|
||
if ($from === $to) { | ||
const prevChar = state.doc.textBetween(prevPos, $from.pos); | ||
|
||
return prevChar === ' '; | ||
} | ||
|
||
return false; | ||
}; | ||
|
||
const openPicker = (view: EditorView) => { | ||
const event = new CustomEvent<void>('open-picker', { | ||
bubbles: true, | ||
composed: true, | ||
}); | ||
view.dom.dispatchEvent(event); | ||
}; | ||
|
||
const closePicker = (view: EditorView) => { | ||
const event = new CustomEvent<void>('close-picker', { | ||
bubbles: true, | ||
composed: true, | ||
}); | ||
view.dom.dispatchEvent(event); | ||
}; | ||
|
||
export const createTriggerPlugin = () => { | ||
let activeTrigger: Trigger | null = null; // Track the active trigger type | ||
let queryString = ''; // queryString to track the input after a trigger | ||
|
||
return new Plugin({ | ||
key: triggerPluginKey, | ||
props: { | ||
handleKeyDown: (view, event) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { $from } = selection; | ||
|
||
if (event.key === 'Escape') { | ||
closePicker(view); | ||
activeTrigger = null; // Reset the active trigger | ||
queryString = ''; // Clear the queryString | ||
|
||
return true; | ||
} | ||
|
||
if (isTrigger(event.key) && shouldTrigger(state)) { | ||
activeTrigger = event.key; | ||
queryString = ''; // Reset queryString when a new trigger starts | ||
openPicker(view); | ||
|
||
return false; | ||
} else if ( | ||
activeTrigger && | ||
(event.key === ' ' || | ||
event.key === 'Enter' || | ||
event.key === 'Tab') | ||
) { | ||
// End of mention/tag | ||
activeTrigger = null; | ||
closePicker(view); | ||
queryString = ''; // Clear the queryString | ||
|
||
return false; | ||
} | ||
|
||
return false; | ||
}, | ||
}, | ||
appendTransaction: (transactions, oldState, newState) => { | ||
Check failure on line 92 in src/components/text-editor/prosemirror-adapter/plugins/trigger-popup-plugin.ts GitHub Actions / Lint
|
||
if (activeTrigger) { | ||
let textAdded = ''; | ||
|
||
transactions.forEach((transaction) => { | ||
if (transaction.docChanged) { | ||
transaction.steps.forEach((step) => { | ||
if ( | ||
step instanceof ReplaceStep || | ||
step instanceof ReplaceAroundStep | ||
) { | ||
const slice = step.slice; | ||
if (slice && slice.size) { | ||
textAdded += slice.content.textBetween( | ||
0, | ||
slice.size, | ||
); | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
if (textAdded) { | ||
queryString += textAdded; | ||
console.log('Captured input:', queryString); | ||
// Update mention/tag suggestions here using the queryString | ||
} | ||
} | ||
|
||
return null; // No new transaction is returned | ||
}, | ||
}); | ||
}; |
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