[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:
Henit Chobisa 2023-11-02 19:31:25 +05:30 committed by GitHub
parent a9b72fa1d2
commit da391064aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 119 deletions

View File

@ -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;

View File

@ -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>
)
}
);
};

View File

@ -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),

View File

@ -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}

View File

@ -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));
}