forked from github/plane
[FIX] Minor bug fixes in MentionList
and MentionNode
UI (#2600)
* fix: removed text color in peek view * fix: fixed list view UI bugs and node view colors * feat: update imports in suggestions for mentionSuggestion type * fix: updated mention list css * fix: updated mention node UI according to the design provided * style: update the mentions dropdown UI * style: mentioned users UI in the editor --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
This commit is contained in:
parent
a9b72fa1d2
commit
da391064aa
@ -1,111 +1,122 @@
|
||||
import { Editor } from '@tiptap/react';
|
||||
import { Editor } from "@tiptap/react";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from 'react'
|
||||
} from "react";
|
||||
|
||||
import { IMentionSuggestion } from '../../types/mention-suggestion';
|
||||
import { IMentionSuggestion } from "../../types/mention-suggestion";
|
||||
|
||||
interface MentionListProps {
|
||||
items: IMentionSuggestion[];
|
||||
command: (item: { id: string, label: string, target: string, redirect_uri: string }) => void;
|
||||
command: (item: {
|
||||
id: string;
|
||||
label: string;
|
||||
target: string;
|
||||
redirect_uri: string;
|
||||
}) => void;
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const MentionList = forwardRef((props: MentionListProps, ref) => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
|
||||
const selectItem = (index: number) => {
|
||||
const item = props.items[index]
|
||||
const item = props.items[index];
|
||||
|
||||
console.log(props.command);
|
||||
|
||||
if (item) {
|
||||
props.command({ id: item.id, label: item.title, target: "users", redirect_uri: item.redirect_uri })
|
||||
props.command({
|
||||
id: item.id,
|
||||
label: item.title,
|
||||
target: "users",
|
||||
redirect_uri: item.redirect_uri,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const upHandler = () => {
|
||||
setSelectedIndex(((selectedIndex + props.items.length) - 1) % props.items.length)
|
||||
}
|
||||
setSelectedIndex(
|
||||
(selectedIndex + props.items.length - 1) % props.items.length,
|
||||
);
|
||||
};
|
||||
|
||||
const downHandler = () => {
|
||||
setSelectedIndex((selectedIndex + 1) % props.items.length)
|
||||
}
|
||||
setSelectedIndex((selectedIndex + 1) % props.items.length);
|
||||
};
|
||||
|
||||
const enterHandler = () => {
|
||||
selectItem(selectedIndex)
|
||||
}
|
||||
selectItem(selectedIndex);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIndex(0)
|
||||
}, [props.items])
|
||||
setSelectedIndex(0);
|
||||
}, [props.items]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
onKeyDown: ({ event }: { event: KeyboardEvent }) => {
|
||||
if (event.key === 'ArrowUp') {
|
||||
upHandler()
|
||||
return true
|
||||
if (event.key === "ArrowUp") {
|
||||
upHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
downHandler()
|
||||
return true
|
||||
if (event.key === "ArrowDown") {
|
||||
downHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
enterHandler()
|
||||
return true
|
||||
if (event.key === "Enter") {
|
||||
enterHandler();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
}))
|
||||
}));
|
||||
|
||||
return (
|
||||
props.items && props.items.length !== 0 ? <div className="items">
|
||||
{ props.items.length ? props.items.map((item, index) => (
|
||||
<div className={`item ${index === selectedIndex ? 'is-selected' : ''} w-72 flex items-center p-3 rounded shadow-md`} onClick={() => selectItem(index)}>
|
||||
{item.avatar ? <div
|
||||
className={`rounded border-[0.5px] ${index ? "border-custom-border-200 bg-custom-background-100" : "border-transparent"
|
||||
}`}
|
||||
style={{
|
||||
height: "24px",
|
||||
width: "24px",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.avatar}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt={item.title}
|
||||
/>
|
||||
</div> :
|
||||
<div
|
||||
className="grid place-items-center text-xs capitalize text-white rounded bg-gray-700 border-[0.5px] border-custom-border-200"
|
||||
style={{
|
||||
height: "24px",
|
||||
width: "24px",
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
{item.title.charAt(0)}
|
||||
</div>
|
||||
}
|
||||
<div className="ml-7 space-y-1">
|
||||
<p className="text-sm font-medium leading-none">{item.title}</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
{item.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
return props.items && props.items.length !== 0 ? (
|
||||
<div className="absolute max-h-40 bg-custom-background-100 rounded-md shadow-custom-shadow-sm text-custom-text-300 text-sm overflow-y-auto w-48 p-1 space-y-0.5">
|
||||
{props.items.length ? (
|
||||
props.items.map((item, index) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className={`flex items-center gap-2 rounded p-1 hover:bg-custom-background-80 cursor-pointer ${
|
||||
index === selectedIndex ? "bg-custom-background-80" : ""
|
||||
}`}
|
||||
onClick={() => selectItem(index)}
|
||||
>
|
||||
<div className="flex-shrink-0 h-4 w-4 grid place-items-center overflow-hidden">
|
||||
{item.avatar && item.avatar.trim() !== "" ? (
|
||||
<img
|
||||
src={item.avatar}
|
||||
className="h-full w-full object-cover rounded-sm"
|
||||
alt={item.title}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-full w-full grid place-items-center text-xs capitalize text-white rounded-sm bg-gray-700">
|
||||
{item.title[0]}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
: <div className="item">No result</div>
|
||||
}
|
||||
</div> : <></>
|
||||
)
|
||||
})
|
||||
<div className="flex-grow space-y-1 truncate">
|
||||
<p className="text-sm font-medium truncate">{item.title}</p>
|
||||
{/* <p className="text-xs text-gray-400">{item.subtitle}</p> */}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="item">No result</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
});
|
||||
|
||||
MentionList.displayName = "MentionList"
|
||||
MentionList.displayName = "MentionList";
|
||||
|
||||
export default MentionList
|
||||
export default MentionList;
|
||||
|
@ -1,32 +1,41 @@
|
||||
/* eslint-disable react/display-name */
|
||||
// @ts-nocheck
|
||||
import { NodeViewWrapper } from '@tiptap/react'
|
||||
import { cn } from '../../lib/utils'
|
||||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { IMentionHighlight } from '../../types/mention-suggestion'
|
||||
import { NodeViewWrapper } from "@tiptap/react";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { useRouter } from "next/router";
|
||||
import { IMentionHighlight } from "../../types/mention-suggestion";
|
||||
|
||||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default props => {
|
||||
|
||||
const router = useRouter()
|
||||
const highlights = props.extension.options.mentionHighlights as IMentionHighlight[]
|
||||
export default (props) => {
|
||||
const router = useRouter();
|
||||
const highlights = props.extension.options
|
||||
.mentionHighlights as IMentionHighlight[];
|
||||
|
||||
const handleClick = () => {
|
||||
if (!props.extension.options.readonly){
|
||||
router.push(props.node.attrs.redirect_uri)
|
||||
if (!props.extension.options.readonly) {
|
||||
router.push(props.node.attrs.redirect_uri);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NodeViewWrapper className="w-fit inline mention-component" >
|
||||
<span className={cn("px-1 py-0.5 inline rounded-md font-bold bg-custom-primary-500 mention", {
|
||||
"text-[#D9C942] bg-[#544D3B] hover:bg-[#544D3B]" : highlights ? highlights.includes(props.node.attrs.id) : false,
|
||||
"cursor-pointer" : !props.extension.options.readonly,
|
||||
"hover:bg-custom-primary-300" : !props.extension.options.readonly && !highlights.includes(props.node.attrs.id)
|
||||
})} onClick={handleClick} data-mention-target={props.node.attrs.target} data-mention-id={props.node.attrs.id}>@{ props.node.attrs.label }</span>
|
||||
<NodeViewWrapper className="w-fit inline mention-component">
|
||||
<span
|
||||
className={cn(
|
||||
"px-1 py-0.5 bg-custom-primary-100/20 text-custom-primary-100 rounded font-medium mention",
|
||||
{
|
||||
"text-yellow-500 bg-yellow-500/20": highlights
|
||||
? highlights.includes(props.node.attrs.id)
|
||||
: false,
|
||||
"cursor-pointer": !props.extension.options.readonly,
|
||||
// "hover:bg-custom-primary-300" : !props.extension.options.readonly && !highlights.includes(props.node.attrs.id)
|
||||
},
|
||||
)}
|
||||
onClick={handleClick}
|
||||
data-mention-target={props.node.attrs.target}
|
||||
data-mention-id={props.node.attrs.id}
|
||||
>
|
||||
@{props.node.attrs.label}
|
||||
</span>
|
||||
</NodeViewWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import { Editor } from "@tiptap/core";
|
||||
import tippy from 'tippy.js'
|
||||
|
||||
import MentionList from './MentionList'
|
||||
import { IMentionSuggestion } from './mentions';
|
||||
import { IMentionSuggestion } from '../../types/mention-suggestion';
|
||||
|
||||
const Suggestion = (suggestions: IMentionSuggestion[]) => ({
|
||||
items: ({ query }: { query: string }) => suggestions.filter(suggestion => suggestion.title.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5),
|
||||
|
@ -140,7 +140,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
||||
</div>
|
||||
<span>{errors.name ? errors.name.message : null}</span>
|
||||
|
||||
<span className="text-black">
|
||||
<span className="">
|
||||
<RichTextEditor
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
|
||||
deleteFile={fileService.deleteImage}
|
||||
|
@ -229,27 +229,3 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
||||
.ProseMirror table * .is-empty::before {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
.items {
|
||||
position: absolute;
|
||||
max-height: 40vh;
|
||||
background: rgb(var(--color-background-100));
|
||||
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: rgb(var(--color-text-100));
|
||||
font-size: 0.9rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.4rem;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item.is-selected {
|
||||
border-color: rgb(var(--color-border-200));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user