238 lines
5.1 KiB
JavaScript
238 lines
5.1 KiB
JavaScript
// src/components/ScreenSecond/index.tsx
|
||
import styled from "styled-components";
|
||
import fallbackLogo from "imgs/logo.svg";
|
||
import arrowTop from "imgs/arrow_top.svg";
|
||
import {
|
||
s_f_Text18_400,
|
||
s_Text18_400 as Text18_400,
|
||
s_Text24_700 as Text24_700,
|
||
s_Text40_700 as Text40_700,
|
||
s_Text14_400 as Text14_400,
|
||
} from "../Typography";
|
||
import { useSingleton } from "cms/factory";
|
||
import { AsyncReveal } from "../AsyncReveal";
|
||
|
||
/* ===================== TYPES & GQL ===================== */
|
||
|
||
type Row = {
|
||
id: string;
|
||
value: string;
|
||
label: string;
|
||
trend: "none" | "up" | "down";
|
||
order: number;
|
||
};
|
||
|
||
type Section = {
|
||
id: string;
|
||
title?: string | null;
|
||
description?: string | null;
|
||
logo?: { url?: string | null } | null;
|
||
footnote?: string | null;
|
||
rows: Row[];
|
||
};
|
||
|
||
const SELECTION = `
|
||
id
|
||
title
|
||
description
|
||
logo { url }
|
||
footnote
|
||
rows(orderBy: { order: asc }) {
|
||
id
|
||
value
|
||
label
|
||
trend
|
||
order
|
||
}
|
||
`;
|
||
|
||
/* ===================== UTILS ===================== */
|
||
|
||
function chunkInto<T>(arr: T[], size: number): T[][] {
|
||
const out: T[][] = [];
|
||
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
|
||
return out;
|
||
}
|
||
|
||
/* ===================== STYLES ===================== */
|
||
|
||
const ScreenSecondBlock = styled.div`
|
||
width: 100%;
|
||
max-width: 1380px;
|
||
padding: 64px 10px;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
justify-content: center;
|
||
@media (max-width: 768px) {
|
||
padding: 10px 10px;
|
||
}
|
||
`;
|
||
|
||
const ContentCard = styled.div`
|
||
width: 90%;
|
||
max-width: 1200px;
|
||
background: #fff;
|
||
border-radius: 16px;
|
||
padding: 64px 0;
|
||
padding-bottom: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
@media (max-width: 768px) {
|
||
width: 100%;
|
||
padding: 10px 10px;
|
||
}
|
||
`;
|
||
|
||
const HeaderRow = styled.div`
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
|
||
@media (max-width: 768px) {
|
||
flex-direction: column-reverse;
|
||
align-items: flex-start;
|
||
gap: 0;
|
||
}
|
||
`;
|
||
|
||
const TitleRow = styled.div`
|
||
display: inline;
|
||
font-size: 0; /* убираем промежутки у inline-children */
|
||
`;
|
||
|
||
const TitleBlock = styled.div`
|
||
max-width: 720px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
`;
|
||
|
||
const LogoBlock = styled.div`
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
margin-left: 30px;
|
||
|
||
img {
|
||
min-width: 185px;
|
||
height: 56px;
|
||
min-height: 56px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
width: 100%;
|
||
justify-content: center;
|
||
margin-left: 0;
|
||
margin-bottom: 32px;
|
||
}
|
||
`;
|
||
|
||
const FinanceRow = styled.div`
|
||
display: flex;
|
||
justify-content: space-between;
|
||
text-align: center;
|
||
margin: 24px 0;
|
||
|
||
@media (max-width: 768px) {
|
||
flex-direction: row;
|
||
gap: 16px;
|
||
}
|
||
`;
|
||
|
||
const FinanceBlock = styled.div`
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
@media (max-width: 768px) {
|
||
width: 100%;
|
||
}
|
||
`;
|
||
|
||
const ValueRow = styled.div`
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding-bottom: 8px;
|
||
`;
|
||
|
||
const Divider = styled.div`
|
||
width: 100%;
|
||
height: 1px;
|
||
background: #e5e7eb;
|
||
margin: 24px 0;
|
||
`;
|
||
|
||
|
||
const Arrow = styled.img`
|
||
transform: ${({ $down }) => ($down ? "rotate(180deg)" : "none")};
|
||
`;
|
||
|
||
|
||
/* ===================== COMPONENT ===================== */
|
||
|
||
function ScreenSecond() {
|
||
const { data: section, loading, error } = useSingleton(
|
||
"ScreenSecondSection",
|
||
SELECTION
|
||
);
|
||
|
||
const groups = chunkInto(section?.rows ?? [], 3);
|
||
const logoSrc = section?.logo?.url || fallbackLogo;
|
||
|
||
return (
|
||
<ScreenSecondBlock id="holding">
|
||
<ContentCard>
|
||
<AsyncReveal loading={loading} error={error} stagger={0.08} from={12}>
|
||
{/* Header */}
|
||
<HeaderRow>
|
||
<TitleBlock>
|
||
<TitleRow style={{ marginBottom: 24 }}>
|
||
{section?.title && <Text40_700>{section.title}</Text40_700>}
|
||
</TitleRow>
|
||
{section?.description && (
|
||
<s_f_Text18_400>{section.description}</s_f_Text18_400>
|
||
)}
|
||
</TitleBlock>
|
||
<LogoBlock>
|
||
<img src={logoSrc} alt="Company logo" />
|
||
</LogoBlock>
|
||
</HeaderRow>
|
||
|
||
{/* Rows in groups of 3 with Divider */}
|
||
{groups.map((group, gi) => (
|
||
<div key={`g-${gi}`}>
|
||
<FinanceRow>
|
||
{group.map((item) => (
|
||
<FinanceBlock key={item.id}>
|
||
<ValueRow>
|
||
<Text24_700>{item.value}</Text24_700>
|
||
{item.trend !== "none" && (
|
||
<Arrow
|
||
src={arrowTop}
|
||
alt={item.trend}
|
||
height={16}
|
||
$down={item.trend === "down"}
|
||
/>
|
||
)}
|
||
</ValueRow>
|
||
<Text18_400>{item.label}</Text18_400>
|
||
</FinanceBlock>
|
||
))}
|
||
</FinanceRow>
|
||
{gi < groups.length - 1 && <Divider />}
|
||
</div>
|
||
))}
|
||
|
||
{/* Footnote */}
|
||
{section?.footnote && <Text14_400>{section.footnote}</Text14_400>}
|
||
</AsyncReveal>
|
||
</ContentCard>
|
||
</ScreenSecondBlock>
|
||
);
|
||
}
|
||
|
||
export default ScreenSecond;
|