[WEB-5511] regression: revamped navigation UI bugs (#8183)

This commit is contained in:
Aaryan Khandelwal 2025-11-26 18:51:03 +05:30 committed by GitHub
parent 05b1c147a9
commit eddf80aaed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 164 additions and 149 deletions

View file

@ -14,20 +14,20 @@ export const TopNavigationRoot = observer(() => {
return ( return (
<div <div
className={cn("flex items-center justify-evenly min-h-11 w-full px-3.5 z-[27] transition-all duration-300", { className={cn("flex items-center min-h-11 w-full px-3.5 z-[27] transition-all duration-300", {
"px-2": !showLabel, "px-2": !showLabel,
})} })}
> >
{/* Workspace Menu */} {/* Workspace Menu */}
<div className="flex items-center justify-start flex-shrink-0"> <div className="shrink-0 flex-1">
<WorkspaceMenuRoot /> <WorkspaceMenuRoot />
</div> </div>
{/* Power K Search */} {/* Power K Search */}
<div className="flex items-center justify-center flex-grow px-4"> <div className="shrink-0">
<TopNavPowerK /> <TopNavPowerK />
</div> </div>
{/* Additional Actions */} {/* Additional Actions */}
<div className="flex gap-1 items-center justify-end flex-shrink-0 min-w-48"> <div className="shrink-0 flex-1 flex gap-1 items-center justify-end">
<HelpMenuRoot /> <HelpMenuRoot />
<div className="flex items-center justify-center size-8 hover:bg-custom-background-80 rounded-md"> <div className="flex items-center justify-center size-8 hover:bg-custom-background-80 rounded-md">
<UserMenuRoot size="xs" /> <UserMenuRoot size="xs" />

View file

@ -5,6 +5,7 @@ import {
DraftIcon, DraftIcon,
HomeIcon, HomeIcon,
InboxIcon, InboxIcon,
MultipleStickyIcon,
ProjectIcon, ProjectIcon,
ViewsIcon, ViewsIcon,
YourWorkIcon, YourWorkIcon,
@ -31,5 +32,7 @@ export const getSidebarNavigationItemIcon = (key: string, className: string = ""
return <DraftIcon className={cn("size-4 flex-shrink-0", className)} />; return <DraftIcon className={cn("size-4 flex-shrink-0", className)} />;
case "archives": case "archives":
return <ArchiveIcon className={cn("size-4 flex-shrink-0", className)} />; return <ArchiveIcon className={cn("size-4 flex-shrink-0", className)} />;
case "stickies":
return <MultipleStickyIcon className={cn("size-4 flex-shrink-0", className)} />;
} }
}; };

View file

@ -71,7 +71,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
const filteredPersonalItems = PERSONAL_ITEMS; const filteredPersonalItems = PERSONAL_ITEMS;
// Filter workspace items by permissions and feature flags, then get pinned/unpinned items // Filter workspace items by permissions and feature flags, then get pinned/unpinned items
const { pinnedItems, unpinnedItems } = useMemo(() => { const workspaceItems = useMemo(() => {
const items = WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS.filter((item) => { const items = WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS.filter((item) => {
// Permission check // Permission check
const hasPermission = allowPermissions( const hasPermission = allowPermissions(
@ -94,11 +94,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
}; };
}); });
// Sort pinned items by sort_order return items.sort((a, b) => a.sortOrder - b.sortOrder);
const pinned = items.filter((item) => item.isPinned).sort((a, b) => a.sortOrder - b.sortOrder);
const unpinned = items.filter((item) => !item.isPinned);
return { pinnedItems: pinned, unpinnedItems: unpinned };
}, [workspaceSlug, allowPermissions, workspacePreferences]); }, [workspaceSlug, allowPermissions, workspacePreferences]);
// Handle checkbox toggle // Handle checkbox toggle
@ -134,7 +130,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
); );
// Separate personal items into enabled/disabled // Separate personal items into enabled/disabled
const { enabledPersonalItems, disabledPersonalItems } = useMemo(() => { const personalItems = useMemo(() => {
const items = filteredPersonalItems.map((item) => { const items = filteredPersonalItems.map((item) => {
const itemState = personalPreferences.items[item.key]; const itemState = personalPreferences.items[item.key];
const isEnabled = typeof itemState === "boolean" ? itemState : (itemState?.enabled ?? true); const isEnabled = typeof itemState === "boolean" ? itemState : (itemState?.enabled ?? true);
@ -147,10 +143,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
}; };
}); });
const enabled = items.filter((item) => item.isEnabled).sort((a, b) => a.sortOrder - b.sortOrder); return items.sort((a, b) => a.sortOrder - b.sortOrder);
const disabled = items.filter((item) => !item.isEnabled);
return { enabledPersonalItems: enabled, disabledPersonalItems: disabled };
}, [personalPreferences, filteredPersonalItems]); }, [personalPreferences, filteredPersonalItems]);
// Prevent typing invalid characters in number input // Prevent typing invalid characters in number input
@ -203,18 +196,19 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
{/* Personal Section */} {/* Personal Section */}
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<h3 className="text-sm font-semibold text-custom-text-400">{t("personal")}</h3> <h3 className="text-sm font-semibold text-custom-text-400">{t("personal")}</h3>
{/* Enabled Items - Sortable */}
<div className="border border-custom-border-200 rounded-md py-2 bg-custom-background-90"> <div className="border border-custom-border-200 rounded-md py-2 bg-custom-background-90">
<Sortable <Sortable
data={enabledPersonalItems} data={personalItems}
onChange={handlePersonalReorder} onChange={handlePersonalReorder}
keyExtractor={(item) => item.key} keyExtractor={(item) => item.key}
id="personal-enabled-items" id="personal-enabled-items"
render={(item) => ( render={(item) => (
<div className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 transition-all duration-200"> <div className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 transition-all duration-200">
<GripVertical className="size-4 text-custom-text-400 cursor-grab active:cursor-grabbing transition-colors" /> <GripVertical className="size-4 text-custom-text-400 cursor-grab active:cursor-grabbing transition-colors" />
<Checkbox checked onChange={(e) => togglePersonalItem(item.key, e.target.checked)} /> <Checkbox
checked={!!personalPreferences.items[item.key]?.enabled}
onChange={(e) => togglePersonalItem(item.key, e.target.checked)}
/>
<div className="flex items-center gap-2 flex-1"> <div className="flex items-center gap-2 flex-1">
{getSidebarNavigationItemIcon(item.key)} {getSidebarNavigationItemIcon(item.key)}
<label className="text-sm text-custom-text-200 flex-1 cursor-pointer"> <label className="text-sm text-custom-text-200 flex-1 cursor-pointer">
@ -224,27 +218,6 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
</div> </div>
)} )}
/> />
{/* Disabled Items */}
{disabledPersonalItems.length > 0 && (
<div className={cn("space-y-1", enabledPersonalItems.length > 0 && "mt-1")}>
{disabledPersonalItems.map((item) => (
<div
key={item.key}
className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 transition-all duration-200"
>
<GripVertical className="size-4 text-custom-text-400 opacity-40" />
<Checkbox checked={false} onChange={(e) => togglePersonalItem(item.key, e.target.checked)} />
<div className="flex items-center gap-2 flex-1">
{getSidebarNavigationItemIcon(item.key)}
<label className="text-sm text-custom-text-200 flex-1 cursor-pointer">
{t(item.labelTranslationKey)}
</label>
</div>
</div>
))}
</div>
)}
</div> </div>
</div> </div>
@ -254,7 +227,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
<div className="border border-custom-border-200 rounded-md py-2 bg-custom-background-90"> <div className="border border-custom-border-200 rounded-md py-2 bg-custom-background-90">
{/* Pinned Items - Draggable */} {/* Pinned Items - Draggable */}
<Sortable <Sortable
data={pinnedItems} data={workspaceItems}
onChange={handleReorder} onChange={handleReorder}
keyExtractor={(item) => item.key} keyExtractor={(item) => item.key}
id="workspace-pinned-items" id="workspace-pinned-items"
@ -263,7 +236,10 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
return ( return (
<div className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 group transition-all duration-200"> <div className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 group transition-all duration-200">
<GripVertical className="size-4 text-custom-text-400 cursor-grab active:cursor-grabbing transition-colors" /> <GripVertical className="size-4 text-custom-text-400 cursor-grab active:cursor-grabbing transition-colors" />
<Checkbox checked onChange={(e) => handleWorkspaceItemToggle(item.key, e.target.checked)} /> <Checkbox
checked={!!workspacePreferences.items[item.key]?.is_pinned}
onChange={(e) => handleWorkspaceItemToggle(item.key, e.target.checked)}
/>
<div className="flex items-center gap-2 flex-1"> <div className="flex items-center gap-2 flex-1">
{icon} {icon}
<span className="text-sm text-custom-text-200">{t(item.labelTranslationKey)}</span> <span className="text-sm text-custom-text-200">{t(item.labelTranslationKey)}</span>
@ -272,31 +248,6 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
); );
}} }}
/> />
{/* Unpinned Items */}
{unpinnedItems.length > 0 && (
<div className="space-y-1 mt-1">
{unpinnedItems.map((item) => {
const icon = getSidebarNavigationItemIcon(item.key);
return (
<div
key={item.key}
className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 transition-all duration-200"
>
<GripVertical className="size-4 text-custom-text-400 opacity-40" />
<Checkbox
checked={false}
onChange={(e) => handleWorkspaceItemToggle(item.key, e.target.checked)}
/>
<div className="flex items-center gap-2 flex-1">
{icon}
<span className="text-sm text-custom-text-200">{t(item.labelTranslationKey)}</span>
</div>
</div>
);
})}
</div>
)}
</div> </div>
</div> </div>

View file

@ -1,14 +1,15 @@
"use client"; "use client";
import type { FC } from "react"; import type { FC } from "react";
import React, { useState, useRef } from "react"; import { useState, useRef } from "react";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { LinkIcon, LogOut, MoreHorizontal, Settings, Share2, ArchiveIcon } from "lucide-react"; import { LinkIcon, LogOut, MoreHorizontal, Settings, Share2, ArchiveIcon } from "lucide-react";
// plane imports
import { MEMBER_TRACKER_ELEMENTS } from "@plane/constants"; import { MEMBER_TRACKER_ELEMENTS } from "@plane/constants";
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { CustomMenu } from "@plane/ui"; import { CustomMenu } from "@plane/ui";
type ProjectActionsMenuProps = { type Props = {
workspaceSlug: string; workspaceSlug: string;
project: { project: {
id: string; id: string;
@ -20,7 +21,7 @@ type ProjectActionsMenuProps = {
onPublishModal: () => void; onPublishModal: () => void;
}; };
export const ProjectActionsMenu: FC<ProjectActionsMenuProps> = ({ export const ProjectActionsMenu: FC<Props> = ({
workspaceSlug, workspaceSlug,
project, project,
isAdmin, isAdmin,
@ -29,10 +30,14 @@ export const ProjectActionsMenu: FC<ProjectActionsMenuProps> = ({
onLeaveProject, onLeaveProject,
onPublishModal, onPublishModal,
}) => { }) => {
const { t } = useTranslation(); // states
const navigate = useNavigate();
const actionSectionRef = useRef<HTMLDivElement | null>(null);
const [isMenuActive, setIsMenuActive] = useState(false); const [isMenuActive, setIsMenuActive] = useState(false);
// translation
const { t } = useTranslation();
// refs
const actionSectionRef = useRef<HTMLDivElement | null>(null);
// router
const navigate = useNavigate();
return ( return (
<CustomMenu <CustomMenu

View file

@ -1,7 +1,7 @@
import type { FC } from "react"; import type { FC } from "react";
// plane imports
import { Logo } from "@plane/propel/emoji-icon-picker"; import { Logo } from "@plane/propel/emoji-icon-picker";
import type { TLogoProps } from "@plane/types"; import type { TLogoProps } from "@plane/types";
import { cn } from "@plane/utils";
type ProjectHeaderProps = { type ProjectHeaderProps = {
project: { project: {
@ -11,7 +11,7 @@ type ProjectHeaderProps = {
}; };
export const ProjectHeader: FC<ProjectHeaderProps> = ({ project }) => ( export const ProjectHeader: FC<ProjectHeaderProps> = ({ project }) => (
<div className={cn("flex-grow flex items-center gap-1.5 text-left select-none w-full flex-shrink-0")}> <div className="flex items-center gap-1.5 text-left select-none w-full">
<div className="size-7 rounded-md bg-custom-background-90 flex items-center justify-center flex-shrink-0"> <div className="size-7 rounded-md bg-custom-background-90 flex items-center justify-center flex-shrink-0">
<Logo logo={project.logo_props} size={16} /> <Logo logo={project.logo_props} size={16} />
</div> </div>

View file

@ -1,14 +1,17 @@
import React from "react"; import React from "react";
import { Link } from "react-router"; import { Link } from "react-router";
import { MoreHorizontal, Star, Pin } from "lucide-react"; import { MoreHorizontal, Pin } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { SetAsDefaultIcon } from "@plane/propel/icons";
import { Menu } from "@plane/propel/menu"; import { Menu } from "@plane/propel/menu";
import { TabNavigationItem } from "@plane/propel/tab-navigation"; import { Tooltip } from "@plane/propel/tooltip";
import { cn } from "@plane/utils"; import { cn } from "@plane/utils";
// local imports
import type { TNavigationItem } from "./tab-navigation-root"; import type { TNavigationItem } from "./tab-navigation-root";
import type { TTabPreferences } from "./tab-navigation-utils"; import type { TTabPreferences } from "./tab-navigation-utils";
export type TTabNavigationOverflowMenuProps = { type Props = {
overflowItems: TNavigationItem[]; overflowItems: TNavigationItem[];
isActive: (item: TNavigationItem) => boolean; isActive: (item: TNavigationItem) => boolean;
tabPreferences: TTabPreferences; tabPreferences: TTabPreferences;
@ -19,9 +22,9 @@ export type TTabNavigationOverflowMenuProps = {
/** /**
* Overflow menu for tab navigation items * Overflow menu for tab navigation items
* Displays items that don't fit in the visible area, with action icons * Displays items that don't fit in the visible area, with action icons
* Shows "Eye" icon for user-hidden items, "Star" icon for all items * Shows "Eye" icon for user-hidden items, "Set as default" icon for all items
*/ */
export const TabNavigationOverflowMenu: React.FC<TTabNavigationOverflowMenuProps> = ({ export const TabNavigationOverflowMenu: React.FC<Props> = ({
overflowItems, overflowItems,
isActive, isActive,
tabPreferences, tabPreferences,
@ -48,23 +51,12 @@ export const TabNavigationOverflowMenu: React.FC<TTabNavigationOverflowMenuProps
const isDefault = item.key === tabPreferences.defaultTab; const isDefault = item.key === tabPreferences.defaultTab;
return ( return (
<Menu.MenuItem <Menu.MenuItem key={`${item.key}-overflow-${itemIsActive ? "active" : "inactive"}`} className="p-0 w-full">
key={`${item.key}-overflow-${itemIsActive ? "active" : "inactive"}`} <div className="flex items-center justify-between w-full group/menu-item">
className={cn("p-0 w-full", { <Link to={item.href} className="flex-1 min-w-0 w-full p-1">
"bg-custom-background-80": itemIsActive, <span className="text-xs">{t(item.i18n_key)}</span>
})}
>
<div className="flex items-center justify-between w-full group">
<Link to={item.href} className="flex-1 min-w-0 w-full">
<TabNavigationItem isActive={itemIsActive}>
<span className="text-sm">{t(item.i18n_key)}</span>
</TabNavigationItem>
</Link> </Link>
<div <div className="flex items-center">
className={cn("flex items-center gap-1 px-2 opacity-0 group-hover:opacity-100 transition-opacity", {
"opacity-100": itemIsActive,
})}
>
{/* Show Eye icon ONLY for user-hidden items */} {/* Show Eye icon ONLY for user-hidden items */}
{isHidden && ( {isHidden && (
<button <button
@ -74,23 +66,30 @@ export const TabNavigationOverflowMenu: React.FC<TTabNavigationOverflowMenuProps
e.preventDefault(); e.preventDefault();
onShow(item.key); onShow(item.key);
}} }}
className="p-1 rounded hover:bg-custom-background-90" className="invisible group-hover/menu-item:visible p-1 rounded text-custom-text-300 hover:text-custom-text-100 transition-colors"
title="Show" title="Show"
> >
<Pin className="h-3.5 w-3.5 text-custom-text-300 rotate-45" /> <Pin className="size-3" />
</button> </button>
)} )}
<button <Tooltip tooltipContent={isDefault ? "Clear default" : "Set as default"}>
onClick={(e) => { <button
e.stopPropagation(); onClick={(e) => {
e.preventDefault(); e.stopPropagation();
onToggleDefault(item.key); e.preventDefault();
}} onToggleDefault(item.key);
className="p-1 rounded hover:bg-custom-background-90" }}
title={isDefault ? "Clear default" : "Set as default"} className={cn(
> "invisible group-hover/menu-item:visible p-1 rounded text-custom-text-300 hover:text-custom-text-100 transition-colors",
<Star className={`h-3.5 w-3.5 text-custom-text-300 ${isDefault ? "fill-current" : ""}`} /> {
</button> visible: isDefault,
}
)}
title={isDefault ? "Clear default" : "Set as default"}
>
<SetAsDefaultIcon className="size-3" />
</button>
</Tooltip>
</div> </div>
</div> </div>
</Menu.MenuItem> </Menu.MenuItem>

View file

@ -167,21 +167,23 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) =
/> />
{/* container for the tab navigation */} {/* container for the tab navigation */}
<div className="flex items-center gap-3 overflow-hidden pl-1.5 w-full h-full"> <div className="flex items-center gap-3 overflow-hidden pl-1.5 size-full">
<div className="flex items-center gap-2 flex-shrink-0 max-w-48 truncate"> <div className="flex items-center gap-2 shrink-0">
<ProjectHeader project={project} /> <ProjectHeader project={project} />
<ProjectActionsMenu <div className="shrink-0">
workspaceSlug={workspaceSlug} <ProjectActionsMenu
project={project} workspaceSlug={workspaceSlug}
isAdmin={isAdmin} project={project}
isAuthorized={isAuthorized} isAdmin={isAdmin}
onCopyText={handleCopyText} isAuthorized={isAuthorized}
onLeaveProject={handleLeaveProject} onCopyText={handleCopyText}
onPublishModal={() => handlePublishModal(true)} onLeaveProject={handleLeaveProject}
/> onPublishModal={() => handlePublishModal(true)}
/>
</div>
</div> </div>
<div className="flex-shrink-0 h-5 w-1 border-l border-custom-border-200" /> <div className="shrink-0 h-5 w-1 border-l border-custom-border-200" />
<div ref={containerRef} className="flex items-center h-full flex-1 min-w-0 overflow-hidden"> <div ref={containerRef} className="flex items-center h-full flex-1 min-w-0 overflow-hidden">
<TabNavigationList className="h-full"> <TabNavigationList className="h-full">

View file

@ -1,8 +1,12 @@
import React from "react"; import React from "react";
import { Link } from "react-router"; import { Link } from "react-router";
import { PinOff } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { ContextMenu } from "@plane/propel/context-menu"; import { ContextMenu } from "@plane/propel/context-menu";
import { SetAsDefaultIcon } from "@plane/propel/icons";
import { TabNavigationItem } from "@plane/propel/tab-navigation"; import { TabNavigationItem } from "@plane/propel/tab-navigation";
// local imports
import type { TNavigationItem } from "./tab-navigation-root"; import type { TNavigationItem } from "./tab-navigation-root";
import type { TTabPreferences } from "./tab-navigation-utils"; import type { TTabPreferences } from "./tab-navigation-utils";
@ -51,7 +55,9 @@ export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps>
e.stopPropagation(); e.stopPropagation();
onToggleDefault(item.key); onToggleDefault(item.key);
}} }}
className="flex items-center gap-2 text-custom-text-200 transition-colors cursor-pointer"
> >
<SetAsDefaultIcon className="shrink-0 size-3" />
<span className="text-xs">{isDefault ? "Clear default" : "Set as default"}</span> <span className="text-xs">{isDefault ? "Clear default" : "Set as default"}</span>
</ContextMenu.Item> </ContextMenu.Item>
<ContextMenu.Item <ContextMenu.Item
@ -59,7 +65,9 @@ export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps>
e.stopPropagation(); e.stopPropagation();
onHide(item.key); onHide(item.key);
}} }}
className="flex items-center gap-2 text-custom-text-200 transition-colors cursor-pointer"
> >
<PinOff className="shrink-0 size-3" />
<span className="text-xs">Hide in more menu</span> <span className="text-xs">Hide in more menu</span>
</ContextMenu.Item> </ContextMenu.Item>
</ContextMenu.Content> </ContextMenu.Content>

View file

@ -207,19 +207,21 @@ export const TopNavPowerK = observer(() => {
); );
return ( return (
<div ref={containerRef} className="relative flex justify-center"> <div ref={containerRef} className="relative">
<div <div
className={cn( className={cn("relative w-[364px] flex items-center transition-all duration-300 ease-in-out z-30", {
"relative flex items-center transition-all duration-300 ease-in-out z-30", "w-[554px]": isOpen,
isOpen ? "w-[554px]" : "w-[364px]" })}
)}
> >
<div <div
className={cn( className={cn(
"flex items-center w-full h-7 px-2 py-2 rounded-md bg-custom-sidebar-background-80 hover:bg-custom-background-80 transition-colors duration-200", "flex items-center w-full h-7 p-2 rounded-md bg-custom-sidebar-background-80 hover:bg-custom-background-80 border border-transparent transition-colors duration-200",
isOpen && "border border-custom-border-200" {
"border-custom-border-200": isOpen,
}
)} )}
onClick={() => inputRef.current?.focus()} onClick={() => inputRef.current?.focus()}
role="button"
> >
<SearchIcon className="shrink-0 size-3.5 text-custom-text-350 mr-2" /> <SearchIcon className="shrink-0 size-3.5 text-custom-text-350 mr-2" />
<input <input
@ -239,7 +241,6 @@ export const TopNavPowerK = observer(() => {
)} )}
</div> </div>
</div> </div>
<div <div
className={cn( className={cn(
"absolute -top-[6px] left-1/2 -translate-x-1/2 bg-custom-background-100 border border-custom-border-200 rounded-md shadow-lg overflow-hidden z-20 transition-all duration-300 ease-in-out flex flex-col px-0 pt-10", "absolute -top-[6px] left-1/2 -translate-x-1/2 bg-custom-background-100 border border-custom-border-200 rounded-md shadow-lg overflow-hidden z-20 transition-all duration-300 ease-in-out flex flex-col px-0 pt-10",

View file

@ -57,19 +57,10 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa
const updatePreferences = async (newPreferences: TTabPreferences) => { const updatePreferences = async (newPreferences: TTabPreferences) => {
if (!memberId) return; if (!memberId) return;
try { await updateProjectMemberPreferences(workspaceSlug, projectId, memberId, {
await updateProjectMemberPreferences(workspaceSlug, projectId, memberId, { default_tab: newPreferences.defaultTab,
default_tab: newPreferences.defaultTab, hide_in_more_menu: newPreferences.hiddenTabs,
hide_in_more_menu: newPreferences.hiddenTabs, });
});
} catch (error) {
console.error("Error updating tab preferences:", error);
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Something went wrong. Please try again later.",
});
}
}; };
/** /**
@ -79,7 +70,21 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa
const handleToggleDefaultTab = (tabKey: string) => { const handleToggleDefaultTab = (tabKey: string) => {
const newDefaultTab = tabKey === tabPreferences.defaultTab ? DEFAULT_TAB_KEY : tabKey; const newDefaultTab = tabKey === tabPreferences.defaultTab ? DEFAULT_TAB_KEY : tabKey;
const newPreferences = { ...tabPreferences, defaultTab: newDefaultTab }; const newPreferences = { ...tabPreferences, defaultTab: newDefaultTab };
updatePreferences(newPreferences); updatePreferences(newPreferences)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Default tab updated successfully.",
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Failed to update default tab. Please try again later.",
});
});
}; };
/** /**
@ -90,7 +95,16 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa
...tabPreferences, ...tabPreferences,
hiddenTabs: [...tabPreferences.hiddenTabs, tabKey], hiddenTabs: [...tabPreferences.hiddenTabs, tabKey],
}; };
updatePreferences(newPreferences); try {
updatePreferences(newPreferences);
} catch (error) {
console.error("Error hiding tab:", error);
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Failed to hide tab. Please try again later.",
});
}
}; };
/** /**
@ -101,7 +115,16 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa
...tabPreferences, ...tabPreferences,
hiddenTabs: tabPreferences.hiddenTabs.filter((key) => key !== tabKey), hiddenTabs: tabPreferences.hiddenTabs.filter((key) => key !== tabKey),
}; };
updatePreferences(newPreferences); try {
updatePreferences(newPreferences);
} catch (error) {
console.error("Error showing tab:", error);
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Something went wrong. Please try again later.",
});
}
}; };
return { return {

View file

@ -65,14 +65,13 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
// Toggle sidebar dropdown state when either menu is open // Toggle sidebar dropdown state when either menu is open
useEffect(() => { useEffect(() => {
if (isWorkspaceMenuOpen) toggleAnySidebarDropdown(true); toggleAnySidebarDropdown(isWorkspaceMenuOpen);
else toggleAnySidebarDropdown(false); }, [isWorkspaceMenuOpen, toggleAnySidebarDropdown]);
}, [isWorkspaceMenuOpen]);
return ( return (
<Menu <Menu
as="div" as="div"
className={cn("relative h-full flex max-w-48 truncate", { className={cn("relative h-full flex max-w-48 w-fit whitespace-nowrap truncate", {
"justify-center text-center": renderLogoOnly, "justify-center text-center": renderLogoOnly,
"flex-grow justify-stretch text-left truncate": !renderLogoOnly, "flex-grow justify-stretch text-left truncate": !renderLogoOnly,
})} })}

View file

@ -54,6 +54,7 @@ export * from "./properties";
export * from "./related-icon"; export * from "./related-icon";
export * from "./sans-serif-icon"; export * from "./sans-serif-icon";
export * from "./serif-icon"; export * from "./serif-icon";
export * from "./set-as-default-icon";
export * from "./side-panel-icon"; export * from "./side-panel-icon";
export * from "./state"; export * from "./state";
export * from "./sticky-note-icon"; export * from "./sticky-note-icon";

View file

@ -0,0 +1,23 @@
import type { ISvgIcons } from "./type";
export function SetAsDefaultIcon({ className = "text-current", ...rest }: ISvgIcons) {
return (
<svg
width="15"
height="15"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
{...rest}
>
<path
d="M7.29167 0.625V13.9583M12.0057 2.57762L2.57762 12.0057M13.9583 7.29167H0.625M12.0057 12.0057L2.57762 2.57762"
stroke="currentColor"
strokeWidth="1.25"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}