diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx index 9db77e3..ee9fccc 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx @@ -15,12 +15,15 @@ import { Breadcrumbs, Header } from "@plane/ui"; import { BreadcrumbLink } from "@/components/common/breadcrumb-link"; // hooks import { useHome } from "@/hooks/store/use-home"; +import { useUserProfile } from "@/hooks/store/user"; export const WorkspaceDashboardHeader = observer(function WorkspaceDashboardHeader() { // plane hooks const { t } = useTranslation(); // hooks const { toggleWidgetSettings } = useHome(); + const { data: userProfile } = useUserProfile(); + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; return ( <> @@ -36,17 +39,19 @@ export const WorkspaceDashboardHeader = observer(function WorkspaceDashboardHead - - - + {isCompactToolbar && ( + + + + )} ); diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/project-shell-top-toolbar.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/project-shell-top-toolbar.tsx index 8ed6465..5064b68 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/project-shell-top-toolbar.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/project-shell-top-toolbar.tsx @@ -7,8 +7,6 @@ */ import { useMemo } from "react"; -import Link from "next/link"; -import { Menu } from "@headlessui/react"; import { observer } from "mobx-react"; import { useParams, usePathname } from "next/navigation"; import useSWR from "swr"; @@ -19,157 +17,31 @@ import { WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS, WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS, } from "@plane/constants"; -import { useTranslation } from "@plane/i18n"; -import { InboxIcon, PlusIcon, ProjectIcon } from "@plane/propel/icons"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -import { Tooltip } from "@plane/propel/tooltip"; -import { cn, copyUrlToClipboard, joinUrlPath } from "@plane/utils"; -import { TopNavPowerK } from "@/components/navigation"; +import { joinUrlPath } from "@plane/utils"; +import { openWorkspaceNotificationsModal } from "@/components/workspace-notifications/notifications-modal.utils"; +import { useWorkspaceNotifications } from "@/hooks/store/notifications"; import { useCommandPalette } from "@/hooks/store/use-command-palette"; import { useProject } from "@/hooks/store/use-project"; -import { useWorkspaceNotifications } from "@/hooks/store/notifications"; -import { useUser, useUserPermissions } from "@/hooks/store/user"; +import { useUser, useUserPermissions, useUserProfile } from "@/hooks/store/user"; import { usePersonalNavigationPreferences, useWorkspaceNavigationPreferences, } from "@/hooks/use-navigation-preferences"; -import { SidebarProjectsListItem } from "@/components/workspace/sidebar/projects-list-item"; -import { UserMenuRoot } from "@/components/workspace/sidebar/user-menu-root"; -import { WorkspaceMenuRoot } from "@/components/workspace/sidebar/workspace-menu-root"; -import { openWorkspaceNotificationsModal } from "@/components/workspace-notifications/notifications-modal.utils"; import { getSidebarNavigationItemIcon } from "@/plane-web/components/workspace/sidebar/helper"; - -type TToolbarItem = { - key: string; - href?: string; - labelTranslationKey: string; - active: boolean; - icon: React.ReactNode; - onClick?: () => void; -}; - -const ToolbarIconLink = ({ item }: { item: TToolbarItem }) => { - const { t } = useTranslation(); - - return ( - - - {item.icon} - - - ); -}; - -const ToolbarIconButton = ({ - label, - active = false, - children, - onClick, - disabled = false, -}: { - label: string; - active?: boolean; - children: React.ReactNode; - onClick?: () => void; - disabled?: boolean; -}) => ( - - - -); - -const ProjectsToolbarMenu = observer(function ProjectsToolbarMenu() { - const { t } = useTranslation(); - const pathname = usePathname(); - const { workspaceSlug } = useParams(); - const { joinedProjectIds } = useProject(); - const { toggleCreateProjectModal } = useCommandPalette(); - - const handleCopyText = (projectId: string) => - copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => { - setToast({ - type: TOAST_TYPE.SUCCESS, - title: t("link_copied"), - message: t("project_link_copied_to_clipboard"), - }); - }); - - return ( - - - - - - - - -
-
- {joinedProjectIds.map((projectId, index) => ( - handleCopyText(projectId)} - projectListType="JOINED" - disableDrag - disableDrop - isLastChild={index === joinedProjectIds.length - 1} - renderInToolbarMenu - /> - ))} -
-
- - - -
-
-
-
- ); -}); +import { + DEFAULT_PROJECT_SHELL_TOOLBAR_LAYOUT, + PROJECT_SHELL_TOOLBAR_LAYOUTS, + type TProjectShellToolbarLayout, + type TToolbarItem, +} from "./top-toolbar"; export const ProjectShellTopToolbar = observer(function ProjectShellTopToolbar() { - const { t } = useTranslation(); const pathname = usePathname(); const { workspaceSlug } = useParams(); const { toggleCreateIssueModal } = useCommandPalette(); const { joinedProjectIds } = useProject(); const { data: currentUser } = useUser(); + const { data: userProfile } = useUserProfile(); const { allowPermissions } = useUserPermissions(); const { unreadNotificationsCount, getUnreadNotificationsCount } = useWorkspaceNotifications(); const { preferences: personalPreferences } = usePersonalNavigationPreferences(); @@ -186,7 +58,7 @@ export const ProjectShellTopToolbar = observer(function ProjectShellTopToolbar() ); const isMentionsEnabled = unreadNotificationsCount.mention_unread_notifications_count > 0; - const totalNotifications = isMentionsEnabled + const notificationsCount = isMentionsEnabled ? unreadNotificationsCount.mention_unread_notifications_count : unreadNotificationsCount.total_unread_notifications_count; @@ -249,62 +121,27 @@ export const ProjectShellTopToolbar = observer(function ProjectShellTopToolbar() }).sort((a, b) => a.sort_order - b.sort_order), [pathname, workspacePreferences, workspaceSlug] ); + const workspaceSlugValue = workspaceSlug?.toString(); const isWorkspaceHome = pathname === `/${workspaceSlugValue}` || pathname === `/${workspaceSlugValue}/`; + const toolbarLayout: TProjectShellToolbarLayout = + userProfile?.theme?.nodedcCompactToolbar === true ? "compact" : DEFAULT_PROJECT_SHELL_TOOLBAR_LAYOUT; + const ToolbarLayout = PROJECT_SHELL_TOOLBAR_LAYOUTS[toolbarLayout]; return ( -
-
-
-
- - - - - - - toggleCreateIssueModal(true)} - disabled={!canCreateIssue || joinedProjectIds.length === 0} - > - - -
-
- -
-
- {primaryItems.map((item) => ( - - ))} -
- -
- - {secondaryItems.map((item) => ( - - ))} -
-
-
-
+ item.key === "drafts")} + homeItem={primaryItems.find((item) => item.key === "home")} + isWorkspaceHome={isWorkspaceHome} + joinedProjectIdsCount={joinedProjectIds.length} + notificationsCount={notificationsCount} + primaryItems={primaryItems} + profileItem={primaryItems.find((item) => item.key === "your_work")} + secondaryItems={secondaryItems} + stickiesItem={primaryItems.find((item) => item.key === "stickies")} + onCreateIssue={() => toggleCreateIssueModal(true)} + onOpenNotifications={() => openWorkspaceNotificationsModal()} + /> ); }); diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/header.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/header.tsx index 433f63b..14d5ccf 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/header.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/header.tsx @@ -21,6 +21,7 @@ import { useProject } from "@/hooks/store/use-project"; import { useAppRouter } from "@/hooks/use-app-router"; // plane web imports import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common"; +import { ExpandedToolbarBreadcrumbs } from "@/plane-web/components/breadcrumbs/expanded-toolbar-breadcrumbs"; import { ProjectFeatureBreadcrumb } from "@/plane-web/components/breadcrumbs/project-feature"; import { PageDetailsHeaderExtraActions } from "@/plane-web/components/pages"; import { EPageStoreType, usePage, usePageStore } from "@/plane-web/hooks/store"; @@ -68,7 +69,7 @@ export const PageDetailsHeader = observer(function PageDetailsHeader() {
- + } /> - +
diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx index 1ccfb26..08ef7cf 100644 --- a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(list)/header.tsx @@ -14,13 +14,14 @@ import { useTranslation } from "@plane/i18n"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TPage } from "@plane/types"; // plane ui -import { Breadcrumbs, Header } from "@plane/ui"; +import { Header } from "@plane/ui"; // components import { AppHeaderPrimaryActionButton } from "@/components/core/app-header/primary-action-button"; // hooks import { useProject } from "@/hooks/store/use-project"; // plane web imports import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common"; +import { ExpandedToolbarBreadcrumbs } from "@/plane-web/components/breadcrumbs/expanded-toolbar-breadcrumbs"; import { ProjectFeatureBreadcrumb } from "@/plane-web/components/breadcrumbs/project-feature"; import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store"; @@ -62,7 +63,7 @@ export const PagesListHeader = observer(function PagesListHeader() { return (
- + - + {canCurrentUserCreatePage && ( diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/compact-layout.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/compact-layout.tsx new file mode 100644 index 0000000..bbd859e --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/compact-layout.tsx @@ -0,0 +1,77 @@ +"use client"; + +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useTranslation } from "@plane/i18n"; +import { PlusIcon } from "@plane/propel/icons"; +import { cn } from "@plane/utils"; +// components +import { TopNavPowerK } from "@/components/navigation"; +import { UserMenuRoot } from "@/components/workspace/sidebar/user-menu-root"; +import { WorkspaceMenuRoot } from "@/components/workspace/sidebar/workspace-menu-root"; +import { ProjectsToolbarMenu } from "./projects-toolbar-menu"; +import { ToolbarIconButton, ToolbarIconLink, ToolbarNotificationsButton } from "./toolbar-controls"; +// types +import type { TProjectShellToolbarLayoutProps } from "./types"; + +export const CompactProjectShellToolbarLayout = ({ + canCreateIssue, + isWorkspaceHome, + joinedProjectIdsCount, + notificationsCount, + primaryItems, + secondaryItems, + onCreateIssue, + onOpenNotifications, +}: TProjectShellToolbarLayoutProps) => { + const { t } = useTranslation(); + + return ( +
+
+
+
+ + + + + + + +
+
+ +
+
+ {primaryItems.map((item) => ( + + ))} +
+ +
+ + {secondaryItems.map((item) => ( + + ))} +
+
+
+
+ ); +}; diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/expanded-layout.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/expanded-layout.tsx new file mode 100644 index 0000000..15deaaa --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/expanded-layout.tsx @@ -0,0 +1,91 @@ +"use client"; + +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useTranslation } from "@plane/i18n"; +import { Shapes } from "lucide-react"; +import { cn } from "@plane/utils"; +// components +import { TopNavPowerK } from "@/components/navigation"; +import { UserMenuRoot } from "@/components/workspace/sidebar/user-menu-root"; +import { WorkspaceMenuRoot } from "@/components/workspace/sidebar/workspace-menu-root"; +import { useHome } from "@/hooks/store/use-home"; +import { ProjectsToolbarMenu } from "./projects-toolbar-menu"; +import { ExpandedToolbarLink, ExpandedToolbarToolButton, ToolbarNotificationsButton } from "./toolbar-controls"; +// types +import type { TProjectShellToolbarLayoutProps } from "./types"; + +export const ExpandedProjectShellToolbarLayout = ({ + draftsItem, + homeItem, + isWorkspaceHome, + notificationsCount, + profileItem, + stickiesItem, + onOpenNotifications, +}: TProjectShellToolbarLayoutProps) => { + const { t } = useTranslation(); + const { toggleWidgetSettings } = useHome(); + + return ( +
+
+
+
+ NODE DC +
+ +
+ +
+ + + + +
+
+ +
+
+ + + +
+
+
+ +
+
+ {!isWorkspaceHome && ( +
+ +
+
+ )} +
+
+ {isWorkspaceHome && ( + toggleWidgetSettings(true)}> + + + )} +
+
+
+
+
+ ); +}; diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/index.ts b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/index.ts new file mode 100644 index 0000000..7bfbe64 --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/index.ts @@ -0,0 +1,2 @@ +export * from "./layout-registry"; +export * from "./types"; diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/layout-registry.ts b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/layout-registry.ts new file mode 100644 index 0000000..e7af678 --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/layout-registry.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import type { ComponentType } from "react"; +// components +import { CompactProjectShellToolbarLayout } from "./compact-layout"; +import { ExpandedProjectShellToolbarLayout } from "./expanded-layout"; +// types +import type { TProjectShellToolbarLayout, TProjectShellToolbarLayoutProps } from "./types"; + +export const DEFAULT_PROJECT_SHELL_TOOLBAR_LAYOUT: TProjectShellToolbarLayout = "expanded"; + +export const PROJECT_SHELL_TOOLBAR_LAYOUTS: Record< + TProjectShellToolbarLayout, + ComponentType +> = { + compact: CompactProjectShellToolbarLayout, + expanded: ExpandedProjectShellToolbarLayout, +}; diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/projects-toolbar-menu.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/projects-toolbar-menu.tsx new file mode 100644 index 0000000..0f8caa0 --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/projects-toolbar-menu.tsx @@ -0,0 +1,102 @@ +"use client"; + +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { Menu } from "@headlessui/react"; +import { observer } from "mobx-react"; +import { useParams, usePathname } from "next/navigation"; +import { useTranslation } from "@plane/i18n"; +import { PlusIcon, ProjectIcon } from "@plane/propel/icons"; +import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { cn, copyUrlToClipboard } from "@plane/utils"; +// hooks +import { useCommandPalette } from "@/hooks/store/use-command-palette"; +import { useProject } from "@/hooks/store/use-project"; +// components +import { SidebarProjectsListItem } from "@/components/workspace/sidebar/projects-list-item"; + +export const ProjectsToolbarMenu = observer(function ProjectsToolbarMenu({ + variant = "compact", +}: { + variant?: "compact" | "expanded"; +}) { + const { t } = useTranslation(); + const pathname = usePathname(); + const { workspaceSlug } = useParams(); + const { joinedProjectIds } = useProject(); + const { toggleCreateProjectModal } = useCommandPalette(); + + const handleCopyText = (projectId: string) => + copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => { + setToast({ + type: TOAST_TYPE.SUCCESS, + title: t("link_copied"), + message: t("project_link_copied_to_clipboard"), + }); + }); + + return ( + + + {variant === "expanded" ? null : ( + + + + )} + {variant === "expanded" && Проекты} + + + +
+
+ {joinedProjectIds.map((projectId, index) => ( + handleCopyText(projectId)} + projectListType="JOINED" + disableDrag + disableDrop + isLastChild={index === joinedProjectIds.length - 1} + renderInToolbarMenu + /> + ))} +
+
+ + + +
+
+
+
+ ); +}); diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/toolbar-controls.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/toolbar-controls.tsx new file mode 100644 index 0000000..bfeabb5 --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/toolbar-controls.tsx @@ -0,0 +1,118 @@ +"use client"; + +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import Link from "next/link"; +import type { ReactNode } from "react"; +import { useTranslation } from "@plane/i18n"; +import { InboxIcon } from "@plane/propel/icons"; +import { Tooltip } from "@plane/propel/tooltip"; +import { cn } from "@plane/utils"; +// types +import type { TToolbarItem } from "./types"; + +export const ToolbarIconLink = ({ item }: { item: TToolbarItem }) => { + const { t } = useTranslation(); + + return ( + + + {item.icon} + + + ); +}; + +export const ToolbarIconButton = ({ + label, + active = false, + children, + onClick, + disabled = false, +}: { + label: string; + active?: boolean; + children: ReactNode; + onClick?: () => void; + disabled?: boolean; +}) => ( + + + +); + +export const ToolbarNotificationsButton = ({ + label, + notificationsCount, + onClick, + variant = "compact", +}: { + label: string; + notificationsCount: number; + onClick: () => void; + variant?: "compact" | "expanded"; +}) => ( + + + +); + +export const ExpandedToolbarLink = ({ item, label }: { item?: TToolbarItem; label: string }) => { + if (!item?.href) return null; + + return ( + + {label} + + ); +}; + +export const ExpandedToolbarToolButton = ({ + label, + children, + onClick, +}: { + label: string; + children: ReactNode; + onClick?: () => void; +}) => ( + + + +); diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/types.ts b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/types.ts new file mode 100644 index 0000000..7582048 --- /dev/null +++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/top-toolbar/types.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import type { ReactNode } from "react"; + +export type TProjectShellToolbarLayout = "compact" | "expanded"; + +export type TToolbarItem = { + key: string; + href?: string; + labelTranslationKey: string; + active: boolean; + icon: ReactNode; + onClick?: () => void; +}; + +export type TProjectShellToolbarLayoutProps = { + canCreateIssue: boolean; + draftsItem?: TToolbarItem; + homeItem?: TToolbarItem; + isWorkspaceHome: boolean; + joinedProjectIdsCount: number; + notificationsCount: number; + primaryItems: TToolbarItem[]; + profileItem?: TToolbarItem; + secondaryItems: TToolbarItem[]; + stickiesItem?: TToolbarItem; + onCreateIssue: () => void; + onOpenNotifications: () => void; +}; diff --git a/plane-src/apps/web/ce/components/breadcrumbs/common.tsx b/plane-src/apps/web/ce/components/breadcrumbs/common.tsx index 9040cb2..31aa43d 100644 --- a/plane-src/apps/web/ce/components/breadcrumbs/common.tsx +++ b/plane-src/apps/web/ce/components/breadcrumbs/common.tsx @@ -6,18 +6,28 @@ // local components import { useProjectNavigationPreferences } from "@/hooks/use-navigation-preferences"; +import { useUserProfile } from "@/hooks/store/user"; import { ProjectBreadcrumb } from "./project"; type TCommonProjectBreadcrumbProps = { workspaceSlug: string; projectId: string; + shouldTruncate?: boolean; }; export function CommonProjectBreadcrumbs(props: TCommonProjectBreadcrumbProps) { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, shouldTruncate } = props; // preferences const { preferences: projectPreferences } = useProjectNavigationPreferences(); + const { data: userProfile } = useUserProfile(); + const shouldUseCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; if (projectPreferences.navigationMode === "TABBED") return null; - return ; + return ( + + ); } diff --git a/plane-src/apps/web/ce/components/breadcrumbs/expanded-toolbar-breadcrumbs.tsx b/plane-src/apps/web/ce/components/breadcrumbs/expanded-toolbar-breadcrumbs.tsx new file mode 100644 index 0000000..dfe4485 --- /dev/null +++ b/plane-src/apps/web/ce/components/breadcrumbs/expanded-toolbar-breadcrumbs.tsx @@ -0,0 +1,59 @@ +"use client"; + +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useEffect, useState } from "react"; +import type { ReactNode } from "react"; +import { createPortal } from "react-dom"; +import { observer } from "mobx-react"; +import { Breadcrumbs } from "@plane/ui"; +import { useUserProfile } from "@/hooks/store/user"; + +type TExpandedToolbarBreadcrumbsProps = { + children: ReactNode; + isLoading?: boolean; + onBack?: () => void; +}; + +export const ExpandedToolbarBreadcrumbs = observer(function ExpandedToolbarBreadcrumbs( + props: TExpandedToolbarBreadcrumbsProps +) { + const { children, isLoading = false, onBack } = props; + const { data: userProfile } = useUserProfile(); + const [target, setTarget] = useState(null); + + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; + + useEffect(() => { + if (isCompactToolbar || typeof document === "undefined") { + setTarget(null); + return; + } + + const animationFrame = window.requestAnimationFrame(() => { + setTarget(document.querySelector("[data-nodedc-expanded-breadcrumbs-slot]")); + }); + + return () => window.cancelAnimationFrame(animationFrame); + }, [isCompactToolbar]); + + const content = ( +
+ + {children} + +
+ ); + + if (!isCompactToolbar && target) return createPortal(content, target); + + return content; +}); diff --git a/plane-src/apps/web/ce/components/breadcrumbs/project.tsx b/plane-src/apps/web/ce/components/breadcrumbs/project.tsx index 796b98c..c303b78 100644 --- a/plane-src/apps/web/ce/components/breadcrumbs/project.tsx +++ b/plane-src/apps/web/ce/components/breadcrumbs/project.tsx @@ -19,10 +19,11 @@ import { BreadcrumbNavigationSearchDropdown } from "@plane/ui"; type TProjectBreadcrumbProps = { workspaceSlug: string; projectId: string; + shouldTruncate?: boolean; }; export const ProjectBreadcrumb = observer(function ProjectBreadcrumb(props: TProjectBreadcrumbProps) { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, shouldTruncate = true } = props; // router const router = useAppRouter(); // store hooks @@ -69,7 +70,7 @@ export const ProjectBreadcrumb = observer(function ProjectBreadcrumb(props: TPro title={currentProjectDetails?.name} icon={renderIcon(currentProjectDetails)} openOnLabelClick - shouldTruncate + shouldTruncate={shouldTruncate} /> ); }); diff --git a/plane-src/apps/web/ce/components/issues/header.tsx b/plane-src/apps/web/ce/components/issues/header.tsx index fe2867e..58bc2fd 100644 --- a/plane-src/apps/web/ce/components/issues/header.tsx +++ b/plane-src/apps/web/ce/components/issues/header.tsx @@ -21,7 +21,7 @@ import { useTranslation } from "@plane/i18n"; import { NewTabIcon } from "@plane/propel/icons"; import { Tooltip } from "@plane/propel/tooltip"; import { EIssuesStoreType } from "@plane/types"; -import { Breadcrumbs, Header } from "@plane/ui"; +import { Header } from "@plane/ui"; import { CountChip } from "@/components/common/count-chip"; import { AppHeaderPrimaryActionButton } from "@/components/core/app-header/primary-action-button"; // constants @@ -31,11 +31,12 @@ import { HeaderFilters } from "@/components/issues/filters"; import { useCommandPalette } from "@/hooks/store/use-command-palette"; import { useIssues } from "@/hooks/store/use-issues"; import { useProject } from "@/hooks/store/use-project"; -import { useUserPermissions } from "@/hooks/store/user"; +import { useUserPermissions, useUserProfile } from "@/hooks/store/user"; import { useAppRouter } from "@/hooks/use-app-router"; import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web imports import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common"; +import { ExpandedToolbarBreadcrumbs } from "@/plane-web/components/breadcrumbs/expanded-toolbar-breadcrumbs"; import { ProjectFeatureBreadcrumb } from "@/plane-web/components/breadcrumbs/project-feature"; export const IssuesHeader = observer(function IssuesHeader() { @@ -53,40 +54,46 @@ export const IssuesHeader = observer(function IssuesHeader() { const { toggleCreateIssueModal } = useCommandPalette(); const { allowPermissions } = useUserPermissions(); + const { data: userProfile } = useUserProfile(); const { isMobile } = usePlatformOS(); const SPACE_APP_URL = (SPACE_BASE_URL.trim() === "" ? window.location.origin : SPACE_BASE_URL) + SPACE_BASE_PATH; const publishedURL = `${SPACE_APP_URL}/issues/${currentProjectDetails?.anchor}`; const issuesCount = getGroupIssueCount(undefined, undefined, false); + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; const canUserCreateIssue = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT ); + const breadcrumbsContent = ( + <> + router.back()} isLoading={loader === "init-loader"}> + + + + {isCompactToolbar && issuesCount && issuesCount > 0 ? ( + + + + ) : null} + + ); + return (
-
- router.back()} isLoading={loader === "init-loader"} className="flex-grow-0"> - - - - {issuesCount && issuesCount > 0 ? ( - - - - ) : null} -
+ {breadcrumbsContent} {currentProjectDetails?.anchor ? (
- + - + {(loader === "mutation-loading" || loader === "issue-loading") && (
diff --git a/plane-src/apps/web/ce/components/projects/settings/intake/header.tsx b/plane-src/apps/web/ce/components/projects/settings/intake/header.tsx index d9fd894..c587725 100644 --- a/plane-src/apps/web/ce/components/projects/settings/intake/header.tsx +++ b/plane-src/apps/web/ce/components/projects/settings/intake/header.tsx @@ -11,7 +11,7 @@ import { RefreshCcw } from "lucide-react"; // ui import { EProjectFeatureKey, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { Breadcrumbs, Header } from "@plane/ui"; +import { Header } from "@plane/ui"; // components import { AppHeaderPrimaryActionButton } from "@/components/core/app-header/primary-action-button"; import { FiltersRoot } from "@/components/inbox/inbox-filter"; @@ -22,6 +22,7 @@ import { useProjectInbox } from "@/hooks/store/use-project-inbox"; import { useUserPermissions } from "@/hooks/store/user"; // plane web imports import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common"; +import { ExpandedToolbarBreadcrumbs } from "@/plane-web/components/breadcrumbs/expanded-toolbar-breadcrumbs"; import { ProjectFeatureBreadcrumb } from "@/plane-web/components/breadcrumbs/project-feature"; export const ProjectInboxHeader = observer(function ProjectInboxHeader() { @@ -46,7 +47,7 @@ export const ProjectInboxHeader = observer(function ProjectInboxHeader() {
- + - + {loader === "pagination-loading" && (
diff --git a/plane-src/apps/web/core/components/core/app-header.tsx b/plane-src/apps/web/core/components/core/app-header.tsx index fab9005..8f29b3a 100644 --- a/plane-src/apps/web/core/components/core/app-header.tsx +++ b/plane-src/apps/web/core/components/core/app-header.tsx @@ -11,6 +11,7 @@ import { observer } from "mobx-react"; import { Row } from "@plane/ui"; // components import { cn } from "@plane/utils"; +import { useUserProfile } from "@/hooks/store/user"; import { ExtendedAppHeader } from "@/plane-web/components/common/extended-app-header"; export interface AppHeaderProps { @@ -24,6 +25,13 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) { const { header, mobileHeader, className, rowClassName } = props; const containerRef = useRef(null); const [dockStyle, setDockStyle] = useState(undefined); + const { data: userProfile } = useUserProfile(); + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; + const effectiveDockStyle = isCompactToolbar + ? dockStyle + : { + left: typeof dockStyle?.left === "number" ? dockStyle.left : 0, + }; useEffect(() => { if (typeof window === "undefined") return; @@ -61,7 +69,15 @@ export const AppHeader = observer(function AppHeader(props: AppHeaderProps) { }, []); return ( -
+
; export const AppHeaderPrimaryActionButton = (props: TPrimaryActionButtonProps) => { - const { children, className, ...buttonProps } = props; + const { children, className, disabled, onClick, ...buttonProps } = props; const { t } = useTranslation(); + const { data: userProfile } = useUserProfile(); + const [expandedToolbarTarget, setExpandedToolbarTarget] = useState(null); + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; + + useEffect(() => { + if (isCompactToolbar || typeof document === "undefined") { + setExpandedToolbarTarget(null); + return; + } + + const animationFrame = window.requestAnimationFrame(() => { + setExpandedToolbarTarget(document.querySelector("[data-nodedc-expanded-primary-action-slot]")); + }); + + return () => window.cancelAnimationFrame(animationFrame); + }, [isCompactToolbar]); + + if (!isCompactToolbar) { + if (!expandedToolbarTarget) return null; + + return createPortal( + + + , + expandedToolbarTarget + ); + } return ( - ) : ( - <> - )}
+ + )} ); }); diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/base-kanban-root.tsx index 3871c3f..9b05c88 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -296,10 +296,10 @@ export const BaseKanBanRoot = observer(function BaseKanBanRoot(props: IBaseKanBa
-
+
{sub_group_by === null && ( -
+
{shouldShowQuickAdd && ( -
+
- + { const { variant = "top-navigation" } = props; const { t } = useTranslation(); + const isWideSearch = variant === "top-navigation" || variant === "expanded-toolbar"; + const isExpandedToolbar = variant === "expanded-toolbar"; // router const router = useAppRouter(); const params = useParams(); @@ -287,44 +289,99 @@ export const TopNavPowerK = observer((props: TTopNavPowerKProps) => { return (
- {variant === "top-navigation" ? ( -
-
inputRef.current?.focus()} - role="button" - > - - { - setSearchTerm(e.target.value); - if (!isOpen) openPanel(); + {isWideSearch ? ( + isExpandedToolbar ? ( +
+
{ + openPanel(); + requestAnimationFrame(() => inputRef.current?.focus()); }} - onMouseDown={handleMouseDown} - onFocus={handleFocus} - onKeyDown={handleKeyDown} - placeholder={t("power_k.search_menu.quick_command_placeholder")} - className="placeholder-text-placeholder min-w-0 flex-1 bg-transparent text-13 text-primary outline-none" - /> - {searchTerm && ( - - )} + role="button" + > +
+ { + setSearchTerm(e.target.value); + if (!isOpen) openPanel(); + }} + onMouseDown={handleMouseDown} + onFocus={handleFocus} + onKeyDown={handleKeyDown} + placeholder="" + tabIndex={isOpen ? 0 : -1} + className="nodedc-expanded-search-input placeholder-text-placeholder min-w-0 flex-1 bg-transparent outline-none" + /> + {searchTerm && ( + + )} +
+
+
-
+ ) : ( +
+
inputRef.current?.focus()} + role="button" + > + + + + { + setSearchTerm(e.target.value); + if (!isOpen) openPanel(); + }} + onMouseDown={handleMouseDown} + onFocus={handleFocus} + onKeyDown={handleKeyDown} + placeholder={t("power_k.search_menu.quick_command_placeholder")} + className="placeholder-text-placeholder min-w-0 flex-1 bg-transparent text-13 text-primary outline-none" + /> + {searchTerm && ( + + )} +
+
+ ) ) : (
)} - {variant === "top-navigation" && ( + {isWideSearch && isExpandedToolbar && ( +
+ {isOpen && searchCommandContent} +
+ )} + {isWideSearch && !isExpandedToolbar && (
diff --git a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx index 37b7993..4146860 100644 --- a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx +++ b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/default-list.tsx @@ -9,6 +9,7 @@ import { observer } from "mobx-react"; import { ThemeSwitcher } from "@/plane-web/components/preferences/theme-switcher"; // local imports import { ProfileSettingsAccentColor } from "./accent-color"; +import { ProfileSettingsToolbarLayout } from "./toolbar-layout"; export const ProfileSettingsDefaultPreferencesList = observer(function ProfileSettingsDefaultPreferencesList() { return ( @@ -21,6 +22,7 @@ export const ProfileSettingsDefaultPreferencesList = observer(function ProfileSe }} /> +
); }); diff --git a/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/toolbar-layout.tsx b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/toolbar-layout.tsx new file mode 100644 index 0000000..c2785e1 --- /dev/null +++ b/plane-src/apps/web/core/components/settings/profile/content/pages/preferences/toolbar-layout.tsx @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import { useState } from "react"; +import { observer } from "mobx-react"; +import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { cn } from "@plane/utils"; +// components +import { SettingsControlItem } from "@/components/settings/control-item"; +// hooks +import { useUserProfile } from "@/hooks/store/user"; + +export const ProfileSettingsToolbarLayout = observer(function ProfileSettingsToolbarLayout() { + const { data: userProfile, updateUserTheme } = useUserProfile(); + const [isSaving, setIsSaving] = useState(false); + + const isCompactToolbar = userProfile?.theme?.nodedcCompactToolbar === true; + + const handleToggle = async () => { + const nextValue = !isCompactToolbar; + + try { + setIsSaving(true); + await updateUserTheme({ nodedcCompactToolbar: nextValue }); + setToast({ + type: TOAST_TYPE.SUCCESS, + title: "Сохранено", + message: nextValue ? "Компактная панель инструментов включена." : "Расширенная панель инструментов включена.", + }); + } catch (_error) { + setToast({ + type: TOAST_TYPE.ERROR, + title: "Ошибка", + message: "Не удалось обновить режим панели инструментов.", + }); + } finally { + setIsSaving(false); + } + }; + + return ( + + + + {isCompactToolbar && } + + + Компактный режим + + {isCompactToolbar + ? "Все основные действия собраны в короткие иконки." + : "Основные разделы вынесены в расширенную верхнюю навигацию."} + + + + + {isCompactToolbar ? "Компактно" : "Расширенно"} + + + } + /> + ); +}); diff --git a/plane-src/apps/web/core/components/voice-tasker/global-control.tsx b/plane-src/apps/web/core/components/voice-tasker/global-control.tsx index ca6c925..5656436 100644 --- a/plane-src/apps/web/core/components/voice-tasker/global-control.tsx +++ b/plane-src/apps/web/core/components/voice-tasker/global-control.tsx @@ -866,7 +866,10 @@ export function VoiceTaskerGlobalControl({ workspaceSlug }: Props) { if (typeof document === "undefined") return; const updateDockSlot = () => { - setDockSlot(document.querySelector("[data-nodedc-voice-task-dock-slot]")); + setDockSlot( + document.querySelector("[data-nodedc-voice-task-toolbar-slot]") ?? + document.querySelector("[data-nodedc-voice-task-dock-slot]") + ); }; updateDockSlot(); diff --git a/plane-src/apps/web/core/components/workspace/logo.tsx b/plane-src/apps/web/core/components/workspace/logo.tsx index 7255c3f..03e646f 100644 --- a/plane-src/apps/web/core/components/workspace/logo.tsx +++ b/plane-src/apps/web/core/components/workspace/logo.tsx @@ -24,13 +24,13 @@ export const WorkspaceLogo = observer(function WorkspaceLogo(props: Props) { className={cn( `relative grid h-6 w-6 flex-shrink-0 place-items-center uppercase ${ !props.logo && "rounded-md bg-accent-primary text-on-color" - } ${props.classNames ? props.classNames : ""}` + } ${props.logo && "rounded-md"} ${props.classNames ? props.classNames : ""}` )} > {props.logo && props.logo !== "" ? ( {t("aria_labels.projects_sidebar.workspace_logo")} ) : ( diff --git a/plane-src/apps/web/core/components/workspace/sidebar/dropdown-item.tsx b/plane-src/apps/web/core/components/workspace/sidebar/dropdown-item.tsx index 894eb56..f522bdb 100644 --- a/plane-src/apps/web/core/components/workspace/sidebar/dropdown-item.tsx +++ b/plane-src/apps/web/core/components/workspace/sidebar/dropdown-item.tsx @@ -6,8 +6,9 @@ import { observer } from "mobx-react"; import Link from "next/link"; -import { useParams } from "next/navigation"; -import { Settings, UserPlus } from "lucide-react"; +import { useParams, useRouter } from "next/navigation"; +import type { MouseEvent } from "react"; +import { Archive, BarChart3, Layers3, Settings, UserPlus } from "lucide-react"; import { Menu } from "@headlessui/react"; // plane imports import { EUserPermissions } from "@plane/constants"; @@ -31,8 +32,18 @@ const SidebarDropdownItem = observer(function SidebarDropdownItem(props: TProps) const { workspace, activeWorkspace, handleItemClick, handleWorkspaceNavigation, handleClose } = props; // router const { workspaceSlug } = useParams(); + const router = useRouter(); // hooks const { t } = useTranslation(); + const canOpenWorkspaceSettings = [EUserPermissions.ADMIN, EUserPermissions.MEMBER].includes(workspace?.role); + const canInviteMembers = [EUserPermissions.ADMIN].includes(workspace?.role); + + const handleWorkspaceAction = (e: MouseEvent, action: () => void) => { + e.preventDefault(); + e.stopPropagation(); + action(); + handleClose(); + }; return ( {workspace.id === activeWorkspace?.id && ( <> -
- {[EUserPermissions.ADMIN, EUserPermissions.MEMBER].includes(workspace?.role) && ( +
+ {canOpenWorkspaceSettings && ( )} - {[EUserPermissions.ADMIN].includes(workspace?.role) && ( - { - e.stopPropagation(); - handleClose(); - }} - className="flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-[1.25rem] border-0 bg-white/[0.05] px-5 py-2.5 text-secondary shadow-none outline-none transition-colors hover:bg-white/[0.09] hover:text-primary" + {canInviteMembers && ( + + )} + {canOpenWorkspaceSettings && ( + + )} + + {canOpenWorkspaceSettings && ( + )}
diff --git a/plane-src/apps/web/core/components/workspace/sidebar/user-menu-root.tsx b/plane-src/apps/web/core/components/workspace/sidebar/user-menu-root.tsx index d5116c5..c67782e 100644 --- a/plane-src/apps/web/core/components/workspace/sidebar/user-menu-root.tsx +++ b/plane-src/apps/web/core/components/workspace/sidebar/user-menu-root.tsx @@ -22,7 +22,7 @@ import { useCommandPalette } from "@/hooks/store/use-command-palette"; import { useUser } from "@/hooks/store/user"; type TUserMenuRootProps = { - variant?: "default" | "sidebar-utility" | "toolbar"; + variant?: "default" | "sidebar-utility" | "toolbar" | "expanded-toolbar"; }; export const UserMenuRoot = observer(function UserMenuRoot(props: TUserMenuRootProps) { @@ -43,6 +43,7 @@ export const UserMenuRoot = observer(function UserMenuRoot(props: TUserMenuRootP const isSidebarUtilityVariant = variant === "sidebar-utility"; const isToolbarVariant = variant === "toolbar"; + const isExpandedToolbarVariant = variant === "expanded-toolbar"; const handleSignOut = () => { signOut().catch(() => @@ -137,16 +138,18 @@ export const UserMenuRoot = observer(function UserMenuRoot(props: TUserMenuRootP className="flex items-center" buttonAsChild button={ - isToolbarVariant ? ( + isToolbarVariant || isExpandedToolbarVariant ? ( diff --git a/plane-src/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx b/plane-src/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx index 6f42ff2..eb7fe45 100644 --- a/plane-src/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx +++ b/plane-src/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx @@ -31,7 +31,7 @@ import { WorkspaceLogo } from "../logo"; import SidebarDropdownItem from "./dropdown-item"; type WorkspaceMenuRootProps = { - variant: "sidebar" | "top-navigation" | "sidebar-panel" | "toolbar"; + variant: "sidebar" | "top-navigation" | "sidebar-panel" | "toolbar" | "expanded-toolbar"; }; type WorkspaceMenuStateSyncProps = { @@ -46,7 +46,12 @@ function WorkspaceMenuStateSync(props: WorkspaceMenuStateSyncProps) { const { open, variant, sidebarPanelButtonRef, onSidebarDropdownToggle, onSidebarPanelPositionChange } = props; const updateSidebarPanelMenuPosition = useCallback(() => { - if (!["sidebar-panel", "toolbar"].includes(variant) || !sidebarPanelButtonRef.current || typeof window === "undefined") return; + if ( + !["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant) || + !sidebarPanelButtonRef.current || + typeof window === "undefined" + ) + return; const rect = sidebarPanelButtonRef.current.getBoundingClientRect(); const width = 480; @@ -64,7 +69,7 @@ function WorkspaceMenuStateSync(props: WorkspaceMenuStateSyncProps) { }, [onSidebarDropdownToggle, open]); useLayoutEffect(() => { - if (!open || !["sidebar-panel", "toolbar"].includes(variant)) { + if (!open || !["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant)) { onSidebarPanelPositionChange(null); return; } @@ -133,7 +138,7 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work "w-full justify-center text-center": variant === "sidebar", "flex-grow justify-stretch text-left": variant === "top-navigation", "w-full max-w-none justify-stretch text-left": variant === "sidebar-panel", - "w-fit max-w-none justify-center text-center": variant === "toolbar", + "w-fit max-w-none justify-center text-center": ["toolbar", "expanded-toolbar"].includes(variant), })} > {({ open, close }: { open: boolean; close: () => void }) => { @@ -221,11 +226,12 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work /> )} - {variant === "toolbar" && ( + {["toolbar", "expanded-toolbar"].includes(variant) && ( )} @@ -247,15 +253,15 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work "z-21 mt-1 flex min-w-[30rem] origin-top-left flex-col divide-y overflow-hidden outline-none", { "fixed divide-subtle rounded-md border-[0.5px] border-strong bg-surface-1 shadow-raised-200": - !["sidebar-panel", "toolbar"].includes(variant), + !["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant), "top-11 left-14": variant === "sidebar", "top-10 left-4": variant === "top-navigation", "nodedc-glass-modal nodedc-glass-popup-surface rounded-[1.5rem] divide-white/10": - ["sidebar-panel", "toolbar"].includes(variant), + ["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant), } )} style={ - ["sidebar-panel", "toolbar"].includes(variant) && sidebarPanelMenuPosition + ["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant) && sidebarPanelMenuPosition ? { position: "fixed", left: `${sidebarPanelMenuPosition.left}px`, @@ -270,8 +276,8 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work className={cn( "sticky top-0 z-21 h-full w-full flex-shrink-0 truncate px-4 pt-3 pb-1 text-left text-13 font-medium text-placeholder", { - "rounded-md bg-surface-1": !["sidebar-panel", "toolbar"].includes(variant), - "bg-transparent": ["sidebar-panel", "toolbar"].includes(variant), + "rounded-md bg-surface-1": !["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant), + "bg-transparent": ["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant), } )} > @@ -343,7 +349,7 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work ); - if (["sidebar-panel", "toolbar"].includes(variant)) { + if (["sidebar-panel", "toolbar", "expanded-toolbar"].includes(variant)) { if (!open || !sidebarPanelMenuPosition || typeof document === "undefined") return null; return createPortal(menuItems, document.body); } diff --git a/plane-src/apps/web/core/store/user/profile.store.ts b/plane-src/apps/web/core/store/user/profile.store.ts index 3ef4739..96b41f4 100644 --- a/plane-src/apps/web/core/store/user/profile.store.ts +++ b/plane-src/apps/web/core/store/user/profile.store.ts @@ -46,6 +46,7 @@ export class ProfileStore implements IUserProfileStore { background: undefined, darkPalette: false, nodedcAccent: undefined, + nodedcCompactToolbar: undefined, }, onboarding_step: { workspace_join: false, diff --git a/plane-src/apps/web/public/nodedc-logo.svg b/plane-src/apps/web/public/nodedc-logo.svg new file mode 100644 index 0000000..92b19d8 --- /dev/null +++ b/plane-src/apps/web/public/nodedc-logo.svg @@ -0,0 +1 @@ + diff --git a/plane-src/apps/web/styles/globals.css b/plane-src/apps/web/styles/globals.css index 2974053..14aa2a8 100644 --- a/plane-src/apps/web/styles/globals.css +++ b/plane-src/apps/web/styles/globals.css @@ -450,6 +450,29 @@ overflow: hidden; } + .nodedc-app-header-expanded { + right: auto !important; + bottom: 0; + height: 0; + pointer-events: none; + } + + .nodedc-app-header-expanded .nodedc-bottom-dock { + display: none !important; + } + + .nodedc-app-header-expanded .nodedc-bottom-dock > [class~="w-full"] { + width: auto !important; + } + + .nodedc-app-header-expanded .nodedc-bottom-dock-left { + max-width: min(31rem, 48vw) !important; + } + + .nodedc-app-header-expanded .nodedc-bottom-dock-voice-slot { + display: none !important; + } + .nodedc-glass-modal [data-slot="button"], .nodedc-glass-modal [data-slot="icon-button"] { border: none !important; @@ -754,6 +777,516 @@ color: rgb(var(--nodedc-on-accent-rgb)); } + .nodedc-expanded-toolbar { + display: flex; + min-height: 4.25rem; + width: 100%; + flex-direction: column; + gap: 0; + } + + .nodedc-expanded-toolbar-top { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + min-height: 3rem; + width: 100%; + align-items: center; + gap: 1rem; + } + + .nodedc-expanded-toolbar-left, + .nodedc-expanded-toolbar-center, + .nodedc-expanded-toolbar-right { + display: flex; + align-items: center; + gap: 0.75rem; + min-width: 0; + } + + .nodedc-expanded-toolbar-left { + justify-content: flex-start; + } + + .nodedc-expanded-toolbar-center { + justify-content: center; + } + + .nodedc-expanded-toolbar-right { + justify-content: flex-end; + } + + .nodedc-expanded-brand-logo { + display: block; + width: 7.25rem; + height: auto; + max-height: 2.2rem; + object-fit: contain; + } + + .nodedc-expanded-user-group { + display: inline-flex; + height: 3.45rem; + min-height: 3.45rem; + align-items: center; + gap: 0.22rem; + border-radius: 999px; + background: rgba(64, 64, 64, 0.48); + padding: 0.32rem; + } + + .nodedc-expanded-user-group .nodedc-expanded-nav-button { + min-height: 2.78rem; + padding-inline: 1.2rem; + } + + .nodedc-expanded-user-group .nodedc-expanded-nav-button:not([data-active="true"]) { + color: rgba(255, 255, 255, 0.68); + } + + .nodedc-expanded-nav-group { + display: inline-flex; + min-height: 3.45rem; + align-items: center; + gap: 0.18rem; + border: 0 !important; + border-radius: 999px; + background: rgba(64, 64, 64, 0.48); + padding: 0.32rem; + box-shadow: none !important; + } + + .nodedc-expanded-toolbar-tools-row { + position: relative; + display: flex; + width: 100%; + min-height: 0; + align-items: center; + justify-content: flex-start; + gap: 0.45rem; + margin: 0; + } + + .nodedc-expanded-main-tool-cluster, + .nodedc-expanded-action-tool-cluster { + position: fixed; + z-index: 80; + display: inline-flex; + min-height: 3rem; + align-items: center; + gap: 0.45rem; + min-width: 0; + pointer-events: auto; + } + + .nodedc-expanded-main-tool-cluster { + left: 50%; + bottom: 1.1rem; + transform: translateX(-50%); + } + + .nodedc-expanded-action-tool-cluster { + right: 2.75rem; + bottom: 1.1rem; + } + + .nodedc-expanded-main-tool-cluster:empty, + .nodedc-expanded-action-tool-cluster:empty { + display: none; + } + + .nodedc-expanded-nav-button { + display: inline-flex !important; + align-items: center; + justify-content: center; + gap: 0; + min-height: 2.78rem; + border: 0 !important; + outline: none !important; + box-shadow: none !important; + border-radius: 999px !important; + background: transparent !important; + color: rgba(255, 255, 255, 0.68); + padding: 0.2rem 1.22rem; + font-size: 0.74rem; + font-weight: 700; + line-height: 1; + letter-spacing: 0; + white-space: nowrap; + transition: + background-color 160ms ease, + color 160ms ease, + opacity 160ms ease; + } + + .nodedc-expanded-nav-button:hover { + background: rgba(255, 255, 255, 0.07) !important; + color: rgba(255, 255, 255, 0.96); + } + + .nodedc-expanded-nav-button[data-active="true"] { + background: rgba(255, 255, 255, 0.92) !important; + color: rgba(8, 8, 10, 0.96); + } + + .nodedc-expanded-nav-icon { + display: grid; + width: 2.1rem; + height: 2.1rem; + flex-shrink: 0; + place-items: center; + border-radius: 999px; + background: rgba(255, 255, 255, 0.06); + color: rgba(255, 255, 255, 0.8); + } + + .nodedc-expanded-nav-button[data-active="true"] .nodedc-expanded-nav-icon { + background: rgb(var(--nodedc-accent-rgb)); + color: rgb(var(--nodedc-on-accent-rgb)); + } + + .nodedc-expanded-tool-button, + .nodedc-expanded-tool-slot .nodedc-bottom-dock-voice-button { + display: grid !important; + height: 3rem !important; + width: 3rem !important; + min-width: 3rem; + place-items: center; + border: 0 !important; + border-radius: 999px !important; + background: rgba(7, 7, 10, 0.94) !important; + color: rgba(255, 255, 255, 0.68) !important; + outline: none !important; + box-shadow: none !important; + transition: + background-color 180ms ease, + color 180ms ease, + transform 180ms ease; + } + + .nodedc-expanded-tool-button:hover, + .nodedc-expanded-tool-slot .nodedc-bottom-dock-voice-button:hover { + color: rgba(255, 255, 255, 0.92) !important; + } + + .nodedc-expanded-tool-button[data-active="true"] { + background: rgba(255, 255, 255, 0.94) !important; + color: rgba(8, 8, 10, 0.94) !important; + } + + .nodedc-expanded-tool-slot { + display: grid; + min-height: 3rem; + min-width: 3rem; + place-items: center; + } + + .nodedc-expanded-header-filters-slot, + .nodedc-expanded-primary-action-slot { + display: contents; + } + + .nodedc-expanded-header-filters-slot:empty, + .nodedc-expanded-primary-action-slot:empty, + .nodedc-expanded-breadcrumbs-slot:empty { + display: none; + } + + .nodedc-expanded-breadcrumbs-slot { + display: flex; + flex: 0 1 auto; + min-width: 0; + justify-content: flex-start; + position: fixed; + z-index: 80; + left: 1.85rem; + bottom: 1.1rem; + pointer-events: auto; + } + + main:has(.nodedc-expanded-toolbar) .nodedc-kanban-scroll-container { + height: calc(100% - 4.5rem) !important; + max-height: calc(100% - 4.5rem); + scrollbar-color: transparent transparent; + } + + main:has(.nodedc-expanded-toolbar) .nodedc-kanban-scroll-container::-webkit-scrollbar, + main:has(.nodedc-expanded-toolbar) .nodedc-kanban-scroll-container::-webkit-scrollbar-track, + main:has(.nodedc-expanded-toolbar) .nodedc-kanban-scroll-container::-webkit-scrollbar-thumb { + background: transparent !important; + border-color: transparent !important; + } + + .nodedc-expanded-breadcrumbs { + display: inline-flex !important; + flex: 0 1 auto !important; + max-width: min(52rem, 48vw); + height: 3rem; + align-items: center; + gap: 0.45rem !important; + overflow: visible !important; + border-radius: 0; + background: transparent; + padding: 0; + } + + .nodedc-expanded-breadcrumbs > div { + height: 3rem !important; + min-width: max-content; + flex-shrink: 0; + } + + .nodedc-expanded-breadcrumbs [class~="rounded-sm"] { + border-radius: 999px !important; + } + + .nodedc-expanded-breadcrumbs [class~="outline-none"], + .nodedc-expanded-breadcrumbs > div > .group { + min-height: 3rem; + border: 0 !important; + border-radius: 999px !important; + background: rgba(64, 64, 64, 0.52) !important; + outline: none !important; + box-shadow: none !important; + } + + .nodedc-expanded-breadcrumbs > div:first-child [class~="outline-none"], + .nodedc-expanded-breadcrumbs > div:first-child > .group { + background: rgba(7, 7, 10, 0.96) !important; + color: rgba(255, 255, 255, 0.86) !important; + } + + .nodedc-expanded-breadcrumbs > div:first-child [class*="text-"], + .nodedc-expanded-breadcrumbs > div:first-child svg { + color: rgba(255, 255, 255, 0.78) !important; + } + + .nodedc-expanded-breadcrumbs [class~="outline-none"] > div, + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="bg-layer-1"], + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="hover:bg-layer-1"], + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="hover:bg-surface-2"], + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="rounded-r-sm"], + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="rounded-r-none"] { + background: transparent !important; + } + + .nodedc-expanded-breadcrumbs [class~="outline-none"] [class~="bg-surface-1"] { + display: none !important; + } + + .nodedc-expanded-breadcrumbs [class~="truncate"] { + max-width: 18rem !important; + } + + .nodedc-expanded-breadcrumbs [class~="text-13"] { + font-size: 0.75rem !important; + font-weight: 700 !important; + letter-spacing: 0 !important; + } + + .nodedc-expanded-breadcrumbs [class~="px-1.5"] { + padding-left: 0.95rem !important; + padding-right: 0.95rem !important; + } + + .nodedc-expanded-breadcrumbs [class~="rounded-r-none"] { + border-top-right-radius: 999px !important; + border-bottom-right-radius: 999px !important; + } + + .nodedc-expanded-breadcrumbs [class~="rounded-r-sm"] { + margin-left: -0.65rem; + border-top-left-radius: 999px !important; + border-bottom-left-radius: 999px !important; + } + + .nodedc-expanded-breadcrumbs [class~="rounded-r-sm"] span[class*="bg-surface"], + .nodedc-expanded-breadcrumbs [class~="rounded-r-sm"] span[class*="bg-layer"] { + display: none !important; + } + + .nodedc-expanded-breadcrumbs [class~="text-tertiary"], + .nodedc-expanded-breadcrumbs [class~="text-placeholder"] { + color: rgba(255, 255, 255, 0.66) !important; + } + + .nodedc-expanded-breadcrumbs [class~="hover:bg-surface-2"]:hover, + .nodedc-expanded-breadcrumbs [class~="hover:bg-layer-1"]:hover { + background: transparent !important; + } + + .nodedc-expanded-breadcrumbs > div:last-child [class~="outline-none"], + .nodedc-expanded-breadcrumbs > div:last-child > .group { + background: rgba(255, 255, 255, 0.94) !important; + color: rgba(8, 8, 10, 0.96) !important; + } + + .nodedc-expanded-breadcrumbs > div:last-child > div [class~="text-tertiary"], + .nodedc-expanded-breadcrumbs > div:last-child > div [class~="text-placeholder"], + .nodedc-expanded-breadcrumbs > div:last-child > div [class~="text-primary"], + .nodedc-expanded-breadcrumbs > div:last-child [class*="text-"] { + color: rgba(8, 8, 10, 0.96) !important; + } + + .nodedc-expanded-breadcrumbs > div:last-child svg { + color: rgba(8, 8, 10, 0.86) !important; + } + + .nodedc-expanded-header-filters { + display: inline-flex; + align-items: center; + gap: 0.45rem; + } + + .nodedc-expanded-header-filters .nodedc-toolbar-group { + min-height: 3rem; + padding: 0.25rem; + } + + .nodedc-expanded-header-filters .nodedc-toolbar-filter-toggle { + display: grid !important; + height: 3rem !important; + width: 3rem !important; + min-width: 3rem !important; + place-items: center; + border: 0 !important; + border-radius: 999px !important; + background: rgba(7, 7, 10, 0.94) !important; + color: rgba(255, 255, 255, 0.68) !important; + outline: none !important; + box-shadow: none !important; + } + + .nodedc-expanded-header-filters .nodedc-toolbar-filter-toggle[data-active="true"] { + background: rgba(255, 255, 255, 0.94) !important; + color: rgba(8, 8, 10, 0.94) !important; + } + + .nodedc-expanded-search-control { + position: relative; + z-index: 30; + height: 3rem; + width: 3rem; + flex-shrink: 0; + overflow: visible; + } + + .nodedc-expanded-search-line-panel { + position: absolute; + top: 0; + right: 0; + z-index: 0; + height: 3rem; + width: min(36rem, 42vw); + overflow: hidden; + border-radius: 999px; + opacity: 0; + pointer-events: none; + transition: opacity 180ms ease; + } + + .nodedc-expanded-search-trigger { + position: relative; + z-index: 2; + } + + .nodedc-expanded-search-line-panel::before { + content: ""; + position: absolute; + right: 3.3rem; + bottom: 0.95rem; + left: 0; + height: 1px; + transform: scaleX(0); + transform-origin: right center; + background: rgba(255, 255, 255, 0.34); + transition: transform 230ms ease; + } + + .nodedc-expanded-search-control[data-open="true"] .nodedc-expanded-search-line-panel { + opacity: 1; + pointer-events: auto; + } + + .nodedc-expanded-search-control[data-open="true"] .nodedc-expanded-search-line-panel::before { + transform: scaleX(1); + } + + .nodedc-expanded-search-input-wrap { + display: flex; + height: 3rem; + width: 100%; + align-items: center; + padding: 0 3.3rem 0 0; + opacity: 0; + transition: opacity 170ms ease 100ms; + } + + .nodedc-expanded-search-control[data-open="true"] .nodedc-expanded-search-input-wrap { + opacity: 1; + } + + .nodedc-expanded-search-input { + height: 100%; + width: 100%; + text-transform: uppercase; + font-size: 1.08rem; + font-weight: 700; + letter-spacing: 0; + } + + .nodedc-expanded-search-clear { + display: grid; + height: 2rem; + width: 2rem; + flex-shrink: 0; + place-items: center; + color: rgba(255, 255, 255, 0.5); + } + + .nodedc-expanded-search-results { + top: calc(100% + 0.85rem); + right: 0; + width: min(36rem, 42vw); + border-radius: 1.45rem; + } + + .nodedc-expanded-main-tool-cluster .nodedc-expanded-search-results { + top: auto; + bottom: calc(100% + 0.85rem); + } + + .nodedc-expanded-notification-button { + height: 2.78rem; + width: 2.78rem; + background: transparent !important; + color: rgba(255, 255, 255, 0.68) !important; + } + + .nodedc-expanded-notification-button .nodedc-toolbar-icon-active-dot { + height: auto; + width: auto; + color: rgba(255, 255, 255, 0.68) !important; + } + + .nodedc-expanded-notification-button:hover, + .nodedc-expanded-notification-button:hover .nodedc-toolbar-icon-active-dot { + color: rgba(255, 255, 255, 0.94) !important; + } + + .nodedc-expanded-notification-button .nodedc-toolbar-notification-dot { + top: 0.58rem !important; + right: 0.58rem !important; + background: rgb(var(--nodedc-accent-rgb)) !important; + } + + .nodedc-expanded-user-avatar-button { + background: transparent !important; + } + + .nodedc-expanded-user-avatar-button:hover { + background: transparent !important; + } + .nodedc-toolbar-pill { position: relative; display: inline-flex !important; @@ -1289,8 +1822,7 @@ border: 0 !important; outline: none !important; background: - linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.026) 100%), - rgba(8, 9, 12, 0.78) !important; + linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.026) 100%), rgba(8, 9, 12, 0.78) !important; -webkit-backdrop-filter: blur(28px); backdrop-filter: blur(28px); } @@ -1720,8 +2252,7 @@ linear-gradient(180deg, rgba(255, 255, 255, 0.024) 0%, rgba(255, 255, 255, 0.008) 100%), rgb(var(--nodedc-card-active-rgb)) !important; color: rgb(var(--nodedc-on-card-active-rgb)) !important; - box-shadow: - 0 12px 32px rgba(0, 0, 0, 0.16) !important; + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16) !important; } .nodedc-external-card[data-active="true"] .text-primary { @@ -1940,6 +2471,8 @@ .nodedc-home-route-surface { min-height: 100vh; + padding-right: 1.25rem !important; + padding-left: 1.25rem !important; background: var(--background-color-surface-1) !important; } @@ -1948,7 +2481,8 @@ } .nodedc-home-page-shell { - max-width: min(1840px, calc(100vw - 5rem)); + max-width: none; + padding: 0 !important; } .nodedc-home-dashboard-shell { @@ -1972,11 +2506,267 @@ } .nodedc-home-main-column { + --nodedc-home-gantt-height: 35.75rem; + --nodedc-home-individual-chart-height: 11.25rem; display: flex; + min-width: 0; + min-height: 0; + height: 100%; flex-direction: column; gap: 0.75rem; } + .nodedc-home-analytics-rail { + display: flex; + min-width: 0; + height: auto; + flex-direction: column; + gap: 0.75rem; + overflow: hidden; + padding-right: 0; + scrollbar-color: rgba(var(--nodedc-card-active-rgb), 0.58) rgba(255, 255, 255, 0.035); + scrollbar-width: thin; + } + + .nodedc-home-analytics-rail::-webkit-scrollbar { + width: 0.45rem; + } + + .nodedc-home-analytics-rail::-webkit-scrollbar-track { + border-radius: 999px; + background: rgba(255, 255, 255, 0.035); + } + + .nodedc-home-analytics-rail::-webkit-scrollbar-thumb { + border-radius: 999px; + background: rgba(var(--nodedc-card-active-rgb), 0.58); + } + + .nodedc-home-analytics-stat-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; + } + + .nodedc-home-analytics-stat { + display: flex; + min-height: 4.2rem; + flex-direction: column; + justify-content: space-between; + border-radius: 1.15rem; + background: rgba(0, 0, 0, 0.18); + padding: 0.8rem; + } + + .nodedc-home-analytics-stat span { + color: var(--text-color-secondary); + font-size: 0.68rem; + font-weight: 650; + } + + .nodedc-home-analytics-stat strong { + color: var(--text-color-primary); + font-size: 1.35rem; + line-height: 1; + } + + .nodedc-home-analytics-rail .nodedc-external-section, + .nodedc-home-analytics-table-wrap, + .nodedc-home-individual-analytics, + .nodedc-home-assignee-analytics { + border: 0 !important; + border-radius: 1.5rem !important; + background: rgba(10, 10, 12, 0.7) !important; + padding: 1rem !important; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.014) !important; + -webkit-backdrop-filter: blur(24px); + backdrop-filter: blur(24px); + } + + .nodedc-home-analytics-rail .nodedc-external-section, + .nodedc-home-assignee-analytics, + .nodedc-home-analytics-recents { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0%, rgba(255, 255, 255, 0.006) 100%), rgba(10, 10, 12, 0.7) !important; + } + + .nodedc-home-analytics-rail > .nodedc-external-section, + .nodedc-home-analytics-rail > .nodedc-home-subpanel { + flex: 0 0 auto; + min-height: 15.75rem; + } + + .nodedc-home-analytics-rail .nodedc-home-subpanel { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0%, rgba(255, 255, 255, 0.006) 100%), rgba(10, 10, 12, 0.7) !important; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.014) !important; + } + + .nodedc-home-individual-analytics { + flex: 0 0 auto; + background: rgba(10, 10, 12, 0.72) !important; + } + + .nodedc-home-individual-analytics > .nodedc-external-section, + .nodedc-home-assignee-analytics > .nodedc-external-section { + height: 100%; + border: 0 !important; + border-radius: 0 !important; + background: transparent !important; + padding: 0 !important; + box-shadow: none !important; + } + + .nodedc-home-individual-analytics .nodedc-external-section > .mb-5, + .nodedc-home-analytics-rail .nodedc-external-section > .mb-5 { + margin-bottom: 0.85rem !important; + } + + .nodedc-home-individual-analytics .nodedc-external-section h1, + .nodedc-home-analytics-rail .nodedc-external-section h1 { + font-size: 0.92rem !important; + line-height: 1.2 !important; + } + + .nodedc-home-analytics-rail .nodedc-analytics-summary-card { + grid-template-columns: repeat(2, minmax(0, 1fr)); + min-height: 0; + overflow: hidden; + border-radius: 1.25rem !important; + } + + .nodedc-home-analytics-rail .nodedc-analytics-summary-item { + min-height: 4.35rem !important; + padding: 0.8rem !important; + } + + .nodedc-home-analytics-rail .nodedc-analytics-summary-item::before { + display: none; + } + + .nodedc-home-individual-analytics .h-\[350px\], + .nodedc-home-analytics-rail .h-\[350px\] { + height: 13.5rem !important; + } + + .nodedc-home-individual-analytics .h-\[350px\] { + height: calc(var(--nodedc-home-individual-chart-height) + 0.75rem) !important; + } + + .nodedc-home-individual-analytics .nodedc-analytics-chart-inner, + .nodedc-home-analytics-rail .nodedc-analytics-chart-inner { + min-width: 24rem; + height: 14rem !important; + } + + .nodedc-home-priority-cell .nodedc-analytics-chart-inner { + width: 100% !important; + min-width: 18rem; + height: 12rem !important; + } + + .nodedc-home-individual-analytics .nodedc-analytics-chart-stack, + .nodedc-home-analytics-rail .nodedc-analytics-chart-stack { + gap: 0.9rem !important; + } + + .nodedc-home-individual-analytics .nodedc-analytics-table-toolbar, + .nodedc-home-analytics-rail .nodedc-analytics-table-toolbar { + align-items: flex-start; + gap: 0.65rem; + } + + .nodedc-home-individual-analytics .nodedc-analytics-table-surface, + .nodedc-home-analytics-rail .nodedc-analytics-table-surface { + max-height: 13rem; + overflow: auto; + } + + .nodedc-home-individual-analytics .nodedc-analytics-chart-stack { + display: grid !important; + grid-template-columns: minmax(0, 1.1fr) minmax(19rem, 0.9fr); + align-items: stretch; + } + + .nodedc-home-individual-analytics .nodedc-analytics-chart-viewport, + .nodedc-home-individual-analytics .nodedc-analytics-table-surface { + height: var(--nodedc-home-individual-chart-height); + min-height: var(--nodedc-home-individual-chart-height); + border-radius: 1.25rem !important; + background: rgba(0, 0, 0, 0.18) !important; + } + + .nodedc-home-individual-analytics .nodedc-analytics-chart-inner { + width: 100% !important; + min-width: 22rem; + height: calc(var(--nodedc-home-individual-chart-height) - 0.75rem) !important; + } + + .nodedc-home-individual-analytics .nodedc-analytics-table-surface table { + width: 100%; + } + + .nodedc-home-analytics-bottom-row { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + gap: 0.75rem; + } + + .nodedc-home-assignee-analytics, + .nodedc-home-analytics-recents { + min-width: 0; + min-height: 22rem; + overflow: hidden; + } + + .nodedc-home-assignee-analytics .nodedc-analytics-table-surface { + max-height: 17.5rem; + overflow: auto; + } + + .nodedc-home-analytics-recents { + max-height: 24rem; + overflow: hidden; + } + + .nodedc-home-analytics-intro { + min-height: 10rem; + } + + .nodedc-home-activity-mini { + flex: 0 0 auto; + min-height: 15rem; + overflow: hidden; + } + + .nodedc-home-activity-mini-chart { + position: relative; + height: 10.5rem; + overflow: hidden; + border-radius: 1.25rem; + background: rgba(0, 0, 0, 0.16); + } + + .nodedc-home-bottom-widgets { + align-items: stretch; + } + + .nodedc-home-bottom-widgets .nodedc-home-card { + min-height: 16rem; + } + + .nodedc-home-bottom-widgets .nodedc-home-card > div { + height: 100%; + } + + @media (max-width: 1279px) { + .nodedc-home-individual-analytics .nodedc-analytics-chart-stack, + .nodedc-home-analytics-bottom-row { + grid-template-columns: 1fr; + } + } + .nodedc-home-hero { --nodedc-home-column-gap: 0.75rem; --nodedc-home-title-width: 13.25rem; @@ -2177,9 +2967,12 @@ .nodedc-home-gantt-card { position: relative; + display: flex; + flex-direction: column; overflow: hidden; isolation: isolate; - min-height: 30rem; + min-height: 0; + flex: 1 1 auto; border-radius: 2rem !important; background: #050506 !important; box-shadow: none !important; @@ -2441,7 +3234,10 @@ .nodedc-home-gantt-surface { position: relative; - min-height: 23.5rem; + flex: 1 1 var(--nodedc-home-gantt-height); + height: auto; + min-height: var(--nodedc-home-gantt-height); + max-height: none; margin: 0 1.25rem 1.25rem; overflow: hidden; border-radius: 1.75rem; @@ -2449,15 +3245,18 @@ } .nodedc-home-gantt-scroll { - min-height: 23.5rem; - overflow-x: auto; - overflow-y: hidden; + height: 100%; + min-height: 100%; + max-height: none; + overflow: auto; + overscroll-behavior: contain; scrollbar-color: rgba(var(--nodedc-card-active-rgb), 0.65) rgba(255, 255, 255, 0.04); scrollbar-width: thin; } .nodedc-home-gantt-scroll::-webkit-scrollbar { - height: 0.55rem; + width: 0.45rem; + height: 0.45rem; } .nodedc-home-gantt-scroll::-webkit-scrollbar-track { @@ -2472,7 +3271,7 @@ .nodedc-home-gantt-canvas { position: relative; - min-height: 23.5rem; + min-height: 100%; padding: 1rem; background: linear-gradient(180deg, rgba(255, 255, 255, 0.035) 1px, transparent 1px) 0 0 / 100% 4.2rem, @@ -3579,8 +4378,7 @@ .nodedc-home-operations-card { background: - linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0%, rgba(255, 255, 255, 0.006) 100%), - rgba(10, 10, 12, 0.68) !important; + linear-gradient(180deg, rgba(255, 255, 255, 0.022) 0%, rgba(255, 255, 255, 0.006) 100%), rgba(10, 10, 12, 0.68) !important; -webkit-backdrop-filter: blur(28px); backdrop-filter: blur(28px); } @@ -4257,5 +5055,4 @@ offset-distance: 100%; } } - } diff --git a/plane-src/packages/types/src/current-user/profile.ts b/plane-src/packages/types/src/current-user/profile.ts index 167ef13..e650131 100644 --- a/plane-src/packages/types/src/current-user/profile.ts +++ b/plane-src/packages/types/src/current-user/profile.ts @@ -14,6 +14,7 @@ export type TUserProfile = { theme: { theme: string | undefined; nodedcAccent?: string | undefined; + nodedcCompactToolbar?: boolean | undefined; }; onboarding_step: { diff --git a/plane-src/packages/types/src/users.ts b/plane-src/packages/types/src/users.ts index cdb264b..b5703d9 100644 --- a/plane-src/packages/types/src/users.ts +++ b/plane-src/packages/types/src/users.ts @@ -70,6 +70,7 @@ export type TUserProfile = { background: string | undefined; darkPalette: boolean | undefined; nodedcAccent?: string | undefined; + nodedcCompactToolbar?: boolean | undefined; }; onboarding_step: TOnboardingSteps; is_onboarded: boolean; @@ -109,6 +110,7 @@ export interface IUserTheme { background?: string | undefined; darkPalette?: boolean | undefined; nodedcAccent?: string | undefined; + nodedcCompactToolbar?: boolean | undefined; } export interface IUserMemberLite extends IUserLite {