Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 148 additions & 98 deletions components/command-palette/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import useTheme from "lib/hooks/useTheme";
import useToast from "lib/hooks/useToast";
// icons
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
import { DocumentPlusIcon, FolderPlusIcon, FolderIcon } from "@heroicons/react/24/outline";
import {
FolderIcon,
RectangleStackIcon,
ClipboardDocumentListIcon,
} from "@heroicons/react/24/outline";
// commons
import { classNames, copyTextToClipboard } from "constants/common";
// components
Expand All @@ -19,12 +23,19 @@ import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssue
import CreateUpdateCycleModal from "components/project/cycles/CreateUpdateCyclesModal";
// types
import { IIssue } from "types";
import { Button } from "ui";
import { SubmitHandler, useForm } from "react-hook-form";

type ItemType = {
name: string;
url?: string;
onClick?: () => void;
};

type FormInput = {
issue: string[];
};

const CommandPalette: React.FC = () => {
const router = useRouter();

Expand All @@ -51,15 +62,15 @@ const CommandPalette: React.FC = () => {
const quickActions = [
{
name: "Add new issue...",
icon: DocumentPlusIcon,
icon: RectangleStackIcon,
shortcut: "I",
onClick: () => {
setIsIssueModalOpen(true);
},
},
{
name: "Add new project...",
icon: FolderPlusIcon,
icon: ClipboardDocumentListIcon,
shortcut: "P",
onClick: () => {
setIsProjectModalOpen(true);
Expand Down Expand Up @@ -116,6 +127,23 @@ const CommandPalette: React.FC = () => {
[toggleCollapsed, setToastAlert, router]
);

const {
register,
formState: { errors, isSubmitting },
handleSubmit,
reset,
setError,
control,
} = useForm<FormInput>();

const handleDelete: SubmitHandler<FormInput> = (data) => {
console.log("Deleting... " + JSON.stringify(data));
};

const handleAddToCycle: SubmitHandler<FormInput> = (data) => {
console.log("Adding to cycle...");
};

useEffect(() => {
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
Expand All @@ -137,7 +165,6 @@ const CommandPalette: React.FC = () => {
setIsOpen={setIsIssueModalOpen}
projectId={activeProject?.id}
/>

<Transition.Root
show={isPaletteOpen}
as={React.Fragment}
Expand Down Expand Up @@ -168,129 +195,152 @@ const CommandPalette: React.FC = () => {
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 overflow-hidden rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
<Combobox
onChange={(item: ItemType) => {
const { url, onClick } = item;
if (url) router.push(url);
else if (onClick) onClick();
handleCommandPaletteClose();
}}
>
<div className="relative m-1">
<MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm outline-none"
placeholder="Search..."
onChange={(event) => setQuery(event.target.value)}
/>
</div>

<Combobox.Options
static
className="max-h-80 scroll-py-2 divide-y divide-gray-500 divide-opacity-10 overflow-y-auto"
<form>
<Combobox
// onChange={(item: ItemType) => {
// const { url, onClick } = item;
// if (url) router.push(url);
// else if (onClick) onClick();
// handleCommandPaletteClose();
// }}
>
{filteredIssues.length > 0 && (
<>
<div className="relative m-1">
<MagnifyingGlassIcon
className="pointer-events-none absolute top-3.5 left-4 h-5 w-5 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<Combobox.Input
className="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm outline-none"
placeholder="Search..."
onChange={(event) => setQuery(event.target.value)}
/>
</div>

<Combobox.Options
static
className="max-h-80 scroll-py-2 divide-y divide-gray-500 divide-opacity-10 overflow-y-auto"
>
{filteredIssues.length > 0 && (
<>
<li className="p-2">
{query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900">
Issues
</h2>
)}
<ul className="text-sm text-gray-700">
{filteredIssues.map((issue) => (
<Combobox.Option
key={issue.id}
value={{
name: issue.name,
url: `/projects/${issue.project}/issues/${issue.id}`,
}}
className={({ active }) =>
classNames(
"flex cursor-pointer select-none items-center rounded-md px-3 py-2",
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : ""
)
}
>
{({ active }) => (
<>
{/* <FolderIcon
className={classNames(
"h-6 w-6 flex-none text-gray-900 text-opacity-40",
active ? "text-opacity-100" : ""
)}
aria-hidden="true"
/> */}
<input
type="checkbox"
{...register("issue")}
value={issue.id}
/>
<span className="ml-3 flex-auto truncate">{issue.name}</span>
{active && (
<span className="ml-3 flex-none text-gray-500">
Jump to...
</span>
)}
</>
)}
</Combobox.Option>
))}
</ul>
</li>
</>
)}
{query === "" && (
<li className="p-2">
{query === "" && (
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900">
Issues
</h2>
)}
<h2 className="sr-only">Quick actions</h2>
<ul className="text-sm text-gray-700">
{filteredIssues.map((issue) => (
{quickActions.map((action) => (
<Combobox.Option
key={issue.id}
key={action.shortcut}
value={{
name: issue.name,
url: `/projects/${issue.project}/issues/${issue.id}`,
name: action.name,
onClick: action.onClick,
}}
className={({ active }) =>
classNames(
"flex cursor-pointer select-none items-center rounded-md px-3 py-2",
"flex cursor-default select-none items-center rounded-md px-3 py-2",
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : ""
)
}
>
{({ active }) => (
<>
<FolderIcon
<action.icon
className={classNames(
"h-6 w-6 flex-none text-gray-900 text-opacity-40",
active ? "text-opacity-100" : ""
)}
aria-hidden="true"
/>
<span className="ml-3 flex-auto truncate">{issue.name}</span>
{active && (
<span className="ml-3 flex-none text-gray-500">
Jump to...
</span>
)}
<span className="ml-3 flex-auto truncate">{action.name}</span>
<span className="ml-3 flex-none text-xs font-semibold text-gray-500">
<kbd className="font-sans">⌘</kbd>
<kbd className="font-sans">{action.shortcut}</kbd>
</span>
</>
)}
</Combobox.Option>
))}
</ul>
</li>
</>
)}
{query === "" && (
<li className="p-2">
<h2 className="sr-only">Quick actions</h2>
<ul className="text-sm text-gray-700">
{quickActions.map((action) => (
<Combobox.Option
key={action.shortcut}
value={{
name: action.name,
onClick: action.onClick,
}}
className={({ active }) =>
classNames(
"flex cursor-default select-none items-center rounded-md px-3 py-2",
active ? "bg-gray-900 bg-opacity-5 text-gray-900" : ""
)
}
>
{({ active }) => (
<>
<action.icon
className={classNames(
"h-6 w-6 flex-none text-gray-900 text-opacity-40",
active ? "text-opacity-100" : ""
)}
aria-hidden="true"
/>
<span className="ml-3 flex-auto truncate">{action.name}</span>
<span className="ml-3 flex-none text-xs font-semibold text-gray-500">
<kbd className="font-sans">⌘</kbd>
<kbd className="font-sans">{action.shortcut}</kbd>
</span>
</>
)}
</Combobox.Option>
))}
</ul>
</li>
)}
</Combobox.Options>

{query !== "" && filteredIssues.length === 0 && (
<div className="py-14 px-6 text-center sm:px-14">
<FolderIcon
className="mx-auto h-6 w-6 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<p className="mt-4 text-sm text-gray-900">
We couldn{"'"}t find any issue with that term. Please try again.
</p>
</div>
)}
</Combobox.Options>
</Combobox>

{query !== "" && filteredIssues.length === 0 && (
<div className="py-14 px-6 text-center sm:px-14">
<FolderIcon
className="mx-auto h-6 w-6 text-gray-900 text-opacity-40"
aria-hidden="true"
/>
<p className="mt-4 text-sm text-gray-900">
We couldn{"'"}t find any issue with that term. Please try again.
</p>
<div className="flex justify-between items-center gap-2 p-3">
<div className="flex items-center gap-2">
<Button onClick={handleSubmit(handleAddToCycle)} size="sm">
Add to Cycle
</Button>
<Button onClick={handleSubmit(handleDelete)} theme="danger" size="sm">
Delete
</Button>
</div>
<div>
<Button type="button" size="sm" onClick={handleCommandPaletteClose}>
Close
</Button>
</div>
)}
</Combobox>
</div>
</form>
</Dialog.Panel>
</Transition.Child>
</div>
Expand Down
31 changes: 31 additions & 0 deletions components/lexical/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
import { ListItemNode, ListNode } from "@lexical/list";
import { CodeHighlightNode, CodeNode } from "@lexical/code";
import { AutoLinkNode, LinkNode } from "@lexical/link";
// theme
import { defaultTheme } from "./theme";

export const initialConfig = {
namespace: "LexicalEditor",
// The editor theme
theme: defaultTheme,
// Handling of errors during update
onError(error: any) {
console.error(error);
},
// Any custom nodes go here
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode,
],
};
Loading