[WEB-986] fix: editor slash command positioning (#4186)

* fix: table selected cell border

* chore: add syncing message when revalidating page data

* fix: slash command positioning

* fix: mentions list dropdown positioning
This commit is contained in:
Aaryan Khandelwal 2024-04-15 19:45:03 +05:30 committed by GitHub
parent 7a21855ab6
commit 8454e4f1e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 36 additions and 35 deletions

View File

@ -49,15 +49,12 @@
}
}
/* Custom list item styles */
/* Custom gap cursor styles */
.ProseMirror-gapcursor::after {
border-top: 1px solid rgb(var(--color-text-100)) !important;
}
/* Custom TODO list checkboxes shoutout to this awesome tutorial: https://moderncss.dev/pure-css-custom-checkbox-style/ */
/* to-do list */
ul[data-type="taskList"] li {
font-size: 1rem;
line-height: 1.5;
@ -143,6 +140,7 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
text-decoration: line-through;
text-decoration-thickness: 2px;
}
/* end to-do list */
/* Overwrite tippy-box original max-width */
@ -238,7 +236,7 @@ div[data-type="horizontalRule"] {
margin-bottom: 0;
& > div {
border-bottom: 1px solid rgb(var(--color-text-100));
border-bottom: 1px solid rgb(var(--color-border-200));
}
}
@ -277,7 +275,7 @@ span:focus .fake-cursor {
z-index: 1;
}
/* number, bulleted and to-do lists */
/* numbered, bulleted and to-do lists spacing */
.prose ol:where(.prose > :first-child):not(:where([class~="not-prose"], [class~="not-prose"] *)),
.prose
ul:not([data-type="taskList"]):where(.prose > :first-child):not(:where([class~="not-prose"], [class~="not-prose"] *)),
@ -306,7 +304,7 @@ ul:not([data-type="taskList"]) ol {
ul[data-type="taskList"] ul[data-type="taskList"] {
margin-top: 0.6rem;
}
/* end number, bulleted and to-do lists */
/* end numbered, bulleted and to-do lists spacing */
/* tailwind typography */
.prose :where(h1):not(:where([class~="not-prose"], [class~="not-prose"] *)) {

View File

@ -31,17 +31,6 @@
}
}
.table-wrapper table td > *,
.table-wrapper table th > * {
margin: 0 !important;
padding: 0.25rem 0 !important;
}
.table-wrapper table td.has-focus,
.table-wrapper table th.has-focus {
box-shadow: rgba(var(--color-primary-300), 0.1) 0px 0px 0px 2px inset !important;
}
.table-wrapper table th {
font-weight: 500;
text-align: left;
@ -49,7 +38,7 @@
}
.table-wrapper table .selectedCell {
border-color: rgba(var(--color-primary-100));
outline: 0.5px solid rgba(var(--color-primary-100));
}
/* table dropdown */

View File

@ -69,7 +69,7 @@ export const CoreEditorExtensions = (
CustomQuoteExtension,
CustomHorizontalRule.configure({
HTMLAttributes: {
class: "my-4",
class: "my-4 border-custom-border-400",
},
}),
CustomKeymap,

View File

@ -135,7 +135,7 @@ export const MentionList = forwardRef((props: MentionListProps, ref) => {
return (
<div
ref={commandListContainer}
className="mentions absolute max-h-48 min-w-[12rem] rounded-md bg-custom-background-100 border-[0.5px] border-custom-border-300 px-2 py-2.5 text-xs shadow-custom-shadow-rg overflow-y-scroll"
className="mentions max-h-48 min-w-[12rem] rounded-md bg-custom-background-100 border-[0.5px] border-custom-border-300 px-2 py-2.5 text-xs shadow-custom-shadow-rg overflow-y-scroll"
>
{isLoading ? (
<div className="text-center text-custom-text-400">Loading...</div>

View File

@ -247,14 +247,15 @@ export const updateScrollView = (container: HTMLElement, item: HTMLElement) => {
};
const CommandList = ({ items, command }: { items: CommandItemProps[]; command: any; editor: any; range: any }) => {
// states
const [selectedIndex, setSelectedIndex] = useState(0);
// refs
const commandListContainer = useRef<HTMLDivElement>(null);
const selectItem = useCallback(
(index: number) => {
const item = items[index];
if (item) {
command(item);
}
if (item) command(item);
},
[command, items]
);
@ -289,8 +290,6 @@ const CommandList = ({ items, command }: { items: CommandItemProps[]; command: a
setSelectedIndex(0);
}, [items]);
const commandListContainer = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
const container = commandListContainer?.current;
@ -299,29 +298,31 @@ const CommandList = ({ items, command }: { items: CommandItemProps[]; command: a
if (item && container) updateScrollView(container, item);
}, [selectedIndex]);
return items.length > 0 ? (
if (items.length <= 0) return null;
return (
<div
id="slash-command"
ref={commandListContainer}
className="fixed z-50 h-auto max-h-[330px] w-52 overflow-y-auto rounded-md border border-custom-border-300 bg-custom-background-100 px-1 py-2 shadow-md transition-all"
className="z-10 max-h-80 min-w-[12rem] overflow-y-auto rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 shadow-custom-shadow-rg"
>
{items.map((item, index) => (
<button
key={item.key}
className={cn(
`flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-sm text-custom-text-100 hover:bg-custom-background-80`,
"flex items-center gap-2 w-full rounded px-1 py-1.5 text-sm text-left truncate text-custom-text-200 hover:bg-custom-background-80",
{
"bg-custom-background-80": index === selectedIndex,
}
)}
onClick={() => selectItem(index)}
>
<div className="grid flex-shrink-0 place-items-center">{item.icon}</div>
<p>{item.title}</p>
<span className="grid place-items-center flex-shrink-0">{item.icon}</span>
<p className="flex-grow truncate">{item.title}</p>
</button>
))}
</div>
) : null;
);
};
interface CommandListInstance {
@ -338,10 +339,12 @@ const renderItems = () => {
editor: props.editor,
});
const tippyContainer = document.querySelector(".active-editor") ?? document.querySelector("#editor-container");
// @ts-expect-error Tippy overloads are messed up
popup = tippy("body", {
getReferenceClientRect: props.clientRect,
appendTo: () => document.querySelector(".active-editor") ?? document.querySelector("#editor-container"),
appendTo: tippyContainer,
content: component.element,
showOnCreate: true,
interactive: true,

View File

@ -19,13 +19,14 @@ import { IPageStore } from "@/store/pages/page.store";
type Props = {
editorRef: React.RefObject<EditorRefApi>;
handleDuplicatePage: () => void;
isSyncing: boolean;
pageStore: IPageStore;
projectId: string;
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
};
export const PageExtraOptions: React.FC<Props> = observer((props) => {
const { editorRef, handleDuplicatePage, pageStore, projectId, readOnlyEditorRef } = props;
const { editorRef, handleDuplicatePage, isSyncing, pageStore, projectId, readOnlyEditorRef } = props;
// states
const [gptModalOpen, setGptModal] = useState(false);
// store hooks
@ -52,6 +53,12 @@ export const PageExtraOptions: React.FC<Props> = observer((props) => {
<span className="text-sm text-custom-text-300">{isSubmitting === "submitting" ? "Saving..." : "Saved"}</span>
</div>
)}
{isSyncing && (
<div className="flex items-center gap-x-2">
<RefreshCw className="h-4 w-4 stroke-custom-text-300" />
<span className="text-sm text-custom-text-300">Syncing...</span>
</div>
)}
{is_locked && (
<div className="flex h-7 items-center gap-2 rounded-full bg-custom-background-80 px-3 py-0.5 text-xs font-medium text-custom-text-300">
<Lock className="h-3 w-3" />

View File

@ -11,6 +11,7 @@ type Props = {
editorRef: React.RefObject<EditorRefApi>;
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
handleDuplicatePage: () => void;
isSyncing: boolean;
markings: IMarking[];
pageStore: IPageStore;
projectId: string;
@ -28,6 +29,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
markings,
readOnlyEditorReady,
handleDuplicatePage,
isSyncing,
pageStore,
projectId,
sidePeekVisible,
@ -61,6 +63,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
<PageExtraOptions
editorRef={editorRef}
handleDuplicatePage={handleDuplicatePage}
isSyncing={isSyncing}
pageStore={pageStore}
projectId={projectId}
readOnlyEditorRef={readOnlyEditorRef}

View File

@ -46,7 +46,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
});
// fetching page details
const { data: swrPageDetails } = useSWR(
const { data: swrPageDetails, isValidating } = useSWR(
pageId ? `PAGE_DETAILS_${pageId}` : null,
pageId ? () => getPageById(pageId.toString()) : null,
{
@ -120,6 +120,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
editorReady={editorReady}
readOnlyEditorReady={readOnlyEditorReady}
handleDuplicatePage={handleDuplicatePage}
isSyncing={isValidating}
markings={markings}
pageStore={pageStore}
projectId={projectId.toString()}