Skip to content

Commit 32a31df

Browse files
viharanmolsinghbhatia
authored andcommitted
fix: workspace level toggle position, paddings, and tab navigation (#6580)
* fix: workspace level toggle position, paddings, and tab navigation * chore: platform-specific command icons --------- Co-authored-by: Anmol Singh Bhatia <[email protected]>
1 parent 1587c59 commit 32a31df

File tree

1 file changed

+73
-30
lines changed

1 file changed

+73
-30
lines changed

web/core/components/command-palette/command-modal.tsx

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import { Command } from "cmdk";
55
import { observer } from "mobx-react";
66
import { useParams } from "next/navigation";
77
import useSWR from "swr";
8-
import { FolderPlus, Search, Settings } from "lucide-react";
8+
import { CommandIcon, FolderPlus, Search, Settings } from "lucide-react";
99
import { Dialog, Transition } from "@headlessui/react";
1010
// plane imports
1111
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
1212
import { useTranslation } from "@plane/i18n";
1313
import { IWorkspaceSearchResults } from "@plane/types";
14-
import { LayersIcon, Loader, ToggleSwitch, Tooltip } from "@plane/ui";
14+
import { LayersIcon, Loader, ToggleSwitch } from "@plane/ui";
1515
// components
1616
import {
1717
ChangeIssueAssignee,
@@ -66,13 +66,13 @@ export const CommandModal: React.FC = observer(() => {
6666
page: [],
6767
},
6868
});
69-
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false);
69+
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(true);
7070
const [pages, setPages] = useState<string[]>([]);
7171
// plane hooks
7272
const { t } = useTranslation();
7373
// hooks
7474
const { workspaceProjectIds } = useProject();
75-
const { isMobile } = usePlatformOS();
75+
const { platform, isMobile } = usePlatformOS();
7676
const { canPerformAnyCreateAction } = useUser();
7777
const { isCommandPaletteOpen, toggleCommandPaletteModal, toggleCreateIssueModal, toggleCreateProjectModal } =
7878
useCommandPalette();
@@ -176,22 +176,60 @@ export const CommandModal: React.FC = observer(() => {
176176
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
177177
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
178178
>
179-
<Dialog.Panel className="relative flex w-full max-w-2xl transform items-center justify-center divide-y divide-custom-border-200 divide-opacity-10 rounded-lg bg-custom-background-100 shadow-custom-shadow-md transition-all">
179+
<Dialog.Panel className="relative flex w-full max-w-2xl transform flex-col items-center justify-center divide-y divide-custom-border-200 divide-opacity-10 rounded-lg bg-custom-background-100 shadow-custom-shadow-md transition-all">
180180
<div className="w-full max-w-2xl">
181181
<Command
182182
filter={(value, search) => {
183183
if (value.toLowerCase().includes(search.toLowerCase())) return 1;
184184
return 0;
185185
}}
186-
onKeyDown={(e) => {
187-
// when search term is not empty, esc should clear the search term
188-
if (e.key === "Escape" && searchTerm) setSearchTerm("");
186+
shouldFilter={searchTerm.length > 0}
187+
onKeyDown={(e: any) => {
188+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
189+
e.preventDefault();
190+
e.stopPropagation();
191+
closePalette();
192+
return;
193+
}
194+
195+
if (e.key === "Tab") {
196+
e.preventDefault();
197+
const commandList = document.querySelector("[cmdk-list]");
198+
const items = commandList?.querySelectorAll("[cmdk-item]") || [];
199+
const selectedItem = commandList?.querySelector('[aria-selected="true"]');
200+
if (items.length === 0) return;
201+
202+
const currentIndex = Array.from(items).indexOf(selectedItem as Element);
203+
let nextIndex;
204+
205+
if (e.shiftKey) {
206+
nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
207+
} else {
208+
nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
209+
}
210+
211+
const nextItem = items[nextIndex] as HTMLElement;
212+
if (nextItem) {
213+
nextItem.setAttribute("aria-selected", "true");
214+
selectedItem?.setAttribute("aria-selected", "false");
215+
nextItem.focus();
216+
nextItem.scrollIntoView({
217+
behavior: "smooth",
218+
block: "nearest",
219+
});
220+
}
221+
}
222+
223+
if (e.key === "Escape" && searchTerm) {
224+
e.preventDefault();
225+
setSearchTerm("");
226+
}
189227

190-
// when user tries to close the modal with esc
191-
if (e.key === "Escape" && !page && !searchTerm) closePalette();
228+
if (e.key === "Escape" && !page && !searchTerm) {
229+
e.preventDefault();
230+
closePalette();
231+
}
192232

193-
// Escape goes to previous page
194-
// Backspace goes to previous page when search is empty
195233
if (e.key === "Escape" || (e.key === "Backspace" && !searchTerm)) {
196234
e.preventDefault();
197235
setPages((pages) => pages.slice(0, -1));
@@ -200,7 +238,7 @@ export const CommandModal: React.FC = observer(() => {
200238
}}
201239
>
202240
<div
203-
className={`flex gap-4 p-3 pb-0 sm:items-center ${
241+
className={`flex gap-4 pb-0 sm:items-center ${
204242
issueDetails ? "flex-col justify-between sm:flex-row" : "justify-end"
205243
}`}
206244
>
@@ -216,23 +254,6 @@ export const CommandModal: React.FC = observer(() => {
216254
{issueDetails.name}
217255
</div>
218256
)}
219-
{projectId && (
220-
<Tooltip tooltipContent="Toggle workspace level search" isMobile={isMobile}>
221-
<div className="flex flex-shrink-0 cursor-pointer items-center gap-1 self-end text-xs sm:self-center">
222-
<button
223-
type="button"
224-
onClick={() => setIsWorkspaceLevel((prevData) => !prevData)}
225-
className="flex-shrink-0"
226-
>
227-
Workspace Level
228-
</button>
229-
<ToggleSwitch
230-
value={isWorkspaceLevel}
231-
onChange={() => setIsWorkspaceLevel((prevData) => !prevData)}
232-
/>
233-
</div>
234-
</Tooltip>
235-
)}
236257
</div>
237258
<div className="relative">
238259
<Search
@@ -413,6 +434,28 @@ export const CommandModal: React.FC = observer(() => {
413434
</Command.List>
414435
</Command>
415436
</div>
437+
{/* Bottom overlay */}
438+
<div className="w-full flex items-center justify-between px-4 py-2 border-t border-custom-border-200 bg-custom-background-90/80 rounded-b-lg">
439+
<div className="flex items-center gap-2">
440+
<span className="text-xs text-custom-text-300">Actions</span>
441+
<div className="flex items-center gap-1">
442+
<div className="grid h-6 min-w-[1.5rem] place-items-center rounded bg-custom-background-80 border-[0.5px] border-custom-border-200 px-1.5 text-[10px] text-custom-text-200">
443+
{platform === "MacOS" ? <CommandIcon className="h-2.5 w-2.5 text-custom-text-200" /> : "Ctrl"}
444+
</div>
445+
<kbd className="grid h-6 min-w-[1.5rem] place-items-center rounded bg-custom-background-80 border-[0.5px] border-custom-border-200 px-1.5 text-[10px] text-custom-text-200">
446+
K
447+
</kbd>
448+
</div>
449+
</div>
450+
<div className="flex items-center gap-2">
451+
<span className="text-xs text-custom-text-300">Workspace Level</span>
452+
<ToggleSwitch
453+
value={isWorkspaceLevel}
454+
onChange={() => setIsWorkspaceLevel((prevData) => !prevData)}
455+
size="sm"
456+
/>
457+
</div>
458+
</div>
416459
</Dialog.Panel>
417460
</Transition.Child>
418461
</div>

0 commit comments

Comments
 (0)