Skip to content

Commit d017f99

Browse files
committed
static tabs and tab badges
1 parent 58a1d6c commit d017f99

File tree

7 files changed

+148
-32
lines changed

7 files changed

+148
-32
lines changed

apps/array/src/renderer/features/panels/components/DraggableTab.tsx

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useSortable } from "@dnd-kit/react/sortable";
22
import { Cross2Icon } from "@radix-ui/react-icons";
3-
import { Box, Button, Flex, IconButton, Text } from "@radix-ui/themes";
3+
import { Box, Flex, IconButton, Text } from "@radix-ui/themes";
44
import type React from "react";
55

66
interface DraggableTabProps {
@@ -9,10 +9,10 @@ interface DraggableTabProps {
99
label: string;
1010
isActive: boolean;
1111
index: number;
12-
draggable?: boolean;
1312
onSelect: () => void;
1413
onClose?: () => void;
1514
icon?: React.ReactNode;
15+
badge?: React.ReactNode;
1616
hasUnsavedChanges?: boolean;
1717
}
1818

@@ -22,10 +22,10 @@ export const DraggableTab: React.FC<DraggableTabProps> = ({
2222
label,
2323
isActive,
2424
index,
25-
draggable = true,
2625
onSelect,
2726
onClose,
2827
icon,
28+
badge,
2929
hasUnsavedChanges,
3030
}) => {
3131
const { ref, isDragging } = useSortable({
@@ -37,35 +37,8 @@ export const DraggableTab: React.FC<DraggableTabProps> = ({
3737
easing: "ease",
3838
},
3939
data: { tabId, panelId, type: "tab" },
40-
disabled: !draggable,
4140
});
4241

43-
if (!draggable) {
44-
return (
45-
<Flex align="center" flexShrink="0" ml="2" mr="2" px="2" py="1">
46-
<Button
47-
variant="ghost"
48-
color="gray"
49-
size="1"
50-
onClick={onSelect}
51-
style={{
52-
backgroundColor: isActive ? "var(--gray-a3)" : undefined,
53-
}}
54-
>
55-
{icon && (
56-
<Box style={{ display: "flex", alignItems: "center" }}>{icon}</Box>
57-
)}
58-
{label}
59-
{hasUnsavedChanges && (
60-
<Text size="1" style={{ color: "var(--amber-9)" }}>
61-
62-
</Text>
63-
)}
64-
</Button>
65-
</Flex>
66-
);
67-
}
68-
6942
return (
7043
<Flex
7144
ref={ref}
@@ -107,6 +80,7 @@ export const DraggableTab: React.FC<DraggableTabProps> = ({
10780
>
10881
{label}
10982
</Text>
83+
{badge}
11084
{hasUnsavedChanges && (
11185
<Text size="1" style={{ color: "var(--amber-9)", marginLeft: "2px" }}>
11286
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type React from "react";
2+
import { DraggableTab } from "./DraggableTab";
3+
import { StaticTab } from "./StaticTab";
4+
5+
interface PanelTabProps {
6+
tabId: string;
7+
panelId: string;
8+
label: string;
9+
isActive: boolean;
10+
index: number;
11+
draggable?: boolean;
12+
onSelect: () => void;
13+
onClose?: () => void;
14+
icon?: React.ReactNode;
15+
badge?: React.ReactNode;
16+
hasUnsavedChanges?: boolean;
17+
}
18+
19+
export const PanelTab: React.FC<PanelTabProps> = ({
20+
tabId,
21+
panelId,
22+
label,
23+
isActive,
24+
index,
25+
draggable = true,
26+
onSelect,
27+
onClose,
28+
icon,
29+
badge,
30+
hasUnsavedChanges,
31+
}) => {
32+
if (!draggable) {
33+
return (
34+
<StaticTab
35+
label={label}
36+
isActive={isActive}
37+
onSelect={onSelect}
38+
icon={icon}
39+
badge={badge}
40+
hasUnsavedChanges={hasUnsavedChanges}
41+
/>
42+
);
43+
}
44+
45+
return (
46+
<DraggableTab
47+
tabId={tabId}
48+
panelId={panelId}
49+
label={label}
50+
isActive={isActive}
51+
index={index}
52+
onSelect={onSelect}
53+
onClose={onClose}
54+
icon={icon}
55+
badge={badge}
56+
hasUnsavedChanges={hasUnsavedChanges}
57+
/>
58+
);
59+
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Box, Button, Flex, Text } from "@radix-ui/themes";
2+
import type React from "react";
3+
4+
interface StaticTabProps {
5+
label: string;
6+
isActive: boolean;
7+
onSelect: () => void;
8+
icon?: React.ReactNode;
9+
badge?: React.ReactNode;
10+
hasUnsavedChanges?: boolean;
11+
}
12+
13+
export const StaticTab: React.FC<StaticTabProps> = ({
14+
label,
15+
isActive,
16+
onSelect,
17+
icon,
18+
badge,
19+
hasUnsavedChanges,
20+
}) => {
21+
return (
22+
<Flex align="center" flexShrink="0" ml="2" mr="2" px="2" py="1">
23+
<Button
24+
variant="ghost"
25+
color="gray"
26+
size="1"
27+
onClick={onSelect}
28+
style={{
29+
backgroundColor: isActive ? "var(--gray-a3)" : undefined,
30+
}}
31+
>
32+
{icon && (
33+
<Box style={{ display: "flex", alignItems: "center" }}>{icon}</Box>
34+
)}
35+
{label}
36+
{badge}
37+
{hasUnsavedChanges && (
38+
<Text size="1" style={{ color: "var(--amber-9)" }}>
39+
40+
</Text>
41+
)}
42+
</Button>
43+
</Flex>
44+
);
45+
};

apps/array/src/renderer/features/panels/components/TabbedPanel.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { useDroppable } from "@dnd-kit/react";
22
import { Box, Flex } from "@radix-ui/themes";
33
import type React from "react";
44
import type { PanelContent } from "../store/panelStore";
5-
import { DraggableTab } from "./DraggableTab";
65
import { PanelDropZones } from "./PanelDropZones";
6+
import { PanelTab } from "./PanelTab";
77

88
interface TabbedPanelProps {
99
panelId: string;
@@ -48,7 +48,7 @@ export const TabbedPanel: React.FC<TabbedPanelProps> = ({
4848
}}
4949
>
5050
{content.tabs.map((tab, index) => (
51-
<DraggableTab
51+
<PanelTab
5252
key={tab.id}
5353
tabId={tab.id}
5454
panelId={panelId}
@@ -67,6 +67,7 @@ export const TabbedPanel: React.FC<TabbedPanelProps> = ({
6767
}
6868
icon={tab.icon}
6969
hasUnsavedChanges={tab.hasUnsavedChanges}
70+
badge={tab.badge}
7071
/>
7172
))}
7273
{/* Spacer to increas DND area */}

apps/array/src/renderer/features/panels/hooks/usePanelLayoutHooks.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ChangesTabBadge } from "@features/task-detail/components/ChangesTabBadge";
12
import { TabContentRenderer } from "@features/task-detail/components/TabContentRenderer";
23
import type { Task } from "@shared/types";
34
import { useCallback, useEffect, useMemo, useRef } from "react";
@@ -65,6 +66,10 @@ export function useTabInjection(
6566
closeTab(taskId, panelId, tab.id);
6667
}
6768
: undefined,
69+
badge:
70+
tab.id === "changes" ? (
71+
<ChangesTabBadge taskId={taskId} task={task} />
72+
) : undefined,
6873
})),
6974
[tabs, panelId, taskId, task, closeTab],
7075
);

apps/array/src/renderer/features/panels/store/panelTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type Tab = {
1212
onSelect?: () => void;
1313
icon?: React.ReactNode;
1414
hasUnsavedChanges?: boolean;
15+
badge?: React.ReactNode;
1516
};
1617

1718
export type PanelContent = {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useTaskData } from "@features/task-detail/hooks/useTaskData";
2+
import { Text } from "@radix-ui/themes";
3+
import type { Task } from "@shared/types";
4+
import { useQuery } from "@tanstack/react-query";
5+
6+
interface ChangesTabBadgeProps {
7+
taskId: string;
8+
task: Task;
9+
}
10+
11+
export function ChangesTabBadge({ taskId, task }: ChangesTabBadgeProps) {
12+
const taskData = useTaskData({ taskId, initialTask: task });
13+
const repoPath = taskData.repoPath;
14+
15+
const { data: changedFiles = [] } = useQuery({
16+
queryKey: ["changed-files-head", repoPath],
17+
queryFn: () => window.electronAPI.getChangedFilesHead(repoPath as string),
18+
enabled: !!repoPath,
19+
staleTime: Infinity,
20+
});
21+
22+
if (changedFiles.length === 0) {
23+
return null;
24+
}
25+
26+
return (
27+
<Text size="1" color="orange" >
28+
({changedFiles.length})
29+
</Text>
30+
);
31+
}

0 commit comments

Comments
 (0)