Skip to content

Commit 37f1db9

Browse files
committed
feat: enhance Gantt and Progress components with improved rendering and styling
1 parent b662342 commit 37f1db9

5 files changed

Lines changed: 120 additions & 101 deletions

File tree

src/components/gantt/Gantt.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ export const Gantt: React.FC<GanttProps> = (props) => {
6262
id: `group-source-${index}`,
6363
type: "group",
6464
data: {
65+
items: group.items,
66+
step: step,
67+
firstGroupStep: groups[0].step,
68+
groupStep: group.step,
6569
displayMessage: `${index}`,
6670
color: hashToColor(`group-source-${index}`),
6771
}
@@ -70,24 +74,18 @@ export const Gantt: React.FC<GanttProps> = (props) => {
7074

7175
})
7276

77+
const minStart = Math.min(...groups[0].items.map(item => item.start))
78+
7379
return (
7480
<ScrollArea w={"100%"} h={"100%"} type={"hover"}>
7581
<ScrollAreaViewport>
76-
{
77-
groups.sort((a, b) => b.step - a.step).map((group, index) => {
78-
79-
const minStart = Math.min(...group.items.map(item => item.start))
80-
81-
return <GanttGroup children={children}
82-
id={`group-target-${index}`}
83-
hideScaling={index != 0}
84-
start={minStart - (((minStart / (groups[0].step * step)) * (group.step * step)))}
85-
step={group.step * step}
86-
stepWidth={stepWidth} rowHeight={rowHeight} items={group.items}
87-
key={`group-target-${index}`}/>
88-
89-
})
90-
}
82+
<GanttGroup children={children}
83+
id={`group-target`}
84+
hideScaling={false}
85+
start={minStart - (((minStart / (groups[0].step * step)) * (groups[0].step * step)))}
86+
step={groups[0].step * step}
87+
stepWidth={stepWidth} rowHeight={rowHeight} items={groups[0].items}
88+
key={`group-target`}/>
9189
</ScrollAreaViewport>
9290
<ScrollAreaScrollbar orientation={"horizontal"}>
9391
<ScrollAreaThumb/>

src/components/gantt/GanttGroup.tsx

Lines changed: 77 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, {CSSProperties} from "react"
22
import {hashToColor, withAlpha} from "../../utils"
33
import {GanttProps} from "./Gantt"
4-
import {GanttFooter} from "./GanttFooter"
54
import {GanttItem} from "./GanttItem"
65
import {GanttHeader} from "./GanttHeader"
76

@@ -34,6 +33,7 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
3433

3534
const viewportRef = React.useRef<HTMLDivElement>(null)
3635
const [viewportWidth, setViewportWidth] = React.useState(0)
36+
const [activeGroup, setActiveGroup] = React.useState<string | undefined>(undefined)
3737

3838
// Parse stepWidth to pixels
3939
const stepWidthPx = parseInt(stepWidth as string)
@@ -62,9 +62,13 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
6262
}
6363

6464
handleResize()
65+
viewportRef?.current?.addEventListener("resize", handleResize)
6566
window.addEventListener("resize", handleResize)
66-
return () => window.removeEventListener("resize", handleResize)
67-
}, [])
67+
return () => {
68+
window.removeEventListener("resize", handleResize)
69+
viewportRef.current?.removeEventListener("resize", handleResize)
70+
}
71+
})
6872

6973
// Calculate row assignments (non-overlapping rows)
7074
const itemRows = items?.length ? items
@@ -81,8 +85,8 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
8185
const containerStyles: CSSProperties = {
8286
display: "grid",
8387
gridTemplateColumns: `repeat(${columnsToRender}, ${stepWidth})`,
84-
width: "100%",
85-
minHeight: "100%"
88+
minWidth: "100%",
89+
gridColumn: "1 / -1",
8690
}
8791

8892
return (
@@ -94,29 +98,30 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
9498
avgDuration={avgDuration}
9599
stepWidth={stepWidth}/>}
96100
{itemRows.map((row, rowIndex) => (
97-
<div key={`row-${rowIndex}`} style={{
98-
gridColumn: "1 / -1",
99-
height: rowHeight,
100-
position: "relative",
101-
backgroundColor: "transparent"
102-
}}>
103-
{Array.from({length: columnsToRender - 1}).map((_, columnIndex) => (
104-
<div key={`grid-${columnIndex}`} style={{
105-
position: "absolute",
106-
left: (columnIndex + 1) * stepWidthPx,
107-
top: 0,
108-
bottom: 0,
109-
width: "0px",
110-
borderLeft: `1px dashed rgba(255, 255, 255, ${hideScaling ? 0.05 : 0.1})`
111-
}}/>
112-
))}
113-
114-
{hideScaling && (
115-
<>
116-
<div
117-
className="gantt__group-wrapper"
118-
style={{
119-
backgroundImage: `
101+
<>
102+
<div key={`row-${rowIndex}`} style={{
103+
gridColumn: "1 / -1",
104+
minHeight: rowHeight,
105+
position: "relative",
106+
backgroundColor: "transparent"
107+
}}>
108+
{Array.from({length: columnsToRender - 1}).map((_, columnIndex) => (
109+
<div key={`grid-${columnIndex}`} style={{
110+
position: "absolute",
111+
left: (columnIndex + 1) * stepWidthPx,
112+
top: 0,
113+
bottom: 0,
114+
width: "0px",
115+
borderLeft: `1px dashed rgba(255, 255, 255, ${hideScaling ? 0.05 : 0.1})`
116+
}}/>
117+
))}
118+
119+
{hideScaling && (
120+
<>
121+
<div
122+
className="gantt__group-wrapper"
123+
style={{
124+
backgroundImage: `
120125
linear-gradient(to right, transparent, #070514),
121126
repeating-linear-gradient(
122127
45deg,
@@ -125,14 +130,14 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
125130
${withAlpha(hashToColor(props.id!.replace("target", "source")), 0.5)} 4px
126131
)
127132
`,
128-
left: `${getItemPosition(itemMinStart, itemMinStart + step, start, end, timeRange, totalTimelineWidth).left}px`,
129-
width: `${getItemPosition(itemMinStart, itemMinStart + step, start, end, timeRange, totalTimelineWidth).width}px`,
130-
}}
131-
/>
132-
<div
133-
className="gantt__group-wrapper"
134-
style={{
135-
backgroundImage: `
133+
left: `${getItemPosition(itemMinStart, itemMinStart + step, start, end, timeRange, totalTimelineWidth).left}px`,
134+
width: `${getItemPosition(itemMinStart, itemMinStart + step, start, end, timeRange, totalTimelineWidth).width}px`,
135+
}}
136+
/>
137+
<div
138+
className="gantt__group-wrapper"
139+
style={{
140+
backgroundImage: `
136141
linear-gradient(to left, transparent, #070514),
137142
repeating-linear-gradient(
138143
45deg,
@@ -141,31 +146,45 @@ export const GanttGroup: React.FC<GanttGroupProps> = (props) => {
141146
${withAlpha(hashToColor(props.id!.replace("target", "source")), 0.5)} 4px
142147
)
143148
`,
144-
left: `${getItemPosition(itemMaxEnd - step, itemMaxEnd, start, end, timeRange, totalTimelineWidth).left}px`,
145-
width: `${getItemPosition(itemMaxEnd - step, itemMaxEnd, start, end, timeRange, totalTimelineWidth).width}px`,
146-
}}
147-
/>
148-
</>
149-
)}
150-
149+
left: `${getItemPosition(itemMaxEnd - step, itemMaxEnd, start, end, timeRange, totalTimelineWidth).left}px`,
150+
width: `${getItemPosition(itemMaxEnd - step, itemMaxEnd, start, end, timeRange, totalTimelineWidth).width}px`,
151+
}}
152+
/>
153+
</>
154+
)}
155+
156+
{row.map((item, itemIndex) => {
157+
const itemPosition = getItemPosition(item.start, item.end, start, end, timeRange, totalTimelineWidth)
158+
const hasVisibleWidth = itemPosition.width > 0
159+
160+
return hasVisibleWidth && (
161+
<>
162+
<GanttItem
163+
key={item.id}
164+
id={item.id}
165+
w={`${itemPosition.width}px`}
166+
left={`${itemPosition.left}px`}
167+
onClick={() => {
168+
if (item.type != "group") return
169+
setActiveGroup(prevState => item.id === prevState ? undefined : item.id)
170+
}}
171+
>
172+
{children?.(item, itemIndex)}
173+
</GanttItem>
174+
</>
175+
)
176+
})}
177+
</div>
151178
{row.map((item, itemIndex) => {
152-
const itemPosition = getItemPosition(item.start, item.end, start, end, timeRange, totalTimelineWidth)
153-
const hasVisibleWidth = itemPosition.width > 0
154-
155-
return hasVisibleWidth && (
156-
<GanttItem
157-
key={item.id}
158-
id={item.id}
159-
w={`${itemPosition.width}px`}
160-
left={`${itemPosition.left}px`}
161-
>
162-
{children?.(item, itemIndex)}
163-
</GanttItem>
164-
)
179+
return item.type === "group" && activeGroup === item.id && <GanttGroup children={children}
180+
id={`group-target-${itemIndex}`}
181+
start={(Math.min(...item.data.items.map((item: any) => item.start))) - ((((Math.min(...item.data.items.map((item: any) => item.start))) / (item.data.firstGroupStep * item.data.step)) * (item.data.groupStep * item.data.step)))}
182+
step={item.data.groupStep * item.data.step}
183+
stepWidth={stepWidth} rowHeight={rowHeight} items={item.data.items}
184+
key={`group-target-${itemIndex}`}/>
165185
})}
166-
</div>
186+
</>
167187
))}
168-
{!hideScaling && <GanttFooter/>}
169188

170189
</div>
171190
)

src/components/progress/Progress.stories.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@ import React from "react";
44
import {Card} from "../card/Card";
55
import {Spacing} from "../spacing/Spacing";
66
import {Text} from "../text/Text";
7+
import {IconAi, IconPlane, IconPlaneFilled} from "@tabler/icons-react";
78

89
export default {
910
title: "Progress",
1011
component: Progress,
1112
} as Meta
1213

1314
export const ProgressExample = () => {
14-
return <Card color={"secondary"} w={"300px"}>
15+
return <Card color={"primary"} w={"300px"}>
1516
<Progress value={50} predictionValue={75} max={100} color={"linear-gradient(to right, #29BF12 0%, #D90429 100%)"}/>
1617
<Spacing spacing={"xs"}/>
1718
<Text>
1819
You used 50% of your available workflow executions and will used 75% until its reseted.
1920
</Text>
2021
<Spacing spacing={"xl"}/>
21-
<Progress value={30} predictionValue={120} max={100} color={"linear-gradient(to right, #29BF12 0%, #D90429 100%)"}/>
22+
<Progress value={30} predictionValue={70} max={100} color={"#70ffb2"}/>
2223
<Spacing spacing={"xs"}/>
2324
<Text>
2425
You used 30% of your available workflow executions and will used 120% until its reseted.

src/components/progress/Progress.style.scss

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,43 @@
66
.progress {
77

88
& {
9-
@include box.box(variables.$tertiary);
9+
@include box.box(variables.$primary);
10+
background: transparent;
1011
@include helpers.borderRadius();
1112
width: 100%;
1213
height: 10px;
1314
position: relative;
1415
transform: translateZ(0);
1516
}
1617

18+
&:before {
19+
@include box.box(variables.$tertiary);
20+
background: var(--color);
21+
opacity: 0.1;
22+
content: "";
23+
position: absolute;
24+
top: 0;
25+
left: 0;
26+
width: 100%;
27+
height: 100%;
28+
border-radius: inherit;
29+
}
30+
1731
&__dot {
18-
@include box.box(variables.$white);
19-
background: helpers.backgroundColor(variables.$white, 2);
2032
position: absolute;
21-
max-width: 100%;
22-
aspect-ratio: 1/1;
23-
height: 200%;
2433
z-index: 2;
34+
display: flex;
35+
align-items: center;
36+
justify-content: center;
2537
top: 50%;
2638
transform: translateX(50%) translateY(-50%);
27-
border-radius: 50rem;
2839
right: calc(100% - (1% * (var(--progress, 0))));
29-
overflow: hidden;
30-
31-
&:after {
32-
content: "";
33-
position: absolute;
34-
top: 50%;
35-
left: 50%;
36-
border-radius: inherit;
37-
width: 60%;
38-
height: 60%;
39-
transform: translate(-50%, -50%);
40-
background: var(--color);
41-
background-repeat: no-repeat;
42-
}
4340
}
4441

4542
&__indicator {
4643
position: absolute;
4744
background: var(--color);
45+
border-radius: inherit;
4846
background-position: left center;
4947
background-repeat: no-repeat;
5048
background-size: calc(100% * (100 / var(--progress, 100))) 100%;
@@ -55,13 +53,13 @@
5553
&--prediction {
5654
background: repeating-linear-gradient(
5755
-45deg,
58-
rgba(0,0,0,.5),
59-
rgba(0,0,0,.5) 2.5px,
56+
rgba(0,0,0,.25),
57+
rgba(0,0,0,.25) 2.5px,
6058
transparent 2.5px,
6159
transparent 5px
6260
), var(--color);
6361
z-index: 0;
64-
opacity: 0.5;
62+
opacity: 0.25;
6563
background-position: left center;
6664
background-repeat: no-repeat;
6765
background-size: calc(100% * (100 / var(--progressPrediction, 100))) 100%;

src/components/progress/Progress.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ import "./Progress.style.scss"
55

66
export type ProgressProps = ComponentProps & Radix.ProgressProps & {
77
color?: CSSProperties['background']
8+
dot?: React.ReactNode
89
predictionValue?: number | null
910
}
1011

1112
export const Progress: React.FC<ProgressProps> = (props) => {
1213

13-
const {color = "white", predictionValue, ...rest} = props
14+
const {color = "white", predictionValue, dot, ...rest} = props
1415

1516
const progress = ((Math.min(props.value ?? 0, props.max ?? 100)) / (props.max ?? 100)) * 100;
1617
const transformValue = `translateX(-${100 - progress}%)`;
@@ -28,7 +29,9 @@ export const Progress: React.FC<ProgressProps> = (props) => {
2829
}
2930
})}>
3031

31-
<div className={"progress__dot"}/>
32+
<div className={"progress__dot"}>
33+
{dot}
34+
</div>
3235

3336
<div style={{
3437
width: "100%",

0 commit comments

Comments
 (0)