[WEB-5511] regression: revamped navigation UI bugs (#8183)
This commit is contained in:
parent
05b1c147a9
commit
eddf80aaed
13 changed files with 164 additions and 149 deletions
|
|
@ -14,20 +14,20 @@ export const TopNavigationRoot = observer(() => {
|
|||
|
||||
return (
|
||||
<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,
|
||||
})}
|
||||
>
|
||||
{/* Workspace Menu */}
|
||||
<div className="flex items-center justify-start flex-shrink-0">
|
||||
<div className="shrink-0 flex-1">
|
||||
<WorkspaceMenuRoot />
|
||||
</div>
|
||||
{/* Power K Search */}
|
||||
<div className="flex items-center justify-center flex-grow px-4">
|
||||
<div className="shrink-0">
|
||||
<TopNavPowerK />
|
||||
</div>
|
||||
{/* 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 />
|
||||
<div className="flex items-center justify-center size-8 hover:bg-custom-background-80 rounded-md">
|
||||
<UserMenuRoot size="xs" />
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
DraftIcon,
|
||||
HomeIcon,
|
||||
InboxIcon,
|
||||
MultipleStickyIcon,
|
||||
ProjectIcon,
|
||||
ViewsIcon,
|
||||
YourWorkIcon,
|
||||
|
|
@ -31,5 +32,7 @@ export const getSidebarNavigationItemIcon = (key: string, className: string = ""
|
|||
return <DraftIcon className={cn("size-4 flex-shrink-0", className)} />;
|
||||
case "archives":
|
||||
return <ArchiveIcon className={cn("size-4 flex-shrink-0", className)} />;
|
||||
case "stickies":
|
||||
return <MultipleStickyIcon className={cn("size-4 flex-shrink-0", className)} />;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
const filteredPersonalItems = PERSONAL_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) => {
|
||||
// Permission check
|
||||
const hasPermission = allowPermissions(
|
||||
|
|
@ -94,11 +94,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
};
|
||||
});
|
||||
|
||||
// Sort pinned items by sort_order
|
||||
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 };
|
||||
return items.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
}, [workspaceSlug, allowPermissions, workspacePreferences]);
|
||||
|
||||
// Handle checkbox toggle
|
||||
|
|
@ -134,7 +130,7 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
);
|
||||
|
||||
// Separate personal items into enabled/disabled
|
||||
const { enabledPersonalItems, disabledPersonalItems } = useMemo(() => {
|
||||
const personalItems = useMemo(() => {
|
||||
const items = filteredPersonalItems.map((item) => {
|
||||
const itemState = personalPreferences.items[item.key];
|
||||
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);
|
||||
const disabled = items.filter((item) => !item.isEnabled);
|
||||
|
||||
return { enabledPersonalItems: enabled, disabledPersonalItems: disabled };
|
||||
return items.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
}, [personalPreferences, filteredPersonalItems]);
|
||||
|
||||
// Prevent typing invalid characters in number input
|
||||
|
|
@ -203,18 +196,19 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
{/* Personal Section */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<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">
|
||||
<Sortable
|
||||
data={enabledPersonalItems}
|
||||
data={personalItems}
|
||||
onChange={handlePersonalReorder}
|
||||
keyExtractor={(item) => item.key}
|
||||
id="personal-enabled-items"
|
||||
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">
|
||||
<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">
|
||||
{getSidebarNavigationItemIcon(item.key)}
|
||||
<label className="text-sm text-custom-text-200 flex-1 cursor-pointer">
|
||||
|
|
@ -224,27 +218,6 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
</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>
|
||||
|
||||
|
|
@ -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">
|
||||
{/* Pinned Items - Draggable */}
|
||||
<Sortable
|
||||
data={pinnedItems}
|
||||
data={workspaceItems}
|
||||
onChange={handleReorder}
|
||||
keyExtractor={(item) => item.key}
|
||||
id="workspace-pinned-items"
|
||||
|
|
@ -263,29 +236,8 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
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">
|
||||
<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)} />
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{icon}
|
||||
<span className="text-sm text-custom-text-200">{t(item.labelTranslationKey)}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 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}
|
||||
checked={!!workspacePreferences.items[item.key]?.is_pinned}
|
||||
onChange={(e) => handleWorkspaceItemToggle(item.key, e.target.checked)}
|
||||
/>
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
|
|
@ -294,9 +246,8 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
"use client";
|
||||
|
||||
import type { FC } from "react";
|
||||
import React, { useState, useRef } from "react";
|
||||
import { useState, useRef } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { LinkIcon, LogOut, MoreHorizontal, Settings, Share2, ArchiveIcon } from "lucide-react";
|
||||
// plane imports
|
||||
import { MEMBER_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
|
||||
type ProjectActionsMenuProps = {
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
project: {
|
||||
id: string;
|
||||
|
|
@ -20,7 +21,7 @@ type ProjectActionsMenuProps = {
|
|||
onPublishModal: () => void;
|
||||
};
|
||||
|
||||
export const ProjectActionsMenu: FC<ProjectActionsMenuProps> = ({
|
||||
export const ProjectActionsMenu: FC<Props> = ({
|
||||
workspaceSlug,
|
||||
project,
|
||||
isAdmin,
|
||||
|
|
@ -29,10 +30,14 @@ export const ProjectActionsMenu: FC<ProjectActionsMenuProps> = ({
|
|||
onLeaveProject,
|
||||
onPublishModal,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
// states
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
// refs
|
||||
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
// router
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<CustomMenu
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { FC } from "react";
|
||||
// plane imports
|
||||
import { Logo } from "@plane/propel/emoji-icon-picker";
|
||||
import type { TLogoProps } from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
type ProjectHeaderProps = {
|
||||
project: {
|
||||
|
|
@ -11,7 +11,7 @@ type ProjectHeaderProps = {
|
|||
};
|
||||
|
||||
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">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
import React from "react";
|
||||
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 { SetAsDefaultIcon } from "@plane/propel/icons";
|
||||
import { Menu } from "@plane/propel/menu";
|
||||
import { TabNavigationItem } from "@plane/propel/tab-navigation";
|
||||
import { Tooltip } from "@plane/propel/tooltip";
|
||||
import { cn } from "@plane/utils";
|
||||
// local imports
|
||||
import type { TNavigationItem } from "./tab-navigation-root";
|
||||
import type { TTabPreferences } from "./tab-navigation-utils";
|
||||
|
||||
export type TTabNavigationOverflowMenuProps = {
|
||||
type Props = {
|
||||
overflowItems: TNavigationItem[];
|
||||
isActive: (item: TNavigationItem) => boolean;
|
||||
tabPreferences: TTabPreferences;
|
||||
|
|
@ -19,9 +22,9 @@ export type TTabNavigationOverflowMenuProps = {
|
|||
/**
|
||||
* Overflow menu for tab navigation items
|
||||
* 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,
|
||||
isActive,
|
||||
tabPreferences,
|
||||
|
|
@ -48,23 +51,12 @@ export const TabNavigationOverflowMenu: React.FC<TTabNavigationOverflowMenuProps
|
|||
const isDefault = item.key === tabPreferences.defaultTab;
|
||||
|
||||
return (
|
||||
<Menu.MenuItem
|
||||
key={`${item.key}-overflow-${itemIsActive ? "active" : "inactive"}`}
|
||||
className={cn("p-0 w-full", {
|
||||
"bg-custom-background-80": itemIsActive,
|
||||
})}
|
||||
>
|
||||
<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>
|
||||
<Menu.MenuItem key={`${item.key}-overflow-${itemIsActive ? "active" : "inactive"}`} className="p-0 w-full">
|
||||
<div className="flex items-center justify-between w-full group/menu-item">
|
||||
<Link to={item.href} className="flex-1 min-w-0 w-full p-1">
|
||||
<span className="text-xs">{t(item.i18n_key)}</span>
|
||||
</Link>
|
||||
<div
|
||||
className={cn("flex items-center gap-1 px-2 opacity-0 group-hover:opacity-100 transition-opacity", {
|
||||
"opacity-100": itemIsActive,
|
||||
})}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{/* Show Eye icon ONLY for user-hidden items */}
|
||||
{isHidden && (
|
||||
<button
|
||||
|
|
@ -74,23 +66,30 @@ export const TabNavigationOverflowMenu: React.FC<TTabNavigationOverflowMenuProps
|
|||
e.preventDefault();
|
||||
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"
|
||||
>
|
||||
<Pin className="h-3.5 w-3.5 text-custom-text-300 rotate-45" />
|
||||
<Pin className="size-3" />
|
||||
</button>
|
||||
)}
|
||||
<Tooltip tooltipContent={isDefault ? "Clear default" : "Set as default"}>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onToggleDefault(item.key);
|
||||
}}
|
||||
className="p-1 rounded hover:bg-custom-background-90"
|
||||
className={cn(
|
||||
"invisible group-hover/menu-item:visible p-1 rounded text-custom-text-300 hover:text-custom-text-100 transition-colors",
|
||||
{
|
||||
visible: isDefault,
|
||||
}
|
||||
)}
|
||||
title={isDefault ? "Clear default" : "Set as default"}
|
||||
>
|
||||
<Star className={`h-3.5 w-3.5 text-custom-text-300 ${isDefault ? "fill-current" : ""}`} />
|
||||
<SetAsDefaultIcon className="size-3" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</Menu.MenuItem>
|
||||
|
|
|
|||
|
|
@ -167,9 +167,10 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) =
|
|||
/>
|
||||
|
||||
{/* 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-2 flex-shrink-0 max-w-48 truncate">
|
||||
<div className="flex items-center gap-3 overflow-hidden pl-1.5 size-full">
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<ProjectHeader project={project} />
|
||||
<div className="shrink-0">
|
||||
<ProjectActionsMenu
|
||||
workspaceSlug={workspaceSlug}
|
||||
project={project}
|
||||
|
|
@ -180,8 +181,9 @@ export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) =
|
|||
onPublishModal={() => handlePublishModal(true)}
|
||||
/>
|
||||
</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">
|
||||
<TabNavigationList className="h-full">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import React from "react";
|
||||
import { Link } from "react-router";
|
||||
import { PinOff } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { ContextMenu } from "@plane/propel/context-menu";
|
||||
import { SetAsDefaultIcon } from "@plane/propel/icons";
|
||||
import { TabNavigationItem } from "@plane/propel/tab-navigation";
|
||||
// local imports
|
||||
import type { TNavigationItem } from "./tab-navigation-root";
|
||||
import type { TTabPreferences } from "./tab-navigation-utils";
|
||||
|
||||
|
|
@ -51,7 +55,9 @@ export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps>
|
|||
e.stopPropagation();
|
||||
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>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item
|
||||
|
|
@ -59,7 +65,9 @@ export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps>
|
|||
e.stopPropagation();
|
||||
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>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
|
|
|
|||
|
|
@ -207,19 +207,21 @@ export const TopNavPowerK = observer(() => {
|
|||
);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="relative flex justify-center">
|
||||
<div ref={containerRef} className="relative">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center transition-all duration-300 ease-in-out z-30",
|
||||
isOpen ? "w-[554px]" : "w-[364px]"
|
||||
)}
|
||||
className={cn("relative w-[364px] flex items-center transition-all duration-300 ease-in-out z-30", {
|
||||
"w-[554px]": isOpen,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
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",
|
||||
isOpen && "border border-custom-border-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",
|
||||
{
|
||||
"border-custom-border-200": isOpen,
|
||||
}
|
||||
)}
|
||||
onClick={() => inputRef.current?.focus()}
|
||||
role="button"
|
||||
>
|
||||
<SearchIcon className="shrink-0 size-3.5 text-custom-text-350 mr-2" />
|
||||
<input
|
||||
|
|
@ -239,7 +241,6 @@ export const TopNavPowerK = observer(() => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -57,19 +57,10 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa
|
|||
const updatePreferences = async (newPreferences: TTabPreferences) => {
|
||||
if (!memberId) return;
|
||||
|
||||
try {
|
||||
await updateProjectMemberPreferences(workspaceSlug, projectId, memberId, {
|
||||
default_tab: newPreferences.defaultTab,
|
||||
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 newDefaultTab = tabKey === tabPreferences.defaultTab ? DEFAULT_TAB_KEY : tabKey;
|
||||
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,
|
||||
hiddenTabs: [...tabPreferences.hiddenTabs, tabKey],
|
||||
};
|
||||
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,
|
||||
hiddenTabs: tabPreferences.hiddenTabs.filter((key) => key !== tabKey),
|
||||
};
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -65,14 +65,13 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
|
|||
|
||||
// Toggle sidebar dropdown state when either menu is open
|
||||
useEffect(() => {
|
||||
if (isWorkspaceMenuOpen) toggleAnySidebarDropdown(true);
|
||||
else toggleAnySidebarDropdown(false);
|
||||
}, [isWorkspaceMenuOpen]);
|
||||
toggleAnySidebarDropdown(isWorkspaceMenuOpen);
|
||||
}, [isWorkspaceMenuOpen, toggleAnySidebarDropdown]);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
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,
|
||||
"flex-grow justify-stretch text-left truncate": !renderLogoOnly,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export * from "./properties";
|
|||
export * from "./related-icon";
|
||||
export * from "./sans-serif-icon";
|
||||
export * from "./serif-icon";
|
||||
export * from "./set-as-default-icon";
|
||||
export * from "./side-panel-icon";
|
||||
export * from "./state";
|
||||
export * from "./sticky-note-icon";
|
||||
|
|
|
|||
23
packages/propel/src/icons/set-as-default-icon.tsx
Normal file
23
packages/propel/src/icons/set-as-default-icon.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue