[WEB-4166] chore: projects app sidebar accessibility (#7115)
* chore: add ARIA attributes * chore: add missing translations * chore: add accessibility translations for multiple languages and configured store according to it * chore: refactor translation file handling and introduce TranslationFiles enum * fix: accessibility issues in workspace sidebar --------- Co-authored-by: JayashTripathy <jayashtripathy371@gmail.com> Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
This commit is contained in:
parent
b4bc49971c
commit
a3a580923c
43 changed files with 777 additions and 170 deletions
|
|
@ -1,4 +1,6 @@
|
|||
// plane utils
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { cn } from "@plane/utils";
|
||||
// helpers
|
||||
import { getFileURL } from "@/helpers/file.helper";
|
||||
|
|
@ -9,22 +11,27 @@ type Props = {
|
|||
classNames?: string;
|
||||
};
|
||||
|
||||
export const WorkspaceLogo = (props: Props) => (
|
||||
<div
|
||||
className={cn(
|
||||
`relative grid h-6 w-6 flex-shrink-0 place-items-center uppercase ${
|
||||
!props.logo && "rounded bg-custom-primary-500 text-white"
|
||||
} ${props.classNames ? props.classNames : ""}`
|
||||
)}
|
||||
>
|
||||
{props.logo && props.logo !== "" ? (
|
||||
<img
|
||||
src={getFileURL(props.logo)}
|
||||
className="absolute left-0 top-0 h-full w-full rounded object-cover"
|
||||
alt="Workspace Logo"
|
||||
/>
|
||||
) : (
|
||||
(props.name?.charAt(0) ?? "...")
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
export const WorkspaceLogo = observer((props: Props) => {
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
`relative grid h-6 w-6 flex-shrink-0 place-items-center uppercase ${
|
||||
!props.logo && "rounded bg-custom-primary-500 text-white"
|
||||
} ${props.classNames ? props.classNames : ""}`
|
||||
)}
|
||||
>
|
||||
{props.logo && props.logo !== "" ? (
|
||||
<img
|
||||
src={getFileURL(props.logo)}
|
||||
className="absolute left-0 top-0 h-full w-full rounded object-cover"
|
||||
alt={t("aria_labels.projects_sidebar.workspace_logo")}
|
||||
/>
|
||||
) : (
|
||||
(props.name?.[0] ?? "...")
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,21 +25,17 @@ import { WorkspaceLogo } from "../logo";
|
|||
import SidebarDropdownItem from "./dropdown-item";
|
||||
|
||||
export const SidebarDropdown = observer(() => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// store hooks
|
||||
const { sidebarCollapsed, toggleSidebar } = useAppTheme();
|
||||
const { data: currentUser } = useUser();
|
||||
const {
|
||||
// updateCurrentUser,
|
||||
// isUserInstanceAdmin,
|
||||
signOut,
|
||||
} = useUser();
|
||||
const { signOut } = useUser();
|
||||
const { updateUserProfile } = useUserProfile();
|
||||
const isWorkspaceCreationEnabled = getIsWorkspaceCreationDisabled() === false;
|
||||
|
||||
const isUserInstanceAdmin = false;
|
||||
const { currentWorkspace: activeWorkspace, workspaces } = useWorkspace();
|
||||
// derived values
|
||||
const isWorkspaceCreationEnabled = getIsWorkspaceCreationDisabled() === false;
|
||||
const isUserInstanceAdmin = false;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
// popper-js refs
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
|
|
@ -87,6 +83,7 @@ export const SidebarDropdown = observer(() => {
|
|||
"group/menu-button flex items-center justify-between gap-1 p-1 truncate rounded text-sm font-medium text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:outline-none",
|
||||
{ "flex-grow": !sidebarCollapsed }
|
||||
)}
|
||||
aria-label={t("aria_labels.projects_sidebar.open_workspace_switcher")}
|
||||
>
|
||||
<div className="flex-grow flex items-center gap-2 truncate">
|
||||
<WorkspaceLogo logo={activeWorkspace?.logo_url} name={activeWorkspace?.name} />
|
||||
|
|
@ -190,7 +187,11 @@ export const SidebarDropdown = observer(() => {
|
|||
)}
|
||||
</Menu>
|
||||
<Menu as="div" className="relative flex-shrink-0">
|
||||
<Menu.Button className="grid place-items-center outline-none" ref={setReferenceElement}>
|
||||
<Menu.Button
|
||||
className="grid place-items-center outline-none"
|
||||
ref={setReferenceElement}
|
||||
aria-label={t("aria_labels.projects_sidebar.open_user_menu")}
|
||||
>
|
||||
<Avatar
|
||||
name={currentUser?.display_name}
|
||||
src={getFileURL(currentUser?.avatar_url ?? "")}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,9 @@ import { useParams } from "next/navigation";
|
|||
import { createRoot } from "react-dom/client";
|
||||
import { PenSquare, Star, MoreHorizontal, ChevronRight, GripVertical } from "lucide-react";
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
|
||||
// plane helpers
|
||||
// plane imports
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
// ui
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { IFavorite, InstructionType } from "@plane/types";
|
||||
import { CustomMenu, Tooltip, DropIndicator, FavoriteFolderIcon, DragHandle } from "@plane/ui";
|
||||
// helpers
|
||||
|
|
@ -29,7 +28,7 @@ import { cn } from "@/helpers/common.helper";
|
|||
import { useAppTheme } from "@/hooks/store";
|
||||
import { useFavorite } from "@/hooks/store/use-favorite";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// constants
|
||||
// local imports
|
||||
import { FavoriteRoot } from "./favorite-items";
|
||||
import { getCanDrop, getInstructionFromPayload } from "./favorites.helpers";
|
||||
import { NewFavoriteFolder } from "./new-fav-folder";
|
||||
|
|
@ -54,10 +53,11 @@ export const FavoriteFolder: React.FC<Props> = (props) => {
|
|||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [folderToRename, setFolderToRename] = useState<string | boolean | null>(null);
|
||||
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);
|
||||
|
||||
// refs
|
||||
const actionSectionRef = useRef<HTMLDivElement | null>(null);
|
||||
const elementRef = useRef<HTMLDivElement | null>(null);
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (favorite.children === undefined && workspaceSlug) {
|
||||
|
|
@ -231,11 +231,11 @@ export const FavoriteFolder: React.FC<Props> = (props) => {
|
|||
<span
|
||||
ref={actionSectionRef}
|
||||
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded"
|
||||
onClick={() => setIsMenuActive(!isMenuActive)}
|
||||
>
|
||||
<MoreHorizontal className="size-4" />
|
||||
<MoreHorizontal className="size-3" />
|
||||
</span>
|
||||
}
|
||||
menuButtonOnClick={() => setIsMenuActive(!isMenuActive)}
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none flex-shrink-0 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
|
||||
{
|
||||
|
|
@ -244,6 +244,7 @@ export const FavoriteFolder: React.FC<Props> = (props) => {
|
|||
)}
|
||||
customButtonClassName="grid place-items-center"
|
||||
placement="bottom-start"
|
||||
ariaLabel={t("aria_labels.projects_sidebar.toggle_quick_actions_menu")}
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={() => handleRemoveFromFavorites(favorite)}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
|
|
@ -267,9 +268,12 @@ export const FavoriteFolder: React.FC<Props> = (props) => {
|
|||
"inline-block": isMenuActive,
|
||||
}
|
||||
)}
|
||||
aria-label={t(
|
||||
open ? "aria_labels.projects_sidebar.close_folder" : "aria_labels.projects_sidebar.open_folder"
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
className={cn("size-3 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": open,
|
||||
})}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
"use client";
|
||||
import React, { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { MoreHorizontal, Star } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { IFavorite } from "@plane/types";
|
||||
// ui
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
|
|
@ -15,19 +17,22 @@ type Props = {
|
|||
handleRemoveFromFavorites: (favorite: IFavorite) => void;
|
||||
};
|
||||
|
||||
export const FavoriteItemQuickAction: FC<Props> = (props) => {
|
||||
export const FavoriteItemQuickAction: FC<Props> = observer((props) => {
|
||||
const { ref, isMenuActive, onChange, handleRemoveFromFavorites, favorite } = props;
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<span
|
||||
ref={ref}
|
||||
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded"
|
||||
onClick={() => onChange(!isMenuActive)}
|
||||
>
|
||||
<MoreHorizontal className="size-4" />
|
||||
</span>
|
||||
}
|
||||
menuButtonOnClick={() => onChange(!isMenuActive)}
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none flex-shrink-0 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
|
||||
{
|
||||
|
|
@ -36,6 +41,7 @@ export const FavoriteItemQuickAction: FC<Props> = (props) => {
|
|||
)}
|
||||
customButtonClassName="grid place-items-center"
|
||||
placement="bottom-start"
|
||||
ariaLabel={t("aria_labels.projects_sidebar.toggle_quick_actions_menu")}
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={() => handleRemoveFromFavorites(favorite)}>
|
||||
<span className="flex items-center justify-start gap-2">
|
||||
|
|
@ -45,4 +51,4 @@ export const FavoriteItemQuickAction: FC<Props> = (props) => {
|
|||
</CustomMenu.MenuItem>
|
||||
</CustomMenu>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,13 +34,12 @@ import { getInstructionFromPayload, TargetData } from "./favorites.helpers";
|
|||
import { NewFavoriteFolder } from "./new-fav-folder";
|
||||
|
||||
export const SidebarFavoritesMenu = observer(() => {
|
||||
//state
|
||||
// states
|
||||
const [createNewFolder, setCreateNewFolder] = useState<boolean | string | null>(null);
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
// navigation
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const { t } = useTranslation();
|
||||
const { sidebarCollapsed } = useAppTheme();
|
||||
const {
|
||||
favoriteIds,
|
||||
|
|
@ -50,17 +49,17 @@ export const SidebarFavoritesMenu = observer(() => {
|
|||
reOrderFavorite,
|
||||
moveFavoriteToFolder,
|
||||
} = useFavorite();
|
||||
const { workspaceSlug } = useParams();
|
||||
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
// platform hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
// local storage
|
||||
const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage<boolean>(IS_FAVORITE_MENU_OPEN, false);
|
||||
// derived values
|
||||
const isFavoriteMenuOpen = !!storedValue;
|
||||
// refs
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const elementRef = useRef(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMoveToFolder = (sourceId: string, destinationId: string) => {
|
||||
moveFavoriteToFolder(workspaceSlug.toString(), sourceId, {
|
||||
|
|
@ -131,6 +130,7 @@ export const SidebarFavoritesMenu = observer(() => {
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemoveFromFavoritesFolder = (favoriteId: string) => {
|
||||
removeFromFavoriteFolder(workspaceSlug.toString(), favoriteId).catch(() => {
|
||||
setToast({
|
||||
|
|
@ -151,7 +151,7 @@ export const SidebarFavoritesMenu = observer(() => {
|
|||
});
|
||||
});
|
||||
},
|
||||
[workspaceSlug, reOrderFavorite]
|
||||
[workspaceSlug, reOrderFavorite, t]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -190,37 +190,68 @@ export const SidebarFavoritesMenu = observer(() => {
|
|||
<>
|
||||
<Disclosure as="div" defaultOpen ref={containerRef}>
|
||||
{!sidebarCollapsed && (
|
||||
<Disclosure.Button
|
||||
<div
|
||||
ref={elementRef}
|
||||
as="button"
|
||||
className={cn(
|
||||
"sticky top-0 bg-custom-sidebar-background-100 z-10 group/workspace-button w-full px-2 py-1.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded text-sm font-semibold",
|
||||
"group/favorites-button w-full flex items-center justify-between px-2 py-1.5 rounded text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90",
|
||||
{
|
||||
"bg-custom-sidebar-background-80 opacity-60": isDragging,
|
||||
"p-0 justify-center w-fit mx-auto bg-custom-sidebar-background-90 hover:bg-custom-sidebar-background-80":
|
||||
sidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span onClick={() => toggleFavoriteMenu(!isFavoriteMenuOpen)} className="flex-1 text-start">
|
||||
{t("favorites")}
|
||||
</span>
|
||||
<span className="flex flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded p-0.5 ">
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
className={cn(
|
||||
"w-full flex items-center gap-1 whitespace-nowrap text-left text-sm font-semibold text-custom-sidebar-text-400",
|
||||
{
|
||||
"!text-center w-8 px-2 py-1.5 justify-center": sidebarCollapsed,
|
||||
"bg-custom-sidebar-background-80 opacity-60": isDragging,
|
||||
}
|
||||
)}
|
||||
onClick={() => toggleFavoriteMenu(!isFavoriteMenuOpen)}
|
||||
aria-label={t(
|
||||
isFavoriteMenuOpen
|
||||
? "aria_labels.projects_sidebar.close_favorites_menu"
|
||||
: "aria_labels.projects_sidebar.open_favorites_menu"
|
||||
)}
|
||||
>
|
||||
<span className="text-sm font-semibold">{t("favorites")}</span>
|
||||
</Disclosure.Button>
|
||||
<div className="flex items-center opacity-0 pointer-events-none group-hover/favorites-button:opacity-100 group-hover/favorites-button:pointer-events-auto">
|
||||
<Tooltip tooltipHeading={t("create_folder")} tooltipContent="">
|
||||
<FolderPlus
|
||||
<button
|
||||
type="button"
|
||||
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0 grid place-items-center"
|
||||
onClick={() => {
|
||||
setCreateNewFolder(true);
|
||||
if (!isFavoriteMenuOpen) toggleFavoriteMenu(!isFavoriteMenuOpen);
|
||||
}}
|
||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform")}
|
||||
/>
|
||||
aria-label={t("aria_labels.projects_sidebar.create_favorites_folder")}
|
||||
>
|
||||
<FolderPlus className="size-3" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<ChevronRight
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0 grid place-items-center"
|
||||
onClick={() => toggleFavoriteMenu(!isFavoriteMenuOpen)}
|
||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": isFavoriteMenuOpen,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
</Disclosure.Button>
|
||||
aria-label={t(
|
||||
isFavoriteMenuOpen
|
||||
? "aria_labels.projects_sidebar.close_favorites_menu"
|
||||
: "aria_labels.projects_sidebar.open_favorites_menu"
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("flex-shrink-0 size-3 transition-all", {
|
||||
"rotate-90": isFavoriteMenuOpen,
|
||||
})}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Transition
|
||||
show={isFavoriteMenuOpen}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,14 @@ export const NewFavoriteFolder = observer((props: TProps) => {
|
|||
name="name"
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => <Input className="w-full" placeholder={t("new_folder")} {...field} />}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
className="w-full"
|
||||
placeholder={t("new_folder")}
|
||||
aria-label={t("aria_labels.projects_sidebar.enter_folder_name")}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -175,8 +175,13 @@ export const SidebarHelpSection: React.FC<WorkspaceHelpSectionProps> = observer(
|
|||
isCollapsed ? "w-full" : ""
|
||||
}`}
|
||||
onClick={() => toggleSidebar()}
|
||||
aria-label={t(
|
||||
isCollapsed
|
||||
? "aria_labels.projects_sidebar.expand_sidebar"
|
||||
: "aria_labels.projects_sidebar.collapse_sidebar"
|
||||
)}
|
||||
>
|
||||
<MoveLeft className={`h-4 w-4 duration-300 ${isCollapsed ? "rotate-180" : ""}`} />
|
||||
<MoveLeft className={`size-4 duration-300 ${isCollapsed ? "rotate-180" : ""}`} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -184,13 +184,13 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
const sourceId = source?.data?.id as string | undefined;
|
||||
const destinationId = self?.data?.id as string | undefined;
|
||||
|
||||
handleOnProjectDrop && handleOnProjectDrop(sourceId, destinationId, currentInstruction === "DRAG_BELOW");
|
||||
handleOnProjectDrop?.(sourceId, destinationId, currentInstruction === "DRAG_BELOW");
|
||||
|
||||
highlightIssueOnDrop(`sidebar-${sourceId}-${projectListType}`);
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [projectRef?.current, dragHandleRef?.current, projectId, isLastChild, projectListType, handleOnProjectDrop]);
|
||||
}, [projectId, isLastChild, projectListType, handleOnProjectDrop]);
|
||||
|
||||
useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false));
|
||||
useOutsideClickDetector(projectRef, () => projectRef?.current?.classList?.remove(HIGHLIGHT_CLASS));
|
||||
|
|
@ -284,6 +284,11 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
className={cn("flex-grow flex items-center gap-1.5 text-left select-none w-full", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
})}
|
||||
aria-label={
|
||||
isProjectListOpen
|
||||
? t("aria_labels.projects_sidebar.close_project_menu")
|
||||
: t("aria_labels.projects_sidebar.open_project_menu")
|
||||
}
|
||||
>
|
||||
<div className="size-4 grid place-items-center flex-shrink-0">
|
||||
<Logo logo={project.logo_props} size={16} />
|
||||
|
|
@ -310,6 +315,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
customButtonClassName="grid place-items-center"
|
||||
placement="bottom-start"
|
||||
ariaLabel={t("aria_labels.projects_sidebar.toggle_quick_actions_menu")}
|
||||
useCaptureForOutsideClick
|
||||
closeOnSelect
|
||||
>
|
||||
|
|
@ -384,6 +390,11 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
}
|
||||
)}
|
||||
onClick={() => setIsProjectListOpen(!isProjectListOpen)}
|
||||
aria-label={t(
|
||||
isProjectListOpen
|
||||
? "aria_labels.projects_sidebar.close_project_menu"
|
||||
: "aria_labels.projects_sidebar.open_project_menu"
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
|
|
|
|||
|
|
@ -167,12 +167,17 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
as="button"
|
||||
type="button"
|
||||
className={cn(
|
||||
"group w-full flex items-center gap-1 whitespace-nowrap text-left text-sm font-semibold text-custom-sidebar-text-400",
|
||||
"w-full flex items-center gap-1 whitespace-nowrap text-left text-sm font-semibold text-custom-sidebar-text-400",
|
||||
{
|
||||
"!text-center w-8 px-2 py-1.5 justify-center": isCollapsed,
|
||||
}
|
||||
)}
|
||||
onClick={() => toggleListDisclosure(!isAllProjectsListOpen)}
|
||||
aria-label={t(
|
||||
isAllProjectsListOpen
|
||||
? "aria_labels.projects_sidebar.close_projects_menu"
|
||||
: "aria_labels.projects_sidebar.open_projects_menu"
|
||||
)}
|
||||
>
|
||||
<Tooltip tooltipHeading={t("projects")} tooltipContent="" position="right" disabled={!isCollapsed}>
|
||||
<>
|
||||
|
|
@ -195,6 +200,7 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
setTrackElement(`APP_SIDEBAR_JOINED_BLOCK`);
|
||||
setIsProjectModalOpen(true);
|
||||
}}
|
||||
aria-label={t("aria_labels.projects_sidebar.create_new_project")}
|
||||
>
|
||||
<Plus className="size-3" />
|
||||
</button>
|
||||
|
|
@ -205,9 +211,14 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
type="button"
|
||||
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
|
||||
onClick={() => toggleListDisclosure(!isAllProjectsListOpen)}
|
||||
aria-label={t(
|
||||
isAllProjectsListOpen
|
||||
? "aria_labels.projects_sidebar.close_projects_menu"
|
||||
: "aria_labels.projects_sidebar.open_projects_menu"
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
className={cn("flex-shrink-0 size-4 transition-all", {
|
||||
className={cn("flex-shrink-0 size-3 transition-all", {
|
||||
"rotate-90": isAllProjectsListOpen,
|
||||
})}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@ export const SidebarQuickActions = observer(() => {
|
|||
|
||||
const handleMouseEnter = () => {
|
||||
// if enter before time out clear the timeout
|
||||
timeoutRef?.current && clearTimeout(timeoutRef.current);
|
||||
if (timeoutRef?.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
setIsDraftButtonOpen(true);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS,
|
||||
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS,
|
||||
} from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { SidebarNavItem } from "@/components/sidebar";
|
||||
|
|
@ -20,9 +21,10 @@ export const SidebarMenuItems = observer(() => {
|
|||
// routers
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const { sidebarCollapsed, toggleExtendedSidebar } = useAppTheme();
|
||||
const { sidebarCollapsed, extendedSidebarCollapsed, toggleExtendedSidebar } = useAppTheme();
|
||||
const { getNavigationPreferences } = useWorkspace();
|
||||
|
||||
// translation
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const currentWorkspaceNavigationPreferences = getNavigationPreferences(workspaceSlug.toString());
|
||||
|
||||
|
|
@ -39,31 +41,35 @@ export const SidebarMenuItems = observer(() => {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn("flex flex-col gap-0.5", {
|
||||
"space-y-0": sidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
{WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS.map((item, _index) => (
|
||||
<SidebarItem key={`static_${_index}`} item={item} />
|
||||
))}
|
||||
{sortedNavigationItems.map((item, _index) => (
|
||||
<SidebarItem key={`dynamic_${_index}`} item={item} />
|
||||
))}
|
||||
<SidebarNavItem className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}>
|
||||
<button
|
||||
onClick={() => toggleExtendedSidebar()}
|
||||
className={cn("flex items-center gap-1.5 text-sm font-medium flex-grow text-custom-text-350", {
|
||||
"justify-center": sidebarCollapsed,
|
||||
})}
|
||||
id="extended-sidebar-toggle"
|
||||
>
|
||||
<Ellipsis className="size-4" />
|
||||
{!sidebarCollapsed && <span>More</span>}
|
||||
</button>
|
||||
</SidebarNavItem>
|
||||
</div>
|
||||
</>
|
||||
<div
|
||||
className={cn("flex flex-col gap-0.5", {
|
||||
"space-y-0": sidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
{WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS.map((item, _index) => (
|
||||
<SidebarItem key={`static_${_index}`} item={item} />
|
||||
))}
|
||||
{sortedNavigationItems.map((item, _index) => (
|
||||
<SidebarItem key={`dynamic_${_index}`} item={item} />
|
||||
))}
|
||||
<SidebarNavItem className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleExtendedSidebar()}
|
||||
className={cn("flex items-center gap-1.5 text-sm font-medium flex-grow text-custom-text-350", {
|
||||
"justify-center": sidebarCollapsed,
|
||||
})}
|
||||
id="extended-sidebar-toggle"
|
||||
aria-label={t(
|
||||
extendedSidebarCollapsed
|
||||
? "aria_labels.projects_sidebar.open_extended_sidebar"
|
||||
: "aria_labels.projects_sidebar.close_extended_sidebar"
|
||||
)}
|
||||
>
|
||||
<Ellipsis className="flex-shrink-0 size-4" />
|
||||
{!sidebarCollapsed && <span>More</span>}
|
||||
</button>
|
||||
</SidebarNavItem>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue