feat: 컴포넌트 추가, 스타일 수정
This commit is contained in:
70
web-app/app/shared/components/descriptions/Descriptions.tsx
Normal file
70
web-app/app/shared/components/descriptions/Descriptions.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { DescriptionsItem, DescriptionsItemProps } from './items/DescriptionsItem';
|
||||
|
||||
export type DescriotionItemType = {
|
||||
items: (DescriptionsItemProps & { key?: string })[];
|
||||
column?: 1 | 2 | 3 | 4;
|
||||
titleWidth?: number;
|
||||
className?: string;
|
||||
titleClassName?: string;
|
||||
contentClassName?: string;
|
||||
};
|
||||
|
||||
export const Descriptions = (props: DescriotionItemType) => {
|
||||
const { items, column = 1, titleWidth, className, titleClassName, contentClassName } = props;
|
||||
|
||||
const columnClassName =
|
||||
{
|
||||
1: 'grid-cols-1',
|
||||
2: 'grid-cols-2',
|
||||
3: 'grid-cols-3',
|
||||
4: 'grid-cols-4',
|
||||
}[column] || 'grid-cols-1';
|
||||
|
||||
const getFirstRowItemIndices = () => {
|
||||
const firstRowItemIndices: number[] = [];
|
||||
let availableColumns = column;
|
||||
|
||||
for (let index = 0; index < items.length; index += 1) {
|
||||
if (availableColumns <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const item = items[index];
|
||||
const itemSpan = item.span === 'filled' ? column : (item.span ?? 1);
|
||||
const effectiveSpan = Math.min(typeof itemSpan === 'number' ? itemSpan : column, column);
|
||||
|
||||
if (effectiveSpan > availableColumns) {
|
||||
break;
|
||||
}
|
||||
|
||||
firstRowItemIndices.push(index);
|
||||
availableColumns -= effectiveSpan;
|
||||
|
||||
if (availableColumns === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return firstRowItemIndices;
|
||||
};
|
||||
|
||||
const firstRowItemIndices = getFirstRowItemIndices();
|
||||
|
||||
return (
|
||||
<div className={`grid w-full gap-0 ${columnClassName || ''} ${className || ''}`}>
|
||||
{items.map((item, index) => {
|
||||
const { key, ...itemProps } = item;
|
||||
return (
|
||||
<DescriptionsItem
|
||||
key={key || index}
|
||||
{...itemProps}
|
||||
isFirstRow={firstRowItemIndices.includes(index)}
|
||||
titleWidth={titleWidth}
|
||||
titleClassName={titleClassName}
|
||||
contentClassName={contentClassName}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import clsx from 'clsx';
|
||||
|
||||
export type DescriptionsItemProps = {
|
||||
title: React.ReactNode;
|
||||
content: React.ReactNode;
|
||||
span?: number | 'filled';
|
||||
isFirstRow?: boolean;
|
||||
titleWidth?: number;
|
||||
titleClassName?: string;
|
||||
contentClassName?: string;
|
||||
};
|
||||
|
||||
export const DescriptionsItem = (props: DescriptionsItemProps) => {
|
||||
const { title, content, span, isFirstRow = false, titleWidth, titleClassName, contentClassName } = props;
|
||||
|
||||
const gridColumnStyle =
|
||||
span === 'filled' ? { gridColumn: '1 / -1' } : span && span > 0 ? { gridColumn: `span ${span}` } : {};
|
||||
|
||||
return (
|
||||
<div className="flex" style={gridColumnStyle}>
|
||||
<div
|
||||
className={`flex w-25 min-h-10 items-center px-4 bg-primary-tertiary01 text-primary-secondary text-sm font-semibold border-b border-white ${titleClassName || ''}`}
|
||||
style={{ width: titleWidth }}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'relative flex flex-1 items-center pl-4 pr-3 py-2 text-sm font-medium text-kc-black-22 overflow-x-auto after:content-[\'\'] after:absolute after:left-0 after:right-0 after:bottom-0 after:h-px after:bg-primary-tertiary01',
|
||||
contentClassName,
|
||||
isFirstRow &&
|
||||
'before:content-[\'\'] before:absolute before:left-0 before:right-0 before:top-0 before:h-px before:bg-primary-tertiary01'
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user