Skip to content

Commit 0660c87

Browse files
committed
refactor: Group releases under same application
WIP Signed-off-by: Jasmina <jasmina.piric@secomind.com>
1 parent 1c47da0 commit 0660c87

13 files changed

Lines changed: 375 additions & 347 deletions

frontend/src/components/CollapseItem.tsx

Lines changed: 98 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* This file is part of Edgehog.
33
*
4-
* Copyright 2025 SECO Mind Srl
4+
* Copyright 2025 - 2026 SECO Mind Srl
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -18,14 +18,11 @@
1818
* SPDX-License-Identifier: Apache-2.0
1919
*/
2020

21-
import { useState } from "react";
21+
import { useCallback, useRef, useState } from "react";
2222
import { useIntl } from "react-intl";
23-
import { Card, Button, Collapse } from "react-bootstrap";
23+
import { Button, Collapse } from "react-bootstrap";
2424

2525
import Icon from "@/components/Icon";
26-
import ContainerStatus, {
27-
parseContainerState,
28-
} from "@/components/ContainerStatus";
2926

3027
export function useCollapseToggle(defaultOpen = false) {
3128
const [open, setOpen] = useState(defaultOpen);
@@ -50,32 +47,26 @@ export function useCollapsibleSections<T extends string | number>(
5047
return { openSections, toggleSection, isSectionOpen, setOpenSections };
5148
}
5249

53-
interface CollapseCaretProps {
54-
open: boolean;
55-
}
56-
57-
const CollapseCaret = ({ open }: CollapseCaretProps) => {
58-
return (
59-
<span
60-
style={{
61-
display: "inline-flex",
62-
transition: "transform 0.2s ease-in-out",
63-
transform: open ? "rotate(0deg)" : "rotate(-180deg)",
64-
}}
65-
>
66-
<Icon icon="caretDown" />
67-
</span>
68-
);
69-
};
50+
const CollapseCaret = ({ open }: { open: boolean }) => (
51+
<span
52+
style={{
53+
display: "inline-flex",
54+
transition: "transform 0.2s ease-in-out",
55+
transform: open ? "rotate(0deg)" : "rotate(-180deg)",
56+
}}
57+
>
58+
<Icon icon="caretDown" />
59+
</span>
60+
);
7061

71-
interface CollapseHeaderButtonProps {
62+
type CollapseHeaderButtonProps = {
7263
open: boolean;
7364
onToggle: () => void;
7465
children: React.ReactNode;
7566
className?: string;
7667
style?: React.CSSProperties;
7768
title?: string;
78-
}
69+
};
7970

8071
const CollapseHeaderButton = ({
8172
open,
@@ -96,98 +87,107 @@ const CollapseHeaderButton = ({
9687
{children}
9788
</Button>
9889
);
99-
100-
type CollapseType = "flat" | "card-parent" | "card-child";
101-
102-
interface CollapseItemProps {
90+
type CollapseItemProps = {
10391
title: React.ReactNode;
10492
children: React.ReactNode;
10593
open: boolean;
10694
onToggle: () => void;
107-
containerStatus?: string | null;
108-
isInsideTable?: boolean;
109-
type?: CollapseType;
110-
}
95+
rightContent?: React.ReactNode;
96+
97+
// layout props
98+
bordered?: boolean;
99+
borderBottom?: boolean;
100+
transparent?: boolean;
101+
boldTitle?: boolean;
102+
childFontSize?: boolean;
103+
showToggleTooltip?: boolean;
104+
caretPosition?: "left" | "right" | "end";
105+
};
111106

112107
const CollapseItem = ({
113108
title,
114109
children,
115110
open,
116111
onToggle,
117-
containerStatus,
118-
isInsideTable = false,
119-
type = "card-child",
112+
rightContent,
113+
bordered = true,
114+
borderBottom,
115+
transparent = false,
116+
boldTitle = false,
117+
childFontSize = false,
118+
showToggleTooltip = false,
119+
caretPosition,
120120
}: CollapseItemProps) => {
121121
const intl = useIntl();
122-
123-
const isFlat = type === "flat";
124-
const isParent = type === "card-parent";
125-
126-
if (isFlat) {
127-
const collapseTitle = isInsideTable
128-
? open
129-
? intl.formatMessage({
130-
id: "components.CollapseItem.collapseList",
131-
defaultMessage: "Collapse list",
132-
})
133-
: intl.formatMessage({
134-
id: "components.CollapseItem.expandList",
135-
defaultMessage: "Expand list",
136-
})
137-
: undefined;
138-
139-
return (
140-
<div
141-
className={
142-
!isInsideTable ? `mb-2 border-bottom ${open ? "pb-4" : "pb-1"}` : ""
143-
}
144-
>
145-
<CollapseHeaderButton
146-
open={open}
147-
onToggle={onToggle}
148-
title={collapseTitle}
149-
className={`d-flex align-items-center ps-0 pe-1 ${!isInsideTable ? "fw-bold" : ""}`}
150-
style={{ backgroundColor: "transparent", border: "none" }}
151-
>
152-
<span className="d-flex align-items-center gap-2">
153-
{title}
154-
<CollapseCaret open={open} />
155-
</span>
156-
</CollapseHeaderButton>
157-
158-
<Collapse in={open}>
159-
<div className={isInsideTable ? "" : "pt-3"}>{children}</div>
160-
</Collapse>
161-
</div>
162-
);
163-
}
122+
const containerRef = useRef<HTMLDivElement | null>(null);
123+
124+
const handleScrollIntoView = useCallback(() => {
125+
if (!containerRef.current) return;
126+
127+
const rect = containerRef.current.getBoundingClientRect();
128+
if (rect.bottom > window.innerHeight) {
129+
containerRef.current.scrollIntoView({
130+
behavior: "smooth",
131+
block: "nearest",
132+
});
133+
}
134+
}, []);
135+
136+
const collapseTitle = showToggleTooltip
137+
? open
138+
? intl.formatMessage({
139+
id: "components.CollapseItem.collapseList",
140+
defaultMessage: "Collapse list",
141+
})
142+
: intl.formatMessage({
143+
id: "components.CollapseItem.expandList",
144+
defaultMessage: "Expand list",
145+
})
146+
: undefined;
147+
148+
const renderCaret = () => <CollapseCaret open={open} />;
164149

165150
return (
166-
<Card className={`shadow-sm ${isParent ? "mb-3" : "mb-2"}`}>
167-
<Card.Header className="p-0">
168-
<CollapseHeaderButton
169-
open={open}
170-
onToggle={onToggle}
171-
className={`w-100 d-flex align-items-center ${isParent ? "fw-bold p-2" : "fw-semibold p-1"}`}
172-
style={{ fontSize: isParent ? "1rem" : "0.9rem" }}
173-
>
151+
<div
152+
ref={containerRef}
153+
className={`
154+
${bordered ? "border rounded" : ""}
155+
${borderBottom ? `mb-2 border-bottom ${open ? "pb-4" : "pb-1"}` : ""}
156+
`}
157+
>
158+
<CollapseHeaderButton
159+
open={open}
160+
onToggle={onToggle}
161+
title={collapseTitle}
162+
className={`d-flex align-items-center w-100 ${
163+
bordered ? "border-0" : "ps-0 pe-1"
164+
} ${boldTitle ? "fw-bold" : ""}`}
165+
style={{
166+
backgroundColor: transparent ? "transparent" : undefined,
167+
border: transparent ? "none" : undefined,
168+
fontSize: childFontSize ? "0.9rem" : undefined,
169+
}}
170+
>
171+
<div className="d-flex align-items-center gap-2">
172+
{caretPosition === "left" && renderCaret()}
174173
<span>{title}</span>
174+
{caretPosition === "right" && renderCaret()}
175+
</div>
176+
177+
{(rightContent || caretPosition === "end") && (
178+
<div className="ms-auto d-flex align-items-center gap-2">
179+
{caretPosition === "end" && renderCaret()}
180+
{rightContent}
181+
</div>
182+
)}
183+
</CollapseHeaderButton>
175184

176-
<span className="ms-auto d-inline-flex gap-2 align-items-center">
177-
{containerStatus && (
178-
<ContainerStatus state={parseContainerState(containerStatus)} />
179-
)}
180-
<CollapseCaret open={open} />
181-
</span>
182-
</CollapseHeaderButton>
183-
</Card.Header>
184-
185-
<Collapse in={open}>
186-
<div className={`border-top ${isParent ? "p-3" : "p-2"}`}>
185+
<Collapse in={open} onEntered={handleScrollIntoView}>
186+
<div className={`${bordered ? "border-top px-3 py-2" : ""}`}>
187187
{children}
188188
</div>
189189
</Collapse>
190-
</Card>
190+
</div>
191191
);
192192
};
193193

0 commit comments

Comments
 (0)