Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 47 additions & 41 deletions src/packages/frontend/projects/projects-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import type { TabsProps } from "antd";
import { Avatar, Popover, Tabs, Tooltip } from "antd";
import { cloneElement, CSSProperties, useMemo, useState } from "react";
import { CSSProperties, useMemo, useState } from "react";

import {
CSS,
Expand All @@ -17,8 +17,8 @@ import {
import { set_window_title } from "@cocalc/frontend/browser";
import { Icon, Loading } from "@cocalc/frontend/components";
import {
SortableTabs,
SortableTab,
SortableTabs,
useItemContext,
useSortable,
} from "@cocalc/frontend/components/sortable-tabs";
Expand All @@ -31,7 +31,6 @@ import { COLORS } from "@cocalc/util/theme";
import { useProjectState } from "../project/page/project-state-hook";
import { useProjectHasInternetAccess } from "../project/settings/has-internet-access-hook";
import { BuyLicenseForProject } from "../site-licenses/purchase/buy-license-for-project";
import { blendBackgroundColor } from "./util";

const PROJECT_NAME_STYLE: CSS = {
whiteSpace: "nowrap",
Expand Down Expand Up @@ -176,15 +175,36 @@ function ProjectTab({ project_id }: ProjectTabProps) {

function renderAvatar() {
const avatar = project?.get("avatar_image_tiny");
if (!avatar) return;
return (
<Avatar
style={{ marginTop: "-2px" }}
shape="circle"
icon={<img src={project.get("avatar_image_tiny")} />}
size={20}
/>
);
const color = project?.get("color");

if (avatar) {
// Avatar exists: show it with colored border
return (
<Avatar
style={{
marginTop: "-2px",
border: color ? `2px solid ${color}` : undefined,
}}
shape="circle"
icon={<img src={project.get("avatar_image_tiny")} />}
size={20}
/>
);
} else if (color) {
// No avatar but has color: show colored circle
return (
<Avatar
style={{
marginTop: "-2px",
backgroundColor: color,
}}
shape="circle"
size={20}
/>
);
}

return undefined;
}

function onMouseUp(e: React.MouseEvent) {
Expand Down Expand Up @@ -242,7 +262,7 @@ export function ProjectsNav(props: ProjectsNavProps) {
const projectActions = useActions("projects");
const activeTopTab = useTypedRedux("page", "active_top_tab");
const openProjects = useTypedRedux("projects", "open_projects");
const project_map = useTypedRedux("projects", "project_map");
//const project_map = useTypedRedux("projects", "project_map");

const items: TabsProps["items"] = useMemo(() => {
if (openProjects == null) return [];
Expand All @@ -259,14 +279,14 @@ export function ProjectsNav(props: ProjectsNavProps) {
return openProjects.toJS().map((project_id) => project_id);
}, [openProjects]);

const onEdit = (project_id: string, action: "add" | "remove") => {
if (action == "add") {
function onEdit(project_id: string, action: "add" | "remove") {
if (action === "add") {
actions.set_active_tab("projects");
} else {
// close given project
actions.close_project_tab(project_id);
}
};
}

function onDragEnd(event) {
const { active, over } = event;
Expand All @@ -289,39 +309,25 @@ export function ProjectsNav(props: ProjectsNavProps) {
{(node) => {
const project_id = node.key;
const isActive = project_id === activeTopTab;
const projectColor = project_map?.getIn([project_id, "color"]) as
| string
| undefined;

const wrapperStyle: CSS = {
border: isActive
? `2px solid ${projectColor ? projectColor : "#d3d3d3"}`
: `2px solid ${projectColor ? projectColor : "transparent"}`,
? `2px solid ${"#d3d3d3"}`
: `2px solid ${"transparent"}`,
borderRadius: "8px",
};

// Add background color if project has a color
if (projectColor) {
// Active tab: lighter/brighter (less color), inactive: darker (more color)
wrapperStyle.backgroundColor = blendBackgroundColor(
projectColor,
isActive ? "white" : COLORS.GRAY_LL,
!isActive,
);
}

// Clone the node with additional style props to override Ant Design's background
const styledNode = cloneElement(node, {
style: {
...node.props.style,
backgroundColor: wrapperStyle.backgroundColor,
},
});

// this is similar to the renderTabBar in sortable-tabs.tsx, but with the above, also styles the background
// Kept for reference, this allows to tweak the node props directly
// const styledNode = cloneElement(node, {
// style: {
// ...node.props.style,
// backgroundColor: wrapperStyle.backgroundColor,
// },
// });

return (
<SortableTab key={node.key} id={node.key} style={wrapperStyle}>
{styledNode}
{node}
</SortableTab>
);
}}
Expand Down