From f57ac538bb655f3fcf5a12254c4ca9996ae5c6c3 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 10 Feb 2025 16:51:50 +0530 Subject: [PATCH 01/15] chore: workspace constant and types updated --- packages/constants/src/workspace.ts | 99 +++++++++++++++++++++++++---- packages/types/src/workspace.d.ts | 18 ++++-- 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/packages/constants/src/workspace.ts b/packages/constants/src/workspace.ts index 3bbb5b1f31f..c034984d5d9 100644 --- a/packages/constants/src/workspace.ts +++ b/packages/constants/src/workspace.ts @@ -84,48 +84,42 @@ export const WORKSPACE_SETTINGS = { i18n_label: "workspace_settings.settings.general.title", href: `/settings`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`, }, members: { key: "members", i18n_label: "workspace_settings.settings.members.title", href: `/settings/members`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/members/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`, }, "billing-and-plans": { key: "billing-and-plans", i18n_label: "workspace_settings.settings.billing_and_plans.title", href: `/settings/billing`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/billing/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`, }, export: { key: "export", i18n_label: "workspace_settings.settings.exports.title", href: `/settings/exports`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/exports/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`, }, webhooks: { key: "webhooks", i18n_label: "workspace_settings.settings.webhooks.title", href: `/settings/webhooks`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/webhooks/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`, }, "api-tokens": { key: "api-tokens", i18n_label: "workspace_settings.settings.api_tokens.title", href: `/settings/api-tokens`, access: [EUserWorkspaceRoles.ADMIN], - highlight: (pathname: string, baseUrl: string) => - pathname === `${baseUrl}/settings/api-tokens/`, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens/`, }, }; @@ -256,3 +250,84 @@ export const DEFAULT_GLOBAL_VIEWS_LIST: { i18n_label: "default_global_view.subscribed", }, ]; + +export interface IWorkspaceSidebarNavigationItem { + key: string; + labelTranslationKey: string; + href: string; + access: EUserWorkspaceRoles[]; +} + +export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record = { + "your-work": { + key: "your_work", + labelTranslationKey: "your_work", + href: `/profile/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], + }, + views: { + key: "views", + labelTranslationKey: "views", + href: `/workspace-views/all-issues/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], + }, + "active-cycles": { + key: "active-cycles", + labelTranslationKey: "cycles", + href: `/active-cycles/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], + }, + analytics: { + key: "analytics", + labelTranslationKey: "analytics", + href: `/analytics/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], + }, + drafts: { + key: "drafts", + labelTranslationKey: "drafts", + href: `/drafts/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], + }, + archives: { + key: "archives", + labelTranslationKey: "archives", + href: `/projects/archives/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER], + }, +}; +export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [ + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["views"], + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["active-cycles"], + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["analytics"], + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["your-work"], + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["drafts"], + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["archives"], +]; + +export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS: Record = { + home: { + key: "home", + labelTranslationKey: "home.title", + href: `/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], + }, + notifications: { + key: "notifications", + labelTranslationKey: "notification.label", + href: `/notifications/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], + }, + projects: { + key: "projects", + labelTranslationKey: "projects", + href: `/projects/`, + access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], + }, +}; + +export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [ + WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["home"], + WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["notifications"], + WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["projects"], +]; diff --git a/packages/types/src/workspace.d.ts b/packages/types/src/workspace.d.ts index 9320f9b5943..3ae58f9f1db 100644 --- a/packages/types/src/workspace.d.ts +++ b/packages/types/src/workspace.d.ts @@ -1,10 +1,4 @@ -import type { - ICycle, - IProjectMember, - IUser, - IUserLite, - IWorkspaceViewProps, -} from "@plane/types"; +import type { ICycle, IProjectMember, IUser, IUserLite, IWorkspaceViewProps, TPaginationInfo } from "@plane/types"; import { EUserWorkspaceRoles } from "@plane/constants"; // TODO: check if importing this over here causes circular dependency import { TUserPermissions } from "./enums"; @@ -229,3 +223,13 @@ export interface IWorkspaceAnalyticsResponse { export type TWorkspacePaginationInfo = TPaginationInfo & { results: IWorkspace[]; }; + +export interface IWorkspaceSidebarNavigationItem { + key?: string; + is_pinned: boolean; + sort_order: number; +} + +export interface IWorkspaceSidebarNavigation { + [key: string]: IWorkspaceSidebarNavigationItem; +} From e46cb471dfbed70c727d9de1692527edbfda65e6 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 10 Feb 2025 16:55:03 +0530 Subject: [PATCH 02/15] chore: workspace service, store and app theme store updated --- web/core/services/workspace.service.ts | 21 +++++++++++ web/core/store/theme.store.ts | 36 ++++++++++++++++++ web/core/store/workspace/index.ts | 52 +++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/web/core/services/workspace.service.ts b/web/core/services/workspace.service.ts index ef3699fad50..95ed2c976b3 100644 --- a/web/core/services/workspace.service.ts +++ b/web/core/services/workspace.service.ts @@ -18,6 +18,7 @@ import { TWidgetEntityData, TActivityEntityData, } from "@plane/types"; +import { IWorkspaceSidebarNavigationItem, IWorkspaceSidebarNavigation } from "@plane/types/src/workspace"; import { APIService } from "@/services/api.service"; // helpers // types @@ -362,4 +363,24 @@ export class WorkspaceService extends APIService { throw error?.response; }); } + + async fetchSidebarNavigationPreferences(workspaceSlug: string): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/sidebar-preferences/`) + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } + + async updateSidebarPreference( + workspaceSlug: string, + key: string, + data: Partial + ): Promise { + return this.patch(`/api/workspaces/${workspaceSlug}/sidebar-preferences/${key}/`, data) + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } } diff --git a/web/core/store/theme.store.ts b/web/core/store/theme.store.ts index cf37b1ae8f5..a5d961e2db2 100644 --- a/web/core/store/theme.store.ts +++ b/web/core/store/theme.store.ts @@ -3,6 +3,8 @@ import { action, observable, makeObservable } from "mobx"; export interface IThemeStore { // observables sidebarCollapsed: boolean | undefined; + extendedSidebarCollapsed: boolean | undefined; + extendedProjectSidebarCollapsed: boolean | undefined; profileSidebarCollapsed: boolean | undefined; workspaceAnalyticsSidebarCollapsed: boolean | undefined; issueDetailSidebarCollapsed: boolean | undefined; @@ -11,6 +13,8 @@ export interface IThemeStore { projectOverviewSidebarCollapsed: boolean | undefined; // actions toggleSidebar: (collapsed?: boolean) => void; + toggleExtendedSidebar: (collapsed?: boolean) => void; + toggleExtendedProjectSidebar: (collapsed?: boolean) => void; toggleProfileSidebar: (collapsed?: boolean) => void; toggleWorkspaceAnalyticsSidebar: (collapsed?: boolean) => void; toggleIssueDetailSidebar: (collapsed?: boolean) => void; @@ -22,6 +26,8 @@ export interface IThemeStore { export class ThemeStore implements IThemeStore { // observables sidebarCollapsed: boolean | undefined = undefined; + extendedSidebarCollapsed: boolean | undefined = undefined; + extendedProjectSidebarCollapsed: boolean | undefined = undefined; profileSidebarCollapsed: boolean | undefined = undefined; workspaceAnalyticsSidebarCollapsed: boolean | undefined = undefined; issueDetailSidebarCollapsed: boolean | undefined = undefined; @@ -33,6 +39,8 @@ export class ThemeStore implements IThemeStore { makeObservable(this, { // observable sidebarCollapsed: observable.ref, + extendedSidebarCollapsed: observable.ref, + extendedProjectSidebarCollapsed: observable.ref, profileSidebarCollapsed: observable.ref, workspaceAnalyticsSidebarCollapsed: observable.ref, issueDetailSidebarCollapsed: observable.ref, @@ -41,6 +49,8 @@ export class ThemeStore implements IThemeStore { projectOverviewSidebarCollapsed: observable.ref, // action toggleSidebar: action, + toggleExtendedSidebar: action, + toggleExtendedProjectSidebar: action, toggleProfileSidebar: action, toggleWorkspaceAnalyticsSidebar: action, toggleIssueDetailSidebar: action, @@ -63,6 +73,32 @@ export class ThemeStore implements IThemeStore { localStorage.setItem("app_sidebar_collapsed", this.sidebarCollapsed.toString()); }; + /** + * Toggle the extended sidebar collapsed state + * @param collapsed + */ + toggleExtendedSidebar = (collapsed?: boolean) => { + if (collapsed === undefined) { + this.extendedSidebarCollapsed = !this.extendedSidebarCollapsed; + } else { + this.extendedSidebarCollapsed = collapsed; + } + localStorage.setItem("extended_sidebar_collapsed", this.extendedSidebarCollapsed.toString()); + }; + + /** + * Toggle the extended project sidebar collapsed state + * @param collapsed + */ + toggleExtendedProjectSidebar = (collapsed?: boolean) => { + if (collapsed === undefined) { + this.extendedProjectSidebarCollapsed = !this.extendedProjectSidebarCollapsed; + } else { + this.extendedProjectSidebarCollapsed = collapsed; + } + localStorage.setItem("extended_project_sidebar_collapsed", this.extendedProjectSidebarCollapsed.toString()); + }; + /** * Toggle the profile sidebar collapsed state * @param collapsed diff --git a/web/core/store/workspace/index.ts b/web/core/store/workspace/index.ts index 112d4395518..f46ecc273e2 100644 --- a/web/core/store/workspace/index.ts +++ b/web/core/store/workspace/index.ts @@ -1,7 +1,8 @@ import set from "lodash/set"; import { action, computed, observable, makeObservable, runInAction } from "mobx"; // types -import { IWorkspace } from "@plane/types"; +import { computedFn } from "mobx-utils"; +import { IWorkspaceSidebarNavigationItem, IWorkspace, IWorkspaceSidebarNavigation } from "@plane/types"; // services import { WorkspaceService } from "@/plane-web/services"; // store @@ -18,6 +19,7 @@ export interface IWorkspaceRootStore { // computed currentWorkspace: IWorkspace | null; workspacesCreatedByCurrentUser: IWorkspace[] | null; + navigationPreferencesMap: Record; // computed actions getWorkspaceBySlug: (workspaceSlug: string) => IWorkspace | null; getWorkspaceById: (workspaceId: string) => IWorkspace | null; @@ -28,6 +30,13 @@ export interface IWorkspaceRootStore { updateWorkspace: (workspaceSlug: string, data: Partial) => Promise; updateWorkspaceLogo: (workspaceSlug: string, logoURL: string) => void; deleteWorkspace: (workspaceSlug: string) => Promise; + fetchSidebarNavigationPreferences: (workspaceSlug: string) => Promise; + updateSidebarPreference: ( + workspaceSlug: string, + key: string, + data: Partial + ) => Promise; + getNavigationPreferences: (workspaceSlug: string) => IWorkspaceSidebarNavigation | undefined; // sub-stores webhook: IWebhookStore; apiToken: IApiTokenStore; @@ -38,6 +47,7 @@ export class WorkspaceRootStore implements IWorkspaceRootStore { loader: boolean = false; // observables workspaces: Record = {}; + navigationPreferencesMap: Record = {}; // services workspaceService; // root store @@ -53,6 +63,7 @@ export class WorkspaceRootStore implements IWorkspaceRootStore { loader: observable.ref, // observables workspaces: observable, + navigationPreferencesMap: observable, // computed currentWorkspace: computed, workspacesCreatedByCurrentUser: computed, @@ -65,6 +76,8 @@ export class WorkspaceRootStore implements IWorkspaceRootStore { updateWorkspace: action, updateWorkspaceLogo: action, deleteWorkspace: action, + fetchSidebarNavigationPreferences: action, + updateSidebarPreference: action, }); // services @@ -183,4 +196,41 @@ export class WorkspaceRootStore implements IWorkspaceRootStore { this.workspaces = updatedWorkspacesList; }); }); + + fetchSidebarNavigationPreferences = async (workspaceSlug: string) => { + try { + const response = await this.workspaceService.fetchSidebarNavigationPreferences(workspaceSlug); + + runInAction(() => { + this.navigationPreferencesMap[workspaceSlug] = response; + }); + } catch (error) { + console.error("Failed to fetch sidebar preferences:", error); + } + }; + + updateSidebarPreference = async ( + workspaceSlug: string, + key: string, + data: Partial + ) => { + try { + const response = await this.workspaceService.updateSidebarPreference(workspaceSlug, key, data); + + runInAction(() => { + this.navigationPreferencesMap[workspaceSlug] = { + ...this.navigationPreferencesMap[workspaceSlug], + [key]: response, + }; + }); + + return response; + } catch (error) { + console.error("Failed to update sidebar preference:", error); + } + }; + + getNavigationPreferences = computedFn( + (workspaceSlug: string): IWorkspaceSidebarNavigation | undefined => this.navigationPreferencesMap[workspaceSlug] + ); } From be9842a8fcc5a348edfaadf2eda8a2d68b16a49c Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 10 Feb 2025 17:26:08 +0530 Subject: [PATCH 03/15] dev: extended sidebar implementation and code refactor --- .../(projects)/extended-project-sidebar.tsx | 155 +++++++++++++ .../(projects)/extended-sidebar.tsx | 126 ++++++++++ .../[workspaceSlug]/(projects)/sidebar.tsx | 105 ++++----- .../sidebar/extended-sidebar-item.tsx | 218 ++++++++++++++++++ .../components/workspace/sidebar/helper.tsx | 26 +++ web/ce/components/workspace/sidebar/index.ts | 3 + .../workspace/sidebar/sidebar-item.tsx | 91 ++++++++ .../workspace/sidebar/projects-list-item.tsx | 49 +--- .../workspace/sidebar/projects-list.tsx | 24 +- .../workspace/sidebar/sidebar-menu-items.tsx | 69 ++++++ ...xtended-sidebar-overview-outside-click.tsx | 49 ++++ .../layouts/auth-layout/workspace-wrapper.tsx | 9 +- web/ee/components/workspace/sidebar/index.ts | 1 + 13 files changed, 822 insertions(+), 103 deletions(-) create mode 100644 web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx create mode 100644 web/app/[workspaceSlug]/(projects)/extended-sidebar.tsx create mode 100644 web/ce/components/workspace/sidebar/extended-sidebar-item.tsx create mode 100644 web/ce/components/workspace/sidebar/helper.tsx create mode 100644 web/ce/components/workspace/sidebar/index.ts create mode 100644 web/ce/components/workspace/sidebar/sidebar-item.tsx create mode 100644 web/core/components/workspace/sidebar/sidebar-menu-items.tsx create mode 100644 web/core/hooks/use-extended-sidebar-overview-outside-click.tsx create mode 100644 web/ee/components/workspace/sidebar/index.ts diff --git a/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx b/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx new file mode 100644 index 00000000000..dc0d7054a29 --- /dev/null +++ b/web/app/[workspaceSlug]/(projects)/extended-project-sidebar.tsx @@ -0,0 +1,155 @@ +"use client"; + +import React, { useRef, useState } from "react"; +import { observer } from "mobx-react"; +import { useParams } from "next/navigation"; +// plane imports +import { Plus, Search } from "lucide-react"; +import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; +import { setToast, TOAST_TYPE, Tooltip } from "@plane/ui"; +import { cn } from "@plane/utils"; +// components +import { CreateProjectModal } from "@/components/project"; +import { SidebarProjectsListItem } from "@/components/workspace"; +// hooks +import { orderJoinedProjects } from "@/helpers/project.helper"; +import { useAppTheme, useProject, useUserPermissions } from "@/hooks/store"; +import useExtendedSidebarOutsideClickDetector from "@/hooks/use-extended-sidebar-overview-outside-click"; +import { TProject } from "@/plane-web/types"; + +export const ExtendedProjectSidebar = observer(() => { + // refs + const extendedProjectSidebarRef = useRef(null); + const [searchQuery, setSearchQuery] = useState(""); + // states + const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); + // routers + const { workspaceSlug } = useParams(); + // store hooks + const { t } = useTranslation(); + const { sidebarCollapsed, extendedProjectSidebarCollapsed, toggleExtendedProjectSidebar } = useAppTheme(); + const { getPartialProjectById, joinedProjectIds: joinedProjects, updateProjectView } = useProject(); + const { allowPermissions } = useUserPermissions(); + + const handleOnProjectDrop = ( + sourceId: string | undefined, + destinationId: string | undefined, + shouldDropAtEnd: boolean + ) => { + if (!sourceId || !destinationId || !workspaceSlug) return; + if (sourceId === destinationId) return; + + const joinedProjectsList: TProject[] = []; + joinedProjects.map((projectId) => { + const projectDetails = getPartialProjectById(projectId); + if (projectDetails) joinedProjectsList.push(projectDetails); + }); + + const sourceIndex = joinedProjects.indexOf(sourceId); + const destinationIndex = shouldDropAtEnd ? joinedProjects.length : joinedProjects.indexOf(destinationId); + + if (joinedProjectsList.length <= 0) return; + + const updatedSortOrder = orderJoinedProjects(sourceIndex, destinationIndex, sourceId, joinedProjectsList); + if (updatedSortOrder != undefined) + updateProjectView(workspaceSlug.toString(), sourceId, { sort_order: updatedSortOrder }).catch(() => { + setToast({ + type: TOAST_TYPE.ERROR, + title: t("error"), + message: t("something_went_wrong"), + }); + }); + }; + + // filter projects based on search query + const filteredProjects = joinedProjects.filter((projectId) => { + const project = getPartialProjectById(projectId); + if (!project) return false; + return project.name.toLowerCase().includes(searchQuery.toLowerCase()) || project.identifier.includes(searchQuery); + }); + + // auth + const isAuthorizedUser = allowPermissions( + [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + EUserPermissionsLevel.WORKSPACE + ); + + useExtendedSidebarOutsideClickDetector( + extendedProjectSidebarRef, + () => { + if (!isProjectModalOpen) { + toggleExtendedProjectSidebar(false); + } + }, + "extended-project-sidebar-toggle" + ); + + return ( + <> + {workspaceSlug && ( + setIsProjectModalOpen(false)} + setToFavorite={false} + workspaceSlug={workspaceSlug.toString()} + /> + )} +
+
+
+ Projects + {isAuthorizedUser && ( + + + + )} +
+
+ + setSearchQuery(e.target.value)} + /> +
+
+
+ {filteredProjects.map((projectId, index) => ( + {}} + projectListType={"JOINED"} + disableDrag={false} + disableDrop={false} + isLastChild={index === joinedProjects.length - 1} + handleOnProjectDrop={handleOnProjectDrop} + /> + ))} +
+
+ + ); +}); diff --git a/web/app/[workspaceSlug]/(projects)/extended-sidebar.tsx b/web/app/[workspaceSlug]/(projects)/extended-sidebar.tsx new file mode 100644 index 00000000000..09bca6af34a --- /dev/null +++ b/web/app/[workspaceSlug]/(projects)/extended-sidebar.tsx @@ -0,0 +1,126 @@ +"use client"; + +import React, { useMemo, useRef } from "react"; +import { observer } from "mobx-react"; +import { useParams } from "next/navigation"; +// plane imports +import { EUserWorkspaceRoles, WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS } from "@plane/constants"; +import { cn } from "@plane/utils"; +// hooks +import { useAppTheme, useWorkspace } from "@/hooks/store"; +import useExtendedSidebarOutsideClickDetector from "@/hooks/use-extended-sidebar-overview-outside-click"; +// plane-web imports +import { ExtendedSidebarItem } from "@/plane-web/components/workspace/sidebar"; + +export const ExtendedAppSidebar = observer(() => { + // refs + const extendedSidebarRef = useRef(null); + // routers + const { workspaceSlug } = useParams(); + // store hooks + const { sidebarCollapsed, extendedSidebarCollapsed, toggleExtendedSidebar } = useAppTheme(); + const { updateSidebarPreference, getNavigationPreferences } = useWorkspace(); + + // derived values + const currentWorkspaceNavigationPreferences = getNavigationPreferences(workspaceSlug.toString()); + + const sortedNavigationItems = useMemo( + () => + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS.map((item) => { + const preference = currentWorkspaceNavigationPreferences?.[item.key]; + return { + ...item, + sort_order: preference ? preference.sort_order : 0, + }; + }).sort((a, b) => a.sort_order - b.sort_order), + [currentWorkspaceNavigationPreferences] + ); + + const sortedNavigationItemsKeys = sortedNavigationItems.map((item) => item.key); + + const orderNavigationItem = ( + sourceIndex: number, + destinationIndex: number, + navigationList: { + sort_order: number; + key: string; + labelTranslationKey: string; + href: string; + access: EUserWorkspaceRoles[]; + }[] + ): number | undefined => { + if (sourceIndex < 0 || destinationIndex < 0 || navigationList.length <= 0) return undefined; + + let updatedSortOrder: number | undefined = undefined; + const sortOrderDefaultValue = 10000; + + if (destinationIndex === 0) { + // updating project at the top of the project + const currentSortOrder = navigationList[destinationIndex].sort_order || 0; + updatedSortOrder = currentSortOrder - sortOrderDefaultValue; + } else if (destinationIndex === navigationList.length) { + // updating project at the bottom of the project + const currentSortOrder = navigationList[destinationIndex - 1].sort_order || 0; + updatedSortOrder = currentSortOrder + sortOrderDefaultValue; + } else { + // updating project in the middle of the project + const destinationTopProjectSortOrder = navigationList[destinationIndex - 1].sort_order || 0; + const destinationBottomProjectSortOrder = navigationList[destinationIndex].sort_order || 0; + const updatedValue = (destinationTopProjectSortOrder + destinationBottomProjectSortOrder) / 2; + updatedSortOrder = updatedValue; + } + + return updatedSortOrder; + }; + + const handleOnNavigationItemDrop = ( + sourceId: string | undefined, + destinationId: string | undefined, + shouldDropAtEnd: boolean + ) => { + if (!sourceId || !destinationId || !workspaceSlug) return; + if (sourceId === destinationId) return; + + const sourceIndex = sortedNavigationItemsKeys.indexOf(sourceId); + const destinationIndex = shouldDropAtEnd + ? sortedNavigationItemsKeys.length + : sortedNavigationItemsKeys.indexOf(destinationId); + + const updatedSortOrder = orderNavigationItem(sourceIndex, destinationIndex, sortedNavigationItems); + + if (updatedSortOrder != undefined) + updateSidebarPreference(workspaceSlug.toString(), sourceId, { + sort_order: updatedSortOrder, + }); + }; + + useExtendedSidebarOutsideClickDetector( + extendedSidebarRef, + () => toggleExtendedSidebar(false), + "extended-sidebar-toggle" + ); + + return ( +
+ {sortedNavigationItems.map((item, index) => ( + + ))} +
+ ); +}); diff --git a/web/app/[workspaceSlug]/(projects)/sidebar.tsx b/web/app/[workspaceSlug]/(projects)/sidebar.tsx index 6b98e6bdbff..ff345dcf04f 100644 --- a/web/app/[workspaceSlug]/(projects)/sidebar.tsx +++ b/web/app/[workspaceSlug]/(projects)/sidebar.tsx @@ -5,16 +5,10 @@ import { observer } from "mobx-react"; import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; // components -import { - SidebarDropdown, - SidebarHelpSection, - SidebarProjectsList, - SidebarQuickActions, - SidebarUserMenu, - SidebarWorkspaceMenu, -} from "@/components/workspace"; -// helpers +import { SidebarDropdown, SidebarHelpSection, SidebarProjectsList, SidebarQuickActions } from "@/components/workspace"; import { SidebarFavoritesMenu } from "@/components/workspace/sidebar/favorites/favorites-menu"; +import { SidebarMenuItems } from "@/components/workspace/sidebar/sidebar-menu-items"; +// helpers import { cn } from "@/helpers/common.helper"; // hooks import { useAppTheme, useUserPermissions } from "@/hooks/store"; @@ -23,6 +17,8 @@ import useSize from "@/hooks/use-window-size"; // plane web components import { SidebarAppSwitcher } from "@/plane-web/components/sidebar"; import { SidebarTeamsList } from "@/plane-web/components/workspace/sidebar/teams-sidebar-list"; +import { ExtendedProjectSidebar } from "./extended-project-sidebar"; +import { ExtendedAppSidebar } from "./extended-sidebar"; export const AppSidebar: FC = observer(() => { // store hooks @@ -55,62 +51,63 @@ export const AppSidebar: FC = observer(() => { const isFavoriteEmpty = isEmpty(groupedFavorites); return ( -
+ <>
- {/* Workspace switcher and settings */} - -
- {/* App switcher */} - {canPerformWorkspaceMemberActions && } - {/* Quick actions */} - -
-
-
- {/* User Menu */} - - {/* Workspace Menu */} - +
+ {/* Workspace switcher and settings */} + +
+ {/* App switcher */} + {canPerformWorkspaceMemberActions && } + {/* Quick actions */} + +

- {/* Favorites Menu */} - {canPerformWorkspaceMemberActions && !isFavoriteEmpty && } - {/* Teams List */} - - {/* Projects List */} - +
+ +
+ {/* Favorites Menu */} + {canPerformWorkspaceMemberActions && !isFavoriteEmpty && } + {/* Teams List */} + + {/* Projects List */} + +
+ {/* Help Section */} +
- {/* Help Section */} -
-
+ + + ); }); diff --git a/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx b/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx new file mode 100644 index 00000000000..5cc740eb054 --- /dev/null +++ b/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx @@ -0,0 +1,218 @@ +import { FC, useEffect, useRef, useState } from "react"; +import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; +import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; +import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item"; +import { observer } from "mobx-react"; +import Link from "next/link"; +import { useParams, usePathname } from "next/navigation"; +import { Eye, EyeClosed } from "lucide-react"; +// plane imports +import { EUserPermissionsLevel, IWorkspaceSidebarNavigationItem } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; +import { DragHandle, DropIndicator, Tooltip } from "@plane/ui"; +import { cn } from "@plane/utils"; +// components +import { SidebarNavItem } from "@/components/sidebar"; +// hooks +import { useAppTheme, useUser, useUserPermissions, useWorkspace } from "@/hooks/store"; +// plane web imports +// local imports +import { getSidebarNavigationItemIcon } from "./helper"; + +type TExtendedSidebarItemProps = { + item: IWorkspaceSidebarNavigationItem; + handleOnNavigationItemDrop?: ( + sourceId: string | undefined, + destinationId: string | undefined, + shouldDropAtEnd: boolean + ) => void; + disableDrag?: boolean; + disableDrop?: boolean; + isLastChild: boolean; +}; + +export const ExtendedSidebarItem: FC = observer((props) => { + const { item, handleOnNavigationItemDrop, disableDrag = false, disableDrop = false, isLastChild } = props; + const { t } = useTranslation(); + // states + const [isDragging, setIsDragging] = useState(false); + const [instruction, setInstruction] = useState<"DRAG_OVER" | "DRAG_BELOW" | undefined>(undefined); + // refs + const navigationIemRef = useRef(null); + const dragHandleRef = useRef(null); + + // nextjs hooks + const pathname = usePathname(); + const { workspaceSlug } = useParams(); + // store hooks + const { getNavigationPreferences, updateSidebarPreference } = useWorkspace(); + const { toggleExtendedSidebar } = useAppTheme(); + const { data } = useUser(); + const { allowPermissions } = useUserPermissions(); + + // derived values + const sidebarPreference = getNavigationPreferences(workspaceSlug.toString()); + const isPinned = sidebarPreference?.[item.key]?.is_pinned; + + const handleLinkClick = () => toggleExtendedSidebar(); + + if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) { + return null; + } + + const itemHref = + item.key === "your_work" + ? `/${workspaceSlug.toString()}${item.href}${data?.id}` + : `/${workspaceSlug.toString()}${item.href}`; + const isActive = itemHref === pathname; + + const pinNavigationItem = (workspaceSlug: string, key: string) => { + updateSidebarPreference(workspaceSlug, key, { + is_pinned: true, + }); + }; + + const unPinNavigationItem = (workspaceSlug: string, key: string) => { + updateSidebarPreference(workspaceSlug, key, { + is_pinned: false, + }); + }; + + const icon = getSidebarNavigationItemIcon(item.key); + + useEffect(() => { + const element = navigationIemRef.current; + const dragHandleElement = dragHandleRef.current; + + if (!element) return; + + return combine( + draggable({ + element, + canDrag: () => !disableDrag, + dragHandle: dragHandleElement ?? undefined, + getInitialData: () => ({ id: item.key, dragInstanceId: "NAVIGATION" }), // var1 + onDragStart: () => { + setIsDragging(true); + }, + onDrop: () => { + setIsDragging(false); + }, + }), + dropTargetForElements({ + element, + canDrop: ({ source }) => + !disableDrop && source?.data?.id !== item.key && source?.data?.dragInstanceId === "NAVIGATION", + getData: ({ input, element }) => { + const data = { id: item.key }; + + // attach instruction for last in list + return attachInstruction(data, { + input, + element, + currentLevel: 0, + indentPerLevel: 0, + mode: isLastChild ? "last-in-group" : "standard", + }); + }, + onDrag: ({ self }) => { + const extractedInstruction = extractInstruction(self?.data)?.type; + // check if the highlight is to be shown above or below + setInstruction( + extractedInstruction + ? extractedInstruction === "reorder-below" && isLastChild + ? "DRAG_BELOW" + : "DRAG_OVER" + : undefined + ); + }, + onDragLeave: () => { + setInstruction(undefined); + }, + onDrop: ({ self, source }) => { + setInstruction(undefined); + const extractedInstruction = extractInstruction(self?.data)?.type; + const currentInstruction = extractedInstruction + ? extractedInstruction === "reorder-below" && isLastChild + ? "DRAG_BELOW" + : "DRAG_OVER" + : undefined; + if (!currentInstruction) return; + + const sourceId = source?.data?.id as string | undefined; + const destinationId = self?.data?.id as string | undefined; + + if (handleOnNavigationItemDrop) + handleOnNavigationItemDrop(sourceId, destinationId, currentInstruction === "DRAG_BELOW"); + }, + }) + ); + }, [isLastChild, handleOnNavigationItemDrop, disableDrag, disableDrop, item.key]); + + return ( +
+ +
+ {!disableDrag && ( + + + + )} + + handleLinkClick()} className="group flex-grow"> +
+ {icon} +

{t(item.labelTranslationKey)}

+
+ + {isPinned ? ( + + unPinNavigationItem(workspaceSlug.toString(), item.key)} + /> + + ) : ( + + pinNavigationItem(workspaceSlug.toString(), item.key)} + /> + + )} +
+
+ {isLastChild && } +
+ ); +}); diff --git a/web/ce/components/workspace/sidebar/helper.tsx b/web/ce/components/workspace/sidebar/helper.tsx new file mode 100644 index 00000000000..1d3bbf289e8 --- /dev/null +++ b/web/ce/components/workspace/sidebar/helper.tsx @@ -0,0 +1,26 @@ +import { BarChart2, Briefcase, Home, Inbox, Layers, PenSquare } from "lucide-react"; +import { ArchiveIcon, ContrastIcon, UserActivityIcon } from "@plane/ui"; +import { cn } from "@plane/utils"; + +export const getSidebarNavigationItemIcon = (key: string, className: string = "") => { + switch (key) { + case "home": + return ; + case "notifications": + return ; + case "projects": + return ; + case "views": + return ; + case "active-cycles": + return ; + case "analytics": + return ; + case "your_work": + return ; + case "drafts": + return ; + case "archives": + return ; + } +}; diff --git a/web/ce/components/workspace/sidebar/index.ts b/web/ce/components/workspace/sidebar/index.ts new file mode 100644 index 00000000000..d9edcc13d30 --- /dev/null +++ b/web/ce/components/workspace/sidebar/index.ts @@ -0,0 +1,3 @@ +export * from "./extended-sidebar-item"; +export * from "./helper"; +export * from "./sidebar-item"; diff --git a/web/ce/components/workspace/sidebar/sidebar-item.tsx b/web/ce/components/workspace/sidebar/sidebar-item.tsx new file mode 100644 index 00000000000..517e7642a99 --- /dev/null +++ b/web/ce/components/workspace/sidebar/sidebar-item.tsx @@ -0,0 +1,91 @@ +"use client"; +import { FC } from "react"; +import { observer } from "mobx-react"; +import Link from "next/link"; +import { useParams, usePathname } from "next/navigation"; +// plane imports +import { EUserPermissionsLevel, IWorkspaceSidebarNavigationItem } from "@plane/constants"; +import { usePlatformOS } from "@plane/hooks"; +import { useTranslation } from "@plane/i18n"; +import { Tooltip } from "@plane/ui"; +// components +import { SidebarNavItem } from "@/components/sidebar"; +// hooks +import { useAppTheme, useUser, useUserPermissions, useWorkspace } from "@/hooks/store"; +// plane web imports +import { UpgradeBadge } from "@/plane-web/components/workspace"; +// local imports +import { getSidebarNavigationItemIcon } from "./helper"; + +type TSidebarItemProps = { + item: IWorkspaceSidebarNavigationItem; +}; + +export const SidebarItem: FC = observer((props) => { + const { item } = props; + const { t } = useTranslation(); + // nextjs hooks + const pathname = usePathname(); + const { workspaceSlug } = useParams(); + const { allowPermissions } = useUserPermissions(); + const { getNavigationPreferences } = useWorkspace(); + const { data } = useUser(); + + // store hooks + const { toggleSidebar, sidebarCollapsed, extendedSidebarCollapsed, toggleExtendedSidebar } = useAppTheme(); + const { isMobile } = usePlatformOS(); + + const handleLinkClick = () => { + if (window.innerWidth < 768) { + toggleSidebar(); + } + if (extendedSidebarCollapsed) toggleExtendedSidebar(); + }; + + const staticItems = ["home", "notifications", "pi-chat", "projects"]; + + if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) { + return null; + } + + const itemHref = + item.key === "your_work" + ? `/${workspaceSlug.toString()}${item.href}/${data?.id}` + : `/${workspaceSlug.toString()}${item.href}`; + + const isActive = itemHref === pathname; + + const sidebarPreference = getNavigationPreferences(workspaceSlug.toString()); + const isPinned = sidebarPreference?.[item.key]?.is_pinned; + if (!isPinned && !staticItems.includes(item.key)) return null; + + const icon = getSidebarNavigationItemIcon(item.key); + + return ( + + handleLinkClick()}> + + {/* */} +
+ {icon} + {!sidebarCollapsed &&

{t(item.labelTranslationKey)}

} +
+ {!sidebarCollapsed && item.key === "active-cycles" && ( +
+ +
+ )} +
+ +
+ ); +}); diff --git a/web/core/components/workspace/sidebar/projects-list-item.tsx b/web/core/components/workspace/sidebar/projects-list-item.tsx index 644bf4ecf0c..ed0259d1c06 100644 --- a/web/core/components/workspace/sidebar/projects-list-item.tsx +++ b/web/core/components/workspace/sidebar/projects-list-item.tsx @@ -8,16 +8,16 @@ import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/el import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item"; import { observer } from "mobx-react"; import Link from "next/link"; -import { useParams, useRouter } from "next/navigation"; +import { useParams } from "next/navigation"; import { createRoot } from "react-dom/client"; -import { LinkIcon, Star, Settings, Share2, LogOut, MoreHorizontal, ChevronRight } from "lucide-react"; +import { LinkIcon, Settings, Share2, LogOut, MoreHorizontal, ChevronRight } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; // plane helpers import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; // ui -import { CustomMenu, Tooltip, ArchiveIcon, setPromiseToast, DropIndicator, DragHandle, ControlLink } from "@plane/ui"; +import { CustomMenu, Tooltip, ArchiveIcon, DropIndicator, DragHandle, ControlLink } from "@plane/ui"; // components import { Logo } from "@/components/common"; import { LeaveProjectModal, PublishProjectModal } from "@/components/project"; @@ -52,7 +52,7 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme(); const { t } = useTranslation(); const { setTrackElement } = useEventTracker(); - const { addProjectToFavorites, removeProjectFromFavorites, getPartialProjectById } = useProject(); + const { getPartialProjectById } = useProject(); const { isMobile } = usePlatformOS(); const { allowPermissions } = useUserPermissions(); // states @@ -67,7 +67,6 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { const projectRef = useRef(null); const dragHandleRef = useRef(null); // router - const router = useRouter(); const { workspaceSlug, projectId: URLProjectId } = useParams(); // derived values const project = getPartialProjectById(projectId); @@ -85,40 +84,6 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { project?.id ); - const handleAddToFavorites = () => { - if (!workspaceSlug || !project) return; - - const addToFavoritePromise = addProjectToFavorites(workspaceSlug.toString(), project.id); - setPromiseToast(addToFavoritePromise, { - loading: t("adding_project_to_favorites"), - success: { - title: t("success"), - message: () => t("project_added_to_favorites"), - }, - error: { - title: t("error"), - message: () => t("couldnt_add_the_project_to_favorites"), - }, - }); - }; - - const handleRemoveFromFavorites = () => { - if (!workspaceSlug || !project) return; - - const removeFromFavoritePromise = removeProjectFromFavorites(workspaceSlug.toString(), project.id); - setPromiseToast(removeFromFavoritePromise, { - loading: t("removing_project_from_favorites"), - success: { - title: t("success"), - message: () => t("project_removed_from_favorites"), - }, - error: { - title: t("error"), - message: () => t("couldnt_remove_the_project_from_favorites"), - }, - }); - }; - const handleLeaveProject = () => { setTrackElement("APP_SIDEBAR_PROJECT_DROPDOWN"); setLeaveProjectModal(true); @@ -222,11 +187,7 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { if (URLProjectId === project.id) setIsProjectListOpen(true); }, [URLProjectId]); - const handleItemClick = () => { - if (!isProjectListOpen && !isMobile) router.push(`/${workspaceSlug}/projects/${project.id}/issues`); - setIsProjectListOpen((prev) => !prev); - }; - + const handleItemClick = () => setIsProjectListOpen((prev) => !prev); return ( <> setPublishModal(false)} /> diff --git a/web/core/components/workspace/sidebar/projects-list.tsx b/web/core/components/workspace/sidebar/projects-list.tsx index 2014062c4d4..5ff345bc801 100644 --- a/web/core/components/workspace/sidebar/projects-list.tsx +++ b/web/core/components/workspace/sidebar/projects-list.tsx @@ -5,7 +5,7 @@ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-scroll/element"; import { observer } from "mobx-react"; import { useParams, usePathname } from "next/navigation"; -import { Briefcase, ChevronRight, Plus } from "lucide-react"; +import { Briefcase, ChevronRight, Ellipsis, Plus } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; @@ -13,6 +13,7 @@ import { useTranslation } from "@plane/i18n"; import { Loader, TOAST_TYPE, Tooltip, setToast } from "@plane/ui"; // components import { CreateProjectModal } from "@/components/project"; +import { SidebarNavItem } from "@/components/sidebar"; import { SidebarProjectsListItem } from "@/components/workspace"; // helpers import { cn } from "@/helpers/common.helper"; @@ -37,7 +38,7 @@ export const SidebarProjectsList: FC = observer(() => { // store hooks const { t } = useTranslation(); const { toggleCreateProjectModal } = useCommandPalette(); - const { sidebarCollapsed } = useAppTheme(); + const { sidebarCollapsed, toggleExtendedProjectSidebar } = useAppTheme(); const { setTrackElement } = useEventTracker(); const { allowPermissions } = useUserPermissions(); @@ -237,12 +238,12 @@ export const SidebarProjectsList: FC = observer(() => { {isAllProjectsListOpen && ( - {joinedProjects.map((projectId, index) => ( + {joinedProjects.slice(0, 7).map((projectId, index) => ( { {!isCollapsed && t("add_project")} )} + + {joinedProjects.length > 7 && ( + + + + )}
); diff --git a/web/core/components/workspace/sidebar/sidebar-menu-items.tsx b/web/core/components/workspace/sidebar/sidebar-menu-items.tsx new file mode 100644 index 00000000000..9e3bfc3bc3a --- /dev/null +++ b/web/core/components/workspace/sidebar/sidebar-menu-items.tsx @@ -0,0 +1,69 @@ +"use client"; +import React, { useMemo } from "react"; +import { observer } from "mobx-react"; +import { useParams } from "next/navigation"; +import { Ellipsis } from "lucide-react"; +// plane imports +import { + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS, + WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS, +} from "@plane/constants"; +import { cn } from "@plane/utils"; +// components +import { SidebarNavItem } from "@/components/sidebar"; +// store hooks +import { useAppTheme, useWorkspace } from "@/hooks/store"; +// plane-web imports +import { SidebarItem } from "@/plane-web/components/workspace/sidebar"; + +export const SidebarMenuItems = observer(() => { + // routers + const { workspaceSlug } = useParams(); + // store hooks + const { sidebarCollapsed, toggleExtendedSidebar } = useAppTheme(); + const { getNavigationPreferences } = useWorkspace(); + + // derived values + const currentWorkspaceNavigationPreferences = getNavigationPreferences(workspaceSlug.toString()); + + const sortedNavigationItems = useMemo( + () => + WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS.map((item) => { + const preference = currentWorkspaceNavigationPreferences?.[item.key]; + return { + ...item, + sort_order: preference ? preference.sort_order : 0, + }; + }).sort((a, b) => a.sort_order - b.sort_order), + [currentWorkspaceNavigationPreferences] + ); + + return ( + <> +
+ {WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS.map((item, _index) => ( + + ))} + {sortedNavigationItems.map((item, _index) => ( + + ))} +
+ + + + + ); +}); diff --git a/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx b/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx new file mode 100644 index 00000000000..138328e1d3b --- /dev/null +++ b/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx @@ -0,0 +1,49 @@ +import React, { useEffect } from "react"; + +const useExtendedSidebarOutsideClickDetector = ( + ref: React.RefObject, + callback: () => void, + targetId: string +) => { + const handleClick = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + // check for the closest element with attribute name data-prevent-outside-click + const preventOutsideClickElement = (event.target as HTMLElement | undefined)?.closest( + "[data-prevent-outside-click]" + ); + // if the closest element with attribute name data-prevent-outside-click is found, return + if (preventOutsideClickElement) { + return; + } + // check if the click target is the current issue element or its children + let targetElement = event.target as HTMLElement | null; + while (targetElement) { + if (targetElement.id === targetId) { + // if the click target is the current issue element, return + return; + } + targetElement = targetElement.parentElement; + } + const delayOutsideClickElement = (event.target as HTMLElement | undefined)?.closest("[data-delay-outside-click]"); + if (delayOutsideClickElement) { + // if the click target is the closest element with attribute name data-delay-outside-click, delay the callback + setTimeout(() => { + callback(); + }, 0); + return; + } + // else, call the callback immediately + callback(); + } + }; + + useEffect(() => { + document.addEventListener("mousedown", handleClick); + + return () => { + document.removeEventListener("mousedown", handleClick); + }; + }); +}; + +export default useExtendedSidebarOutsideClickDetector; diff --git a/web/core/layouts/auth-layout/workspace-wrapper.tsx b/web/core/layouts/auth-layout/workspace-wrapper.tsx index 55a335cc712..8ced177dd3f 100644 --- a/web/core/layouts/auth-layout/workspace-wrapper.tsx +++ b/web/core/layouts/auth-layout/workspace-wrapper.tsx @@ -44,7 +44,7 @@ export const WorkspaceAuthWrapper: FC = observer((props) const { workspace: { fetchWorkspaceMembers }, } = useMember(); - const { workspaces } = useWorkspace(); + const { workspaces, fetchSidebarNavigationPreferences } = useWorkspace(); const { isMobile } = usePlatformOS(); const { loader, workspaceInfoBySlug, fetchUserWorkspaceInfo, fetchUserProjectPermissions, allowPermissions } = useUserPermissions(); @@ -101,6 +101,13 @@ export const WorkspaceAuthWrapper: FC = observer((props) { revalidateIfStale: false, revalidateOnFocus: false } ); + // fetch workspace sidebar preferences + useSWR( + workspaceSlug ? `WORKSPACE_SIDEBAR_PREFERENCES_${workspaceSlug}` : null, + workspaceSlug ? () => fetchSidebarNavigationPreferences(workspaceSlug.toString()) : null, + { revalidateIfStale: false, revalidateOnFocus: false } + ); + // initialize the local database const { isLoading: isDBInitializing } = useSWRImmutable( workspaceSlug ? `WORKSPACE_DB_${workspaceSlug}` : null, diff --git a/web/ee/components/workspace/sidebar/index.ts b/web/ee/components/workspace/sidebar/index.ts new file mode 100644 index 00000000000..eb17e4018f4 --- /dev/null +++ b/web/ee/components/workspace/sidebar/index.ts @@ -0,0 +1 @@ +export * from "ce/components/workspace/sidebar"; From 5d858cc5e1aa4de56998f430c88bbf7803146bb8 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 10 Feb 2025 17:42:40 +0530 Subject: [PATCH 04/15] chore: ux improvements --- .../components/workspace/sidebar/dropdown.tsx | 122 +++++++++--------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/web/core/components/workspace/sidebar/dropdown.tsx b/web/core/components/workspace/sidebar/dropdown.tsx index 8d131594840..3904a7a6bfe 100644 --- a/web/core/components/workspace/sidebar/dropdown.tsx +++ b/web/core/components/workspace/sidebar/dropdown.tsx @@ -79,7 +79,11 @@ export const SidebarDropdown = observer(() => { const workspacesList = Object.values(workspaces ?? {}); // TODO: fix workspaces list scroll return ( -
+
{ )} - {!sidebarCollapsed && ( - - - - - - + + + + + } - style={styles.popper} - {...attributes.popper} - > -
- {currentUser?.email} - - - - - {t("settings")} + ref={setPopperElement as Ref} + style={styles.popper} + {...attributes.popper} + > +
+ {currentUser?.email} + + + + + {t("settings")} + + + +
+
+ + + {t("sign_out")} + +
+ {isUserInstanceAdmin && ( +
+ + + + {t("enter_god_mode")}
-
- - - {t("sign_out")} - -
- {isUserInstanceAdmin && ( -
- - - - {t("enter_god_mode")} - - - -
- )} - - -
- )} + )} + + +
); }); From 84adb24ae7fa75686c84a187a09c106c6c2ebcec Mon Sep 17 00:00:00 2001 From: sangeethailango Date: Mon, 10 Feb 2025 18:26:36 +0530 Subject: [PATCH 05/15] chore: sidebar preference endpoint updated --- .../app/views/workspace/user_preference.py | 18 +++++++++++++----- apiserver/plane/db/models/workspace.py | 3 ++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apiserver/plane/app/views/workspace/user_preference.py b/apiserver/plane/app/views/workspace/user_preference.py index 66daffd1933..f484a515e37 100644 --- a/apiserver/plane/app/views/workspace/user_preference.py +++ b/apiserver/plane/app/views/workspace/user_preference.py @@ -40,20 +40,28 @@ def get(self, request, slug): preference = WorkspaceUserPreference.objects.bulk_create( [ WorkspaceUserPreference( - key=key, user=request.user, workspace=workspace + key=key, user=request.user, workspace=workspace, sort_order=(65535 + (i*10000)) ) - for key in create_preference_keys + for i, key in enumerate(create_preference_keys) ], batch_size=10, ignore_conflicts=True, ) - preference = WorkspaceUserPreference.objects.filter( + preferences = WorkspaceUserPreference.objects.filter( user=request.user, workspace_id=workspace.id - ) + ).order_by("sort_order").values("key", "is_pinned", "sort_order") + + + user_preferences = {} + for preference in preferences: + user_preferences[(str(preference["key"]))] = { + "is_pinned": preference["is_pinned"], + "sort_order": preference["sort_order"], + } return Response( - preference.values("key", "is_pinned", "sort_order"), + user_preferences, status=status.HTTP_200_OK, ) diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index a7b4855790c..40f74510e9e 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -397,7 +397,8 @@ class UserPreferenceKeys(models.TextChoices): CYCLES = "cycles", "Cycles" VIEWS = "views", "Views" YOUR_WORK = "your_work", "Your Work" - + DRAFTS = "drafts", "Drafts" + ARCHIVES = "archives", "Archives" workspace = models.ForeignKey( "db.Workspace", From f0e9675311da49612d9ff2df3c4264e48d53079f Mon Sep 17 00:00:00 2001 From: sangeethailango Date: Mon, 10 Feb 2025 18:45:13 +0530 Subject: [PATCH 06/15] chore: sidebar preference endpoint updated --- apiserver/plane/app/views/workspace/user_preference.py | 1 - apiserver/plane/db/models/workspace.py | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apiserver/plane/app/views/workspace/user_preference.py b/apiserver/plane/app/views/workspace/user_preference.py index f484a515e37..07ae70ac08b 100644 --- a/apiserver/plane/app/views/workspace/user_preference.py +++ b/apiserver/plane/app/views/workspace/user_preference.py @@ -30,7 +30,6 @@ def get(self, request, slug): keys = [ key for key, _ in WorkspaceUserPreference.UserPreferenceKeys.choices - if key not in ["projects"] ] for preference in keys: diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index 40f74510e9e..0cfde5fceaa 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -391,13 +391,11 @@ def __str__(self): class WorkspaceUserPreference(BaseModel): """Preference for the workspace for a user""" - class UserPreferenceKeys(models.TextChoices): - PROJECTS = "projects", "Projects" - ANALYTICS = "analytics", "Analytics" - CYCLES = "cycles", "Cycles" + class UserPreferenceKeys(models.TextChoices): VIEWS = "views", "Views" - YOUR_WORK = "your_work", "Your Work" + ANALYTICS = "analytics", "Analytics" DRAFTS = "drafts", "Drafts" + YOUR_WORK = "your_work", "Your Work" ARCHIVES = "archives", "Archives" workspace = models.ForeignKey( From e4c005fbae8e976a328a66bb21f473c828c63cd8 Mon Sep 17 00:00:00 2001 From: sangeethailango Date: Mon, 10 Feb 2025 19:02:19 +0530 Subject: [PATCH 07/15] chore: sidebar preference endpoint updated --- apiserver/plane/db/models/workspace.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index 0cfde5fceaa..2c0370a61ad 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -393,6 +393,7 @@ class WorkspaceUserPreference(BaseModel): class UserPreferenceKeys(models.TextChoices): VIEWS = "views", "Views" + ACTIVE_CYCLES = "active_cycles", "Active Cycles" ANALYTICS = "analytics", "Analytics" DRAFTS = "drafts", "Drafts" YOUR_WORK = "your_work", "Your Work" From c040a6697f0a53a2b1538465ad6435d059682386 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 10 Feb 2025 19:20:38 +0530 Subject: [PATCH 08/15] chore: code refactor --- packages/constants/src/workspace.ts | 2 +- .../sidebar/extended-sidebar-item.tsx | 38 +++++++++++-------- .../components/workspace/sidebar/helper.tsx | 2 +- .../workspace/sidebar/sidebar-item.tsx | 2 +- .../workspace/sidebar/workspace-menu-item.tsx | 4 +- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/constants/src/workspace.ts b/packages/constants/src/workspace.ts index c034984d5d9..5466a7fb818 100644 --- a/packages/constants/src/workspace.ts +++ b/packages/constants/src/workspace.ts @@ -272,7 +272,7 @@ export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record = observer((prop

{t(item.labelTranslationKey)}

- {isPinned ? ( - - unPinNavigationItem(workspaceSlug.toString(), item.key)} - /> - - ) : ( - - pinNavigationItem(workspaceSlug.toString(), item.key)} - /> - - )} +
+ {item.key === "active_cycles" && ( +
+ +
+ )} + {isPinned ? ( + + unPinNavigationItem(workspaceSlug.toString(), item.key)} + /> + + ) : ( + + pinNavigationItem(workspaceSlug.toString(), item.key)} + /> + + )} +
{isLastChild && } diff --git a/web/ce/components/workspace/sidebar/helper.tsx b/web/ce/components/workspace/sidebar/helper.tsx index 1d3bbf289e8..b2dafda09f1 100644 --- a/web/ce/components/workspace/sidebar/helper.tsx +++ b/web/ce/components/workspace/sidebar/helper.tsx @@ -12,7 +12,7 @@ export const getSidebarNavigationItemIcon = (key: string, className: string = "" return ; case "views": return ; - case "active-cycles": + case "active_cycles": return ; case "analytics": return ; diff --git a/web/ce/components/workspace/sidebar/sidebar-item.tsx b/web/ce/components/workspace/sidebar/sidebar-item.tsx index 517e7642a99..99b535972b9 100644 --- a/web/ce/components/workspace/sidebar/sidebar-item.tsx +++ b/web/ce/components/workspace/sidebar/sidebar-item.tsx @@ -79,7 +79,7 @@ export const SidebarItem: FC = observer((props) => { {icon} {!sidebarCollapsed &&

{t(item.labelTranslationKey)}

} - {!sidebarCollapsed && item.key === "active-cycles" && ( + {!sidebarCollapsed && item.key === "active_cycles" && (
diff --git a/web/core/components/workspace/sidebar/workspace-menu-item.tsx b/web/core/components/workspace/sidebar/workspace-menu-item.tsx index 0fd72761406..6b06390e462 100644 --- a/web/core/components/workspace/sidebar/workspace-menu-item.tsx +++ b/web/core/components/workspace/sidebar/workspace-menu-item.tsx @@ -65,12 +65,12 @@ export const SidebarWorkspaceMenuItem: FC = obser
{!sidebarCollapsed &&

{t(item.labelTranslationKey)}

}
- {!sidebarCollapsed && item.key === "active-cycles" && ( + {!sidebarCollapsed && item.key === "active_cycles" && (
From 63c74046380b4fa391f8b03e177b1c0a056452e2 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 12 Feb 2025 15:15:30 +0530 Subject: [PATCH 09/15] chore: code refactor --- .../hooks/src/use-outside-click-detector.tsx | 6 +++--- .../workspace/sidebar/extended-sidebar-item.tsx | 16 +++++----------- ...e-extended-sidebar-overview-outside-click.tsx | 2 +- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/hooks/src/use-outside-click-detector.tsx b/packages/hooks/src/use-outside-click-detector.tsx index 9436b51bf37..54979f96ac1 100644 --- a/packages/hooks/src/use-outside-click-detector.tsx +++ b/packages/hooks/src/use-outside-click-detector.tsx @@ -8,9 +8,9 @@ export const useOutsideClickDetector = ( const handleClick = (event: MouseEvent) => { if (ref.current && !ref.current.contains(event.target as any)) { // check for the closest element with attribute name data-prevent-outside-click - const preventOutsideClickElement = ( - event.target as unknown as HTMLElement | undefined - )?.closest("[data-prevent-outside-click]"); + const preventOutsideClickElement = (event.target as unknown as HTMLElement | undefined)?.closest( + "[data-prevent-outside-click]" + ); // if the closest element with attribute name data-prevent-outside-click is found, return if (preventOutsideClickElement) { return; diff --git a/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx b/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx index 90e81318e9e..663776eb4c6 100644 --- a/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx +++ b/web/ce/components/workspace/sidebar/extended-sidebar-item.tsx @@ -68,15 +68,11 @@ export const ExtendedSidebarItem: FC = observer((prop const isActive = itemHref === pathname; const pinNavigationItem = (workspaceSlug: string, key: string) => { - updateSidebarPreference(workspaceSlug, key, { - is_pinned: true, - }); + updateSidebarPreference(workspaceSlug, key, { is_pinned: true }); }; const unPinNavigationItem = (workspaceSlug: string, key: string) => { - updateSidebarPreference(workspaceSlug, key, { - is_pinned: false, - }); + updateSidebarPreference(workspaceSlug, key, { is_pinned: false }); }; const icon = getSidebarNavigationItemIcon(item.key); @@ -153,9 +149,7 @@ export const ExtendedSidebarItem: FC = observer((prop return (
@@ -205,14 +199,14 @@ export const ExtendedSidebarItem: FC = observer((prop {isPinned ? ( unPinNavigationItem(workspaceSlug.toString(), item.key)} /> ) : ( pinNavigationItem(workspaceSlug.toString(), item.key)} /> diff --git a/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx b/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx index 138328e1d3b..7c3705251e5 100644 --- a/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx +++ b/web/core/hooks/use-extended-sidebar-overview-outside-click.tsx @@ -43,7 +43,7 @@ const useExtendedSidebarOutsideClickDetector = ( return () => { document.removeEventListener("mousedown", handleClick); }; - }); + }, []); }; export default useExtendedSidebarOutsideClickDetector; From 3097b0d79c40e391bb4ca5f09ad847c3340283b9 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Sun, 16 Feb 2025 21:49:05 +0530 Subject: [PATCH 10/15] chore: radix-ui react-scroll-area added to plane ui package --- packages/ui/package.json | 1 + yarn.lock | 129 ++++++++++++++++++--------------------- 2 files changed, 62 insertions(+), 68 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index 580225477f1..fff80ee84b0 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -34,6 +34,7 @@ "@plane/hooks": "*", "@plane/utils": "*", "@popperjs/core": "^2.11.8", + "@radix-ui/react-scroll-area": "^1.2.3", "clsx": "^2.0.0", "emoji-picker-react": "^4.5.16", "lodash": "^4.17.21", diff --git a/yarn.lock b/yarn.lock index 8acfac5931f..45d9a5d68a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1102,127 +1102,127 @@ "@esbuild/aix-ppc64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== "@esbuild/android-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== "@esbuild/android-arm@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== "@esbuild/android-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== "@esbuild/darwin-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== "@esbuild/darwin-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== "@esbuild/freebsd-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== "@esbuild/freebsd-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== "@esbuild/linux-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== "@esbuild/linux-arm@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== "@esbuild/linux-ia32@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== "@esbuild/linux-loong64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== "@esbuild/linux-mips64el@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== "@esbuild/linux-ppc64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== "@esbuild/linux-riscv64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== "@esbuild/linux-s390x@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== "@esbuild/linux-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== "@esbuild/netbsd-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== "@esbuild/netbsd-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== "@esbuild/openbsd-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== "@esbuild/openbsd-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== "@esbuild/sunos-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== "@esbuild/win32-arm64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== "@esbuild/win32-ia32@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== "@esbuild/win32-x64@0.25.0": version "0.25.0" - resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": @@ -2224,6 +2224,11 @@ dependencies: "@opentelemetry/instrumentation" "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0" +"@radix-ui/number@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" + integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== + "@radix-ui/primitive@1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" @@ -2259,6 +2264,11 @@ aria-hidden "^1.2.4" react-remove-scroll "^2.6.3" +"@radix-ui/react-direction@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" + integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== + "@radix-ui/react-dismissable-layer@1.1.5": version "1.1.5" resolved "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz#96dde2be078c694a621e55e047406c58cd5fe774" @@ -2314,6 +2324,21 @@ dependencies: "@radix-ui/react-slot" "1.1.2" +"@radix-ui/react-scroll-area@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.3.tgz#6a9a7897add739ce84b517796ee345d495893d3f" + integrity sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ== + dependencies: + "@radix-ui/number" "1.1.0" + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.2" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-slot@1.1.2", "@radix-ui/react-slot@^1.1.1": version "1.1.2" resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz#daffff7b2bfe99ade63b5168407680b93c00e1c6" @@ -4174,29 +4199,22 @@ resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== -"@types/react@*": - version "19.0.8" - resolved "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz#7098e6159f2a61e4f4cef2c1223c044a9bec590e" - integrity sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw== +"@types/react@*", "@types/react@^18.3.11": + version "18.3.18" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.18.tgz#9b382c4cd32e13e463f97df07c2ee3bbcd26904b" + integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ== dependencies: + "@types/prop-types" "*" csstype "^3.0.2" "@types/react@18.3.1": version "18.3.1" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== dependencies: "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^18.3.11": - version "18.3.18" - resolved "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz#9b382c4cd32e13e463f97df07c2ee3bbcd26904b" - integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/reactcss@*": version "1.2.13" resolved "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.13.tgz#11c7468cc96b5353f7af998a5664deae21c7af08" @@ -6554,7 +6572,7 @@ esbuild-register@^3.5.0: esbuild@0.25.0, "esbuild@^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0", esbuild@^0.19.2: version "0.25.0" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw== optionalDependencies: "@esbuild/aix-ppc64" "0.25.0" @@ -9092,7 +9110,7 @@ mz@^2.7.0: nanoid@3.3.8, nanoid@^3.3.6, nanoid@^3.3.8: version "3.3.8" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== napi-build-utils@^2.0.0: @@ -11387,16 +11405,7 @@ streamx@^2.15.0, streamx@^2.21.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11489,14 +11498,7 @@ string_decoder@^1.1.1, string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12724,16 +12726,7 @@ word-wrap@^1.2.5: resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From fc1e71c2456542b24345762059b0cd46f9b05497 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Sun, 16 Feb 2025 21:49:33 +0530 Subject: [PATCH 11/15] chore: scrollbar color token added to tailwind config --- packages/tailwind-config/tailwind.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/tailwind-config/tailwind.config.js b/packages/tailwind-config/tailwind.config.js index 0731894f06f..3e34ca1f042 100644 --- a/packages/tailwind-config/tailwind.config.js +++ b/packages/tailwind-config/tailwind.config.js @@ -203,6 +203,11 @@ module.exports = { }, }, backdrop: "rgba(0, 0, 0, 0.25)", + scrollbar: { + neutral: "rgba(96, 100, 108, 0.1)", + hover: "rgba(96, 100, 108, 0.25)", + active: "rgba(96, 100, 108, 0.7)", + }, }, onboarding: { background: { From 56534b4dbaa0c0f6eac823380943020253c1fb85 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Sun, 16 Feb 2025 21:50:15 +0530 Subject: [PATCH 12/15] dev: scroll area component --- packages/ui/src/index.ts | 1 + packages/ui/src/scroll-area.tsx | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 packages/ui/src/scroll-area.tsx diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 279e19a3e34..f583178ddd0 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -25,6 +25,7 @@ export * from "./popovers"; export * from "./tables"; export * from "./header"; export * from "./row"; +export * from "./scroll-area"; export * from "./content-wrapper"; export * from "./card"; export * from "./tag"; diff --git a/packages/ui/src/scroll-area.tsx b/packages/ui/src/scroll-area.tsx new file mode 100644 index 00000000000..3a90b57dcbd --- /dev/null +++ b/packages/ui/src/scroll-area.tsx @@ -0,0 +1,32 @@ +"use client"; +import * as RadixScrollArea from "@radix-ui/react-scroll-area"; +import React, { FC } from "react"; +import { cn } from "../helpers"; + +type TScrollAreaProps = { + type?: "auto" | "always" | "scroll" | "hover"; + className?: string; + scrollHideDelay?: number; + children: React.ReactNode; +}; + +export const ScrollArea: FC = (props) => { + const { type = "always", className = "", scrollHideDelay = 600, children } = props; + return ( + + {children} + + + + + + + + ); +}; From 4bcb0635d2ec04fa1ef1353f36afec06d4a80e46 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 17 Feb 2025 01:30:22 +0530 Subject: [PATCH 13/15] chore-scroll-area-component-improvement --- packages/ui/src/scroll-area.tsx | 46 ++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/scroll-area.tsx b/packages/ui/src/scroll-area.tsx index 3a90b57dcbd..948360e15bc 100644 --- a/packages/ui/src/scroll-area.tsx +++ b/packages/ui/src/scroll-area.tsx @@ -7,25 +7,59 @@ type TScrollAreaProps = { type?: "auto" | "always" | "scroll" | "hover"; className?: string; scrollHideDelay?: number; + size?: "sm" | "md" | "lg"; children: React.ReactNode; }; export const ScrollArea: FC = (props) => { - const { type = "always", className = "", scrollHideDelay = 600, children } = props; + const { type = "always", className = "", scrollHideDelay = 600, size = "md", children } = props; + + const sizeStyles = { + sm: "p-[0.112rem] data-[orientation=vertical]:w-2.5 data-[orientation=horizontal]:h-2.5", + md: "p-[0.152rem] data-[orientation=vertical]:w-3 data-[orientation=horizontal]:h-3", + lg: "p-[0.225rem] data-[orientation=vertical]:w-4 data-[orientation=horizontal]:h-4", + }; + + const thumbSizeStyles = { + sm: "before:absolute before:left-1/2 before:top-1/2 before:size-full before:min-h-11 before:min-w-11 before:-translate-x-1/2 before:-translate-y-1/2", + md: "before:absolute before:left-1/2 before:top-1/2 before:size-full before:min-h-14 before:min-w-14 before:-translate-x-1/2 before:-translate-y-1/2", + lg: "before:absolute before:left-1/2 before:top-1/2 before:size-full before:min-h-17 before:min-w-17 before:-translate-x-1/2 before:-translate-y-1/2", + }; + return ( - + {children} - + - + ); From 9209959f5db2d1825c574ec5ee25d19eb46e7372 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Mon, 17 Feb 2025 01:36:01 +0530 Subject: [PATCH 14/15] fix: build error --- web/core/components/workspace/sidebar/project-navigation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/components/workspace/sidebar/project-navigation.tsx b/web/core/components/workspace/sidebar/project-navigation.tsx index a8a76ad6083..f961f02345b 100644 --- a/web/core/components/workspace/sidebar/project-navigation.tsx +++ b/web/core/components/workspace/sidebar/project-navigation.tsx @@ -154,7 +154,7 @@ export const ProjectNavigation: FC = observer((props) => { Date: Mon, 17 Feb 2025 21:38:29 +0530 Subject: [PATCH 15/15] chore: code refactor --- packages/ui/src/icons/overview-icon.tsx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/icons/overview-icon.tsx b/packages/ui/src/icons/overview-icon.tsx index 81b07224b69..6c32881d885 100644 --- a/packages/ui/src/icons/overview-icon.tsx +++ b/packages/ui/src/icons/overview-icon.tsx @@ -2,20 +2,13 @@ import * as React from "react"; import { ISvgIcons } from "./type"; -export const OverviewIcon: React.FC = ({ width = "16", height = "16", className = "" }) => ( - +export const OverviewIcon: React.FC = ({ className = "text-current", ...rest }) => ( + );