mirror of
https://github.com/makeplane/plane.git
synced 2025-12-21 05:10:24 -06:00
[WEB-5614] chore: platform design token enhancements (#8373)
This commit is contained in:
committed by
GitHub
parent
78f4a7294b
commit
67416935b4
@@ -102,7 +102,7 @@ export const IssuesHeader = observer(function IssuesHeader() {
|
||||
)}
|
||||
</Header.LeftItem>
|
||||
<Header.RightItem>
|
||||
<div className="hidden gap-3 md:flex">
|
||||
<div className="hidden gap-2 md:flex">
|
||||
<HeaderFilters
|
||||
projectId={projectId}
|
||||
currentProjectDetails={currentProjectDetails}
|
||||
|
||||
@@ -40,7 +40,7 @@ export const TopNavigationRoot = observer(function TopNavigationRoot() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("flex items-center min-h-11 w-full px-3.5 bg-canvas z-[27] transition-all duration-300", {
|
||||
className={cn("flex items-center min-h-10 w-full px-3.5 bg-canvas z-[27] transition-all duration-300", {
|
||||
"px-2": !showLabel,
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -19,7 +19,7 @@ const IconWrapper = React.memo(function IconWrapper({ icon }: { icon: React.Reac
|
||||
IconWrapper.displayName = "IconWrapper";
|
||||
|
||||
const LabelWrapper = React.memo(function LabelWrapper({ label }: { label: React.ReactNode }) {
|
||||
return <div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>;
|
||||
return <div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate text-primary">{label}</div>;
|
||||
});
|
||||
|
||||
LabelWrapper.displayName = "LabelWrapper";
|
||||
|
||||
@@ -86,7 +86,7 @@ export const DashboardWidgets = observer(function DashboardWidgets() {
|
||||
{!isWikiApp && <NoProjectsEmptyState />}
|
||||
|
||||
{isAnyWidgetEnabled ? (
|
||||
<div className="flex flex-col divide-y-[1px] divide-subtle">
|
||||
<div className="flex flex-col">
|
||||
{orderedWidgets.map((key) => {
|
||||
const WidgetComponent = HOME_WIDGETS_LIST[key]?.component;
|
||||
const isEnabled = widgetsMap[key]?.is_enabled;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
export function LinksEmptyState() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full">
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full rounded-lg">
|
||||
<EmptyStateCompact
|
||||
assetKey="link"
|
||||
assetClassName="w-20 h-20"
|
||||
|
||||
@@ -3,12 +3,12 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Check, Hotel, Users } from "lucide-react";
|
||||
import { Check, Hotel } from "lucide-react";
|
||||
// plane ui
|
||||
import { EUserPermissions, EUserPermissionsLevel, PROJECT_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useLocalStorage } from "@plane/hooks";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { ProjectIcon, CloseIcon } from "@plane/propel/icons";
|
||||
import { MembersPropertyIcon, ProjectIcon, CloseIcon } from "@plane/propel/icons";
|
||||
import { cn, getFileURL } from "@plane/utils";
|
||||
// helpers
|
||||
// hooks
|
||||
@@ -66,7 +66,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
id: "invite-team",
|
||||
title: "home.empty.invite_team.title",
|
||||
description: "home.empty.invite_team.description",
|
||||
icon: <Users className="size-4" />,
|
||||
icon: <MembersPropertyIcon className="size-4" />,
|
||||
flag: "visited_members",
|
||||
cta: {
|
||||
text: "home.empty.invite_team.cta",
|
||||
@@ -103,7 +103,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={`/${workspaceSlug}/profile/${currentUser?.id}`}>
|
||||
<span className="relative flex size-4 items-center justify-center rounded-full bg-gray-700 p-4 capitalize text-on-color text-13">
|
||||
<span className="relative flex size-4 items-center justify-center rounded-full bg-[#028375] p-4 capitalize text-on-color text-13">
|
||||
{(currentUser?.email ?? currentUser?.display_name ?? "?")[0]}
|
||||
</span>
|
||||
</Link>
|
||||
@@ -150,7 +150,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
{EMPTY_STATE_DATA.map((item) => {
|
||||
const isStateComplete = isComplete(item.flag);
|
||||
return (
|
||||
<div key={item.id} className="flex flex-col p-4 bg-surface-1 rounded-xl border border-subtle/40">
|
||||
<div key={item.id} className="flex flex-col p-4 bg-layer-2 rounded-xl border border-subtle">
|
||||
<div
|
||||
className={cn("grid place-items-center bg-surface-2 rounded-full size-9 mb-3 text-placeholder", {
|
||||
"text-accent-primary bg-accent-primary/10": !isStateComplete,
|
||||
@@ -187,7 +187,7 @@ export const NoProjectsEmptyState = observer(function NoProjectsEmptyState() {
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="text-accent-primary hover:text-accent-secondary text-13 font-medium"
|
||||
className="text-accent-primary hover:text-accent-secondary text-13 font-medium text-left"
|
||||
onClick={item.cta.onClick}
|
||||
>
|
||||
{t(item.cta.text)}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function RecentsEmptyState({ type }: { type: string }) {
|
||||
const { assetKey, text } = getDisplayContent(type);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full">
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full rounded-lg">
|
||||
<EmptyStateCompact assetKey={assetKey} assetClassName="size-20" title={t(text)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { EmptyStateCompact } from "@plane/propel/empty-state";
|
||||
export function StickiesEmptyState() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full">
|
||||
<div className="flex items-center justify-center py-10 bg-layer-1 w-full rounded-lg">
|
||||
<EmptyStateCompact assetKey="note" assetClassName="size-20" title={t("stickies.empty_state.simple")} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TContextMenuItem } from "@plane/ui";
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||
import { useLayoutMenuItems } from "@/components/common/quick-actions-helper";
|
||||
import { Ellipsis, MoreHorizontal } from "lucide-react";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
@@ -13,7 +13,7 @@ type Props = {
|
||||
storeType: "PROJECT" | "EPIC";
|
||||
};
|
||||
|
||||
export const LayoutQuickActions: React.FC<Props> = observer((props) => {
|
||||
export const LayoutQuickActions = observer(function LayoutQuickActions(props: Props) {
|
||||
const { workspaceSlug, projectId, storeType } = props;
|
||||
|
||||
const layoutLink = `${workspaceSlug}/projects/${projectId}/${storeType === "EPIC" ? "epics" : "issues"}`;
|
||||
@@ -49,6 +49,7 @@ export const LayoutQuickActions: React.FC<Props> = observer((props) => {
|
||||
closeOnSelect
|
||||
maxHeight="lg"
|
||||
className="flex-shrink-0 flex items-center justify-center size-[26px] rounded"
|
||||
customButton={<IconButton size="lg" variant="tertiary" icon={Ellipsis} />}
|
||||
>
|
||||
{MENU_ITEMS.map((item) => {
|
||||
if (item.shouldRender === false) return null;
|
||||
|
||||
@@ -220,7 +220,7 @@ export const IssuePeekOverviewHeader = observer(function IssuePeekOverviewHeader
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<NameDescriptionUpdateStatus isSubmitting={isSubmitting} />
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
{currentUser && !isArchived && (
|
||||
<IssueSubscription workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} />
|
||||
)}
|
||||
|
||||
@@ -35,7 +35,6 @@ export const WorkspaceDraftEmptyState = observer(function WorkspaceDraftEmptySta
|
||||
title={t("workspace_empty_state.drafts.title")}
|
||||
description={t("workspace_empty_state.drafts.description")}
|
||||
assetKey="draft"
|
||||
assetClassName="size-20"
|
||||
actions={[
|
||||
{
|
||||
label: t("workspace_empty_state.drafts.cta_primary"),
|
||||
|
||||
@@ -176,7 +176,7 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
<ModalCore isOpen={isOpen} handleClose={onClose} position={EModalPosition.CENTER} width={EModalWidth.XXL}>
|
||||
<div className="flex flex-col max-h-[90vh] bg-surface-1 rounded-lg">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between px-6 py-4">
|
||||
<div className="flex justify-between px-6 pt-4">
|
||||
<div>
|
||||
<h2 className="text-18 font-semibold text-primary">{t("customize_navigation")}</h2>
|
||||
<p className="mt-1 text-13 text-tertiary">
|
||||
@@ -213,7 +213,7 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
/>
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{getSidebarNavigationItemIcon(item.key)}
|
||||
<label className="text-13 text-secondary flex-1 cursor-pointer">
|
||||
<label className="text-13 text-primary flex-1 cursor-pointer">
|
||||
{t(item.labelTranslationKey)}
|
||||
</label>
|
||||
</div>
|
||||
@@ -244,7 +244,7 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
/>
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
{icon}
|
||||
<span className="text-13 text-secondary">{t(item.labelTranslationKey)}</span>
|
||||
<span className="text-13 text-primary">{t(item.labelTranslationKey)}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -271,8 +271,8 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
className="size-4 text-accent-primary focus:ring-accent-strong mt-1"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<div className="text-13 text-secondary">{t("accordion_navigation_control")}</div>
|
||||
<div className="text-11 text-tertiary">
|
||||
<div className="text-13 text-primary">{t("accordion_navigation_control")}</div>
|
||||
<div className="text-11 text-secondary">
|
||||
Feature tabs will appear as nested items under project and acts as accordion.
|
||||
</div>
|
||||
</div>
|
||||
@@ -288,8 +288,8 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
className="size-4 text-accent-primary focus:ring-accent-strong mt-1"
|
||||
/>
|
||||
<div className="flex-1">
|
||||
<div className="text-13 text-secondary">{t("horizontal_navigation_bar")}</div>
|
||||
<div className="text-11 text-tertiary">
|
||||
<div className="text-13 text-primary">{t("horizontal_navigation_bar")}</div>
|
||||
<div className="text-11 text-secondary">
|
||||
Feature tabs will appear as horizontal tabs inside a project.
|
||||
</div>
|
||||
</div>
|
||||
@@ -297,20 +297,20 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi
|
||||
</div>
|
||||
|
||||
{/* Limited Projects Checkbox */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<label className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-surface-2 cursor-pointer">
|
||||
<Checkbox
|
||||
checked={projectPreferences.showLimitedProjects}
|
||||
onChange={(e) => updateShowLimitedProjects(e.target.checked)}
|
||||
/>
|
||||
<span className="text-13 text-secondary">{t("show_limited_projects_on_sidebar")}</span>
|
||||
<span className="text-13 text-primary">{t("show_limited_projects_on_sidebar")}</span>
|
||||
</label>
|
||||
|
||||
{projectPreferences.showLimitedProjects && (
|
||||
<div className="pl-8">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<label className="text-11 text-tertiary w-full">{t("enter_number_of_projects")}</label>
|
||||
<div className="flex flex-col gap-2 w-full pb-1.5">
|
||||
<label className="text-11 text-secondary w-full">{t("enter_number_of_projects")}</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
|
||||
@@ -13,7 +13,7 @@ export function SidebarNavItem(props: TSidebarNavItem) {
|
||||
className={cn(
|
||||
"cursor-pointer relative group w-full flex items-center justify-between gap-1.5 rounded-md px-2 py-1 outline-none",
|
||||
{
|
||||
"text-secondary !bg-layer-transparent-active": isActive,
|
||||
"text-primary !bg-layer-transparent-active": isActive,
|
||||
"text-secondary hover:bg-layer-transparent-hover active:bg-layer-transparent-active": !isActive,
|
||||
},
|
||||
className
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PanelLeft } from "lucide-react";
|
||||
// hooks
|
||||
import { useAppTheme } from "@/hooks/store/use-app-theme";
|
||||
import { isSidebarToggleVisible } from "@/plane-web/components/desktop";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
export const AppSidebarToggleButton = observer(function AppSidebarToggleButton() {
|
||||
// store hooks
|
||||
@@ -10,14 +11,14 @@ export const AppSidebarToggleButton = observer(function AppSidebarToggleButton()
|
||||
|
||||
if (!isSidebarToggleVisible()) return null;
|
||||
return (
|
||||
<button
|
||||
className="flex items-center justify-center size-6 rounded-md text-secondary hover:text-accent-primary hover:bg-surface-2"
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={PanelLeft}
|
||||
onClick={() => {
|
||||
if (sidebarPeek) toggleSidebarPeek(false);
|
||||
toggleSidebar();
|
||||
}}
|
||||
>
|
||||
<PanelLeft className="size-4" />
|
||||
</button>
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import useSize from "@/hooks/use-window-size";
|
||||
// plane web components
|
||||
import { WorkspaceEditionBadge } from "@/plane-web/components/workspace/edition-badge";
|
||||
import { AppSidebarToggleButton } from "./sidebar-toggle-button";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
type TSidebarWrapperProps = {
|
||||
title: string;
|
||||
@@ -51,13 +52,12 @@ export const SidebarWrapper = observer(function SidebarWrapper(props: TSidebarWr
|
||||
<span className="text-16 text-primary font-medium pt-1">{title}</span>
|
||||
{title === "Projects" && (
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-center size-6 rounded-md text-secondary hover:text-accent-primary hover:bg-surface-2"
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={PreferencesIcon}
|
||||
onClick={() => setIsCustomizeNavDialogOpen(true)}
|
||||
>
|
||||
<PreferencesIcon className="size-4" />
|
||||
</button>
|
||||
/>
|
||||
<AppSidebarToggleButton />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { PopoverMenu } from "@plane/ui";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// local imports
|
||||
import { NotificationFilterOptionItem } from "./menu-option-item";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
export const NotificationFilter = observer(function NotificationFilter() {
|
||||
// hooks
|
||||
@@ -26,9 +27,7 @@ export const NotificationFilter = observer(function NotificationFilter() {
|
||||
data={translatedFilterTypeOptions}
|
||||
button={
|
||||
<Tooltip tooltipContent={t("notification.options.filters")} isMobile={isMobile} position="bottom">
|
||||
<div className="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-layer-1 rounded-xs outline-none">
|
||||
<ListFilter className="h-3 w-3" />
|
||||
</div>
|
||||
<IconButton size="base" variant="ghost" icon={ListFilter} />
|
||||
</Tooltip>
|
||||
}
|
||||
keyExtractor={(item: { label: string; value: ENotificationFilterType }) => item.value}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Check, CheckCircle, Clock } from "lucide-react";
|
||||
import { Check, CheckCircle, Clock, MoreVertical } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { ArchiveIcon } from "@plane/propel/icons";
|
||||
@@ -10,6 +10,7 @@ import { PopoverMenu } from "@plane/ui";
|
||||
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
|
||||
// local imports
|
||||
import { NotificationMenuOptionItem } from "./menu-item";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
export type TPopoverMenuOptions = {
|
||||
key: string;
|
||||
@@ -72,7 +73,7 @@ export const NotificationHeaderMenuOption = observer(function NotificationHeader
|
||||
return (
|
||||
<PopoverMenu
|
||||
data={popoverMenuOptions}
|
||||
buttonClassName="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-layer-1 bg-surface-1 rounded-xs outline-none"
|
||||
button={<IconButton size="base" variant="ghost" icon={MoreVertical} />}
|
||||
keyExtractor={(item: TPopoverMenuOptions) => item.key}
|
||||
panelClassName="p-0 py-2 rounded-md border border-subtle bg-surface-1 space-y-1"
|
||||
render={(item: TPopoverMenuOptions) => <NotificationMenuOptionItem {...item} />}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// local imports
|
||||
import { NotificationFilter } from "../../filters/menu";
|
||||
import { NotificationHeaderMenuOption } from "./menu-option";
|
||||
import { IconButton } from "@plane/propel/icon-button";
|
||||
|
||||
type TNotificationSidebarHeaderOptions = {
|
||||
workspaceSlug: string;
|
||||
@@ -55,32 +56,29 @@ export const NotificationSidebarHeaderOptions = observer(function NotificationSi
|
||||
<div className="relative flex justify-center items-center gap-2 text-body-xs-medium">
|
||||
{/* mark all notifications as read*/}
|
||||
<Tooltip tooltipContent={t("notification.options.mark_all_as_read")} isMobile={isMobile} position="bottom">
|
||||
<div
|
||||
className="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-layer-1 rounded-xs"
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
data-ph-element={NOTIFICATION_TRACKER_ELEMENTS.MARK_ALL_AS_READ_BUTTON}
|
||||
icon={loader === ENotificationLoader.MARK_ALL_AS_READY ? Spinner : CheckCheck}
|
||||
onClick={() => {
|
||||
captureSuccess({
|
||||
eventName: NOTIFICATION_TRACKER_EVENTS.all_marked_read,
|
||||
});
|
||||
handleMarkAllNotificationsAsRead();
|
||||
}}
|
||||
>
|
||||
{loader === ENotificationLoader.MARK_ALL_AS_READY ? (
|
||||
<Spinner height="14px" width="14px" />
|
||||
) : (
|
||||
<CheckCheck className="h-3 w-3" />
|
||||
)}
|
||||
</div>
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* refetch current notifications */}
|
||||
<Tooltip tooltipContent={t("notification.options.refresh")} isMobile={isMobile} position="bottom">
|
||||
<div
|
||||
className="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-layer-1 rounded-xs"
|
||||
<IconButton
|
||||
size="base"
|
||||
variant="ghost"
|
||||
icon={RefreshCw}
|
||||
className={loader === ENotificationLoader.MUTATION_LOADER ? "animate-spin" : ""}
|
||||
onClick={refreshNotifications}
|
||||
>
|
||||
<RefreshCw className={`h-3 w-3 ${loader === ENotificationLoader.MUTATION_LOADER ? "animate-spin" : ""}`} />
|
||||
</div>
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{/* notification filters */}
|
||||
|
||||
@@ -27,7 +27,7 @@ export const NotificationSidebarHeader = observer(function NotificationSidebarHe
|
||||
component={
|
||||
<BreadcrumbLink
|
||||
label={t("notification.label")}
|
||||
icon={<InboxIcon className="h-4 w-4 text-tertiary" />}
|
||||
icon={<InboxIcon className="h-4 w-4 text-primary" />}
|
||||
disableTooltip
|
||||
/>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user