Work on mentions suggestions
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
dadaadcdde
commit
2833bf4458
|
@ -35,8 +35,6 @@ const StatePlugin = ({ composeId }: { composeId: string }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [editor] = useLexicalComposerContext();
|
const [editor] = useLexicalComposerContext();
|
||||||
|
|
||||||
(window as any).xd = editor;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
editor.registerUpdateListener(({ editorState }) => {
|
editor.registerUpdateListener(({ editorState }) => {
|
||||||
dispatch(setEditorState(composeId, editorState.isEmpty() ? null : JSON.stringify(editorState.toJSON())));
|
dispatch(setEditorState(composeId, editorState.isEmpty() ? null : JSON.stringify(editorState.toJSON())));
|
||||||
|
|
|
@ -103,19 +103,6 @@ const scrollIntoViewIfNeeded = (target: HTMLElement) => {
|
||||||
target.scrollIntoView({ block: 'nearest' });
|
target.scrollIntoView({ block: 'nearest' });
|
||||||
};
|
};
|
||||||
|
|
||||||
function getTextUpToAnchor(selection: RangeSelection): string | null {
|
|
||||||
const anchor = selection.anchor;
|
|
||||||
if (!['mention', 'text'].includes(anchor.type)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const anchorNode = anchor.getNode();
|
|
||||||
if (anchor.type === 'text' && !anchorNode.isSimpleText()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const anchorOffset = anchor.offset;
|
|
||||||
return anchorNode.getTextContent().slice(0, anchorOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryToPositionRange(leadOffset: number, range: Range): boolean {
|
function tryToPositionRange(leadOffset: number, range: Range): boolean {
|
||||||
const domSelection = window.getSelection();
|
const domSelection = window.getSelection();
|
||||||
if (domSelection === null || !domSelection.isCollapsed) {
|
if (domSelection === null || !domSelection.isCollapsed) {
|
||||||
|
@ -140,15 +127,12 @@ function tryToPositionRange(leadOffset: number, range: Range): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQueryTextForSearch(editor: LexicalEditor): string | null {
|
function getQueryTextForSearch(editor: LexicalEditor): string | null {
|
||||||
let text = null;
|
const state = editor.getEditorState();
|
||||||
editor.getEditorState().read(() => {
|
const node = (state._selection as RangeSelection)?.anchor?.getNode();
|
||||||
const selection = $getSelection();
|
|
||||||
if (!$isRangeSelection(selection)) {
|
if (node && node.getType() === 'mention') return node.getTextContent();
|
||||||
return;
|
|
||||||
}
|
return null;
|
||||||
text = getTextUpToAnchor(selection);
|
|
||||||
});
|
|
||||||
return text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,10 +157,7 @@ function getFullMatchOffset(
|
||||||
* Split Lexical TextNode and return a new TextNode only containing matched text.
|
* Split Lexical TextNode and return a new TextNode only containing matched text.
|
||||||
* Common use cases include: removing the node, replacing with a new node.
|
* Common use cases include: removing the node, replacing with a new node.
|
||||||
*/
|
*/
|
||||||
function splitNodeContainingQuery(
|
function splitNodeContainingQuery(match: QueryMatch): TextNode | null {
|
||||||
editor: LexicalEditor,
|
|
||||||
match: QueryMatch,
|
|
||||||
): TextNode | null {
|
|
||||||
const selection = $getSelection();
|
const selection = $getSelection();
|
||||||
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -369,10 +350,7 @@ function LexicalPopoverMenu<TOption extends TypeaheadOption>({
|
||||||
const selectOptionAndCleanUp = useCallback(
|
const selectOptionAndCleanUp = useCallback(
|
||||||
(selectedEntry: TOption) => {
|
(selectedEntry: TOption) => {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
const textNodeContainingQuery = splitNodeContainingQuery(
|
const textNodeContainingQuery = splitNodeContainingQuery(resolution.match);
|
||||||
editor,
|
|
||||||
resolution.match,
|
|
||||||
);
|
|
||||||
|
|
||||||
onSelectOption(
|
onSelectOption(
|
||||||
selectedEntry,
|
selectedEntry,
|
||||||
|
|
|
@ -228,3 +228,99 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.typeahead-popover {
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover ul::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover ul {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover ul li {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 180px;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover ul li.selected {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li {
|
||||||
|
margin: 0 8px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #050505;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 16px;
|
||||||
|
font-size: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li.active {
|
||||||
|
display: flex;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li:first-child {
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li:last-child {
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li .text {
|
||||||
|
display: flex;
|
||||||
|
line-height: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeahead-popover li .icon {
|
||||||
|
display: flex;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
user-select: none;
|
||||||
|
margin-right: 8px;
|
||||||
|
line-height: 16px;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mentions-menu {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue