컬럼 고정기능 구현
This commit is contained in:
parent
f5caa7127c
commit
964b6415f8
|
|
@ -58,25 +58,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
component,
|
||||
isDesignMode = false,
|
||||
isSelected = false,
|
||||
isInteractive = false,
|
||||
onClick,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
config,
|
||||
className,
|
||||
style,
|
||||
formData,
|
||||
onFormDataChange,
|
||||
screenId,
|
||||
size,
|
||||
position,
|
||||
componentConfig,
|
||||
selectedScreen,
|
||||
onZoneComponentDrop,
|
||||
onZoneClick,
|
||||
tableName,
|
||||
onRefresh,
|
||||
onClose,
|
||||
}) => {
|
||||
// 컴포넌트 설정
|
||||
const tableConfig = {
|
||||
|
|
@ -86,7 +75,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
} as TableListConfig;
|
||||
|
||||
// 상태 관리
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [data, setData] = useState<Record<string, any>[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
|
@ -150,7 +139,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
try {
|
||||
const response = await tableTypeApi.getColumns(tableConfig.selectedTable);
|
||||
// API 응답 구조 확인 및 컬럼 배열 추출
|
||||
const columns = Array.isArray(response) ? response : response.columns || [];
|
||||
const columns = Array.isArray(response) ? response : (response as any).columns || [];
|
||||
const labels: Record<string, string> = {};
|
||||
const meta: Record<string, { webType?: string; codeCategory?: string }> = {};
|
||||
|
||||
|
|
@ -463,6 +452,72 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
return displayColumns.filter((col) => col.visible).sort((a, b) => a.order - b.order);
|
||||
}, [displayColumns, tableConfig.columns]);
|
||||
|
||||
// 컬럼을 고정 위치별로 분류
|
||||
const columnsByPosition = useMemo(() => {
|
||||
const leftFixed: ColumnConfig[] = [];
|
||||
const rightFixed: ColumnConfig[] = [];
|
||||
const normal: ColumnConfig[] = [];
|
||||
|
||||
visibleColumns.forEach((col) => {
|
||||
if (col.fixed === "left") {
|
||||
leftFixed.push(col);
|
||||
} else if (col.fixed === "right") {
|
||||
rightFixed.push(col);
|
||||
} else {
|
||||
normal.push(col);
|
||||
}
|
||||
});
|
||||
|
||||
// 고정 컬럼들은 fixedOrder로 정렬
|
||||
leftFixed.sort((a, b) => (a.fixedOrder || 0) - (b.fixedOrder || 0));
|
||||
rightFixed.sort((a, b) => (a.fixedOrder || 0) - (b.fixedOrder || 0));
|
||||
|
||||
return { leftFixed, rightFixed, normal };
|
||||
}, [visibleColumns]);
|
||||
|
||||
// 가로 스크롤이 필요한지 계산
|
||||
const needsHorizontalScroll = useMemo(() => {
|
||||
if (!tableConfig.horizontalScroll?.enabled) {
|
||||
console.log("🚫 가로 스크롤 비활성화됨");
|
||||
return false;
|
||||
}
|
||||
|
||||
const maxVisible = tableConfig.horizontalScroll.maxVisibleColumns || 8;
|
||||
const totalColumns = visibleColumns.length;
|
||||
const result = totalColumns > maxVisible;
|
||||
|
||||
console.log(
|
||||
`🔍 가로 스크롤 계산: ${totalColumns}개 컬럼 > ${maxVisible}개 최대 = ${result ? "스크롤 필요" : "스크롤 불필요"}`,
|
||||
);
|
||||
console.log("📊 가로 스크롤 설정:", tableConfig.horizontalScroll);
|
||||
console.log(
|
||||
"📋 현재 컬럼들:",
|
||||
visibleColumns.map((c) => c.columnName),
|
||||
);
|
||||
|
||||
return result;
|
||||
}, [visibleColumns.length, tableConfig.horizontalScroll]);
|
||||
|
||||
// 컬럼 너비 계산 - 내용 길이에 맞게 자동 조정
|
||||
const getColumnWidth = (column: ColumnConfig) => {
|
||||
if (column.width) return column.width;
|
||||
|
||||
// 컬럼 헤더 텍스트 길이 기반으로 계산
|
||||
const headerText = columnLabels[column.columnName] || column.displayName || column.columnName;
|
||||
const headerLength = headerText.length;
|
||||
|
||||
// 데이터 셀의 최대 길이 추정 (실제 데이터가 있다면 더 정확하게 계산 가능)
|
||||
const estimatedContentLength = Math.max(headerLength, 10); // 최소 10자
|
||||
|
||||
// 문자당 약 8px 정도로 계산하고, 패딩 및 여백 고려
|
||||
const calculatedWidth = estimatedContentLength * 8 + 40; // 40px는 패딩과 여백
|
||||
|
||||
// 최소 너비만 보장하고, 최대 너비 제한은 제거
|
||||
const minWidth = 80;
|
||||
|
||||
return Math.max(minWidth, calculatedWidth);
|
||||
};
|
||||
|
||||
// 🎯 값 포맷팅 (전역 코드 캐시 사용)
|
||||
const formatCellValue = useMemo(() => {
|
||||
return (value: any, format?: string, columnName?: string) => {
|
||||
|
|
@ -596,7 +651,226 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
<div className="mt-1 text-xs text-gray-400">{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : needsHorizontalScroll ? (
|
||||
// 가로 스크롤이 필요한 경우 - 고정 컬럼 지원 테이블
|
||||
<div className="relative flex h-full">
|
||||
{/* 왼쪽 고정 컬럼 */}
|
||||
{columnsByPosition.leftFixed.length > 0 && (
|
||||
<div className="flex-shrink-0 border-r bg-gray-50/50">
|
||||
<table className="table-auto">
|
||||
<thead className={tableConfig.stickyHeader ? "sticky top-0 z-20 bg-white" : ""}>
|
||||
<tr>
|
||||
{columnsByPosition.leftFixed.map((column) => (
|
||||
<th
|
||||
key={`fixed-left-${column.columnName}`}
|
||||
style={{ minWidth: `${getColumnWidth(column)}px` }}
|
||||
className={cn(
|
||||
"cursor-pointer border-b px-4 py-3 text-left font-medium whitespace-nowrap text-gray-900 select-none",
|
||||
`text-${column.align}`,
|
||||
column.sortable && "hover:bg-gray-50",
|
||||
)}
|
||||
onClick={() => column.sortable && handleSort(column.columnName)}
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-sm">{columnLabels[column.columnName] || column.displayName}</span>
|
||||
{column.sortable && (
|
||||
<div className="flex flex-col">
|
||||
{sortColumn === column.columnName ? (
|
||||
sortDirection === "asc" ? (
|
||||
<ArrowUp className="h-3 w-3" />
|
||||
) : (
|
||||
<ArrowDown className="h-3 w-3" />
|
||||
)
|
||||
) : (
|
||||
<ArrowUpDown className="h-3 w-3 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={columnsByPosition.leftFixed.length} className="py-8 text-center text-gray-500">
|
||||
데이터가 없습니다
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((row, index) => (
|
||||
<tr
|
||||
key={`fixed-left-row-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer border-b",
|
||||
tableConfig.tableStyle?.hoverEffect && "hover:bg-gray-50",
|
||||
tableConfig.tableStyle?.alternateRows && index % 2 === 1 && "bg-gray-50/50",
|
||||
)}
|
||||
onClick={() => handleRowClick(row)}
|
||||
>
|
||||
{columnsByPosition.leftFixed.map((column) => (
|
||||
<td
|
||||
key={`fixed-left-cell-${column.columnName}`}
|
||||
className={cn("px-4 py-3 text-sm whitespace-nowrap", `text-${column.align}`)}
|
||||
>
|
||||
{formatCellValue(row[column.columnName], column.format, column.columnName)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 스크롤 가능한 중앙 컬럼들 */}
|
||||
<div className="flex-1 overflow-x-auto">
|
||||
<table className="w-full table-auto">
|
||||
<thead className={tableConfig.stickyHeader ? "sticky top-0 z-10 bg-white" : ""}>
|
||||
<tr>
|
||||
{columnsByPosition.normal.map((column) => (
|
||||
<th
|
||||
key={`normal-${column.columnName}`}
|
||||
style={{ minWidth: `${getColumnWidth(column)}px` }}
|
||||
className={cn(
|
||||
"cursor-pointer border-b px-4 py-3 text-left font-medium whitespace-nowrap text-gray-900 select-none",
|
||||
`text-${column.align}`,
|
||||
column.sortable && "hover:bg-gray-50",
|
||||
)}
|
||||
onClick={() => column.sortable && handleSort(column.columnName)}
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-sm">{columnLabels[column.columnName] || column.displayName}</span>
|
||||
{column.sortable && (
|
||||
<div className="flex flex-col">
|
||||
{sortColumn === column.columnName ? (
|
||||
sortDirection === "asc" ? (
|
||||
<ArrowUp className="h-3 w-3" />
|
||||
) : (
|
||||
<ArrowDown className="h-3 w-3" />
|
||||
)
|
||||
) : (
|
||||
<ArrowUpDown className="h-3 w-3 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={columnsByPosition.normal.length} className="py-8 text-center text-gray-500">
|
||||
{columnsByPosition.leftFixed.length === 0 && columnsByPosition.rightFixed.length === 0
|
||||
? "데이터가 없습니다"
|
||||
: ""}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((row, index) => (
|
||||
<tr
|
||||
key={`normal-row-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer border-b",
|
||||
tableConfig.tableStyle?.hoverEffect && "hover:bg-gray-50",
|
||||
tableConfig.tableStyle?.alternateRows && index % 2 === 1 && "bg-gray-50/50",
|
||||
)}
|
||||
onClick={() => handleRowClick(row)}
|
||||
>
|
||||
{columnsByPosition.normal.map((column) => (
|
||||
<td
|
||||
key={`normal-cell-${column.columnName}`}
|
||||
className={cn("px-4 py-3 text-sm whitespace-nowrap", `text-${column.align}`)}
|
||||
>
|
||||
{formatCellValue(row[column.columnName], column.format, column.columnName)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* 오른쪽 고정 컬럼 */}
|
||||
{columnsByPosition.rightFixed.length > 0 && (
|
||||
<div className="flex-shrink-0 border-l bg-gray-50/50">
|
||||
<table className="table-auto">
|
||||
<thead className={tableConfig.stickyHeader ? "sticky top-0 z-20 bg-white" : ""}>
|
||||
<tr>
|
||||
{columnsByPosition.rightFixed.map((column) => (
|
||||
<th
|
||||
key={`fixed-right-${column.columnName}`}
|
||||
style={{ minWidth: `${getColumnWidth(column)}px` }}
|
||||
className={cn(
|
||||
"cursor-pointer border-b px-4 py-3 text-left font-medium whitespace-nowrap text-gray-900 select-none",
|
||||
`text-${column.align}`,
|
||||
column.sortable && "hover:bg-gray-50",
|
||||
)}
|
||||
onClick={() => column.sortable && handleSort(column.columnName)}
|
||||
>
|
||||
<div className="flex items-center space-x-1">
|
||||
<span className="text-sm">{columnLabels[column.columnName] || column.displayName}</span>
|
||||
{column.sortable && (
|
||||
<div className="flex flex-col">
|
||||
{sortColumn === column.columnName ? (
|
||||
sortDirection === "asc" ? (
|
||||
<ArrowUp className="h-3 w-3" />
|
||||
) : (
|
||||
<ArrowDown className="h-3 w-3" />
|
||||
)
|
||||
) : (
|
||||
<ArrowUpDown className="h-3 w-3 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={columnsByPosition.rightFixed.length} className="py-8 text-center text-gray-500">
|
||||
{columnsByPosition.leftFixed.length === 0 && columnsByPosition.normal.length === 0
|
||||
? "데이터가 없습니다"
|
||||
: ""}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((row, index) => (
|
||||
<tr
|
||||
key={`fixed-right-row-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer border-b",
|
||||
tableConfig.tableStyle?.hoverEffect && "hover:bg-gray-50",
|
||||
tableConfig.tableStyle?.alternateRows && index % 2 === 1 && "bg-gray-50/50",
|
||||
)}
|
||||
onClick={() => handleRowClick(row)}
|
||||
>
|
||||
{columnsByPosition.rightFixed.map((column) => (
|
||||
<td
|
||||
key={`fixed-right-cell-${column.columnName}`}
|
||||
className={cn("px-4 py-3 text-sm whitespace-nowrap", `text-${column.align}`)}
|
||||
>
|
||||
{formatCellValue(row[column.columnName], column.format, column.columnName)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
// 기존 테이블 (가로 스크롤이 필요 없는 경우)
|
||||
<Table>
|
||||
<TableHeader className={tableConfig.stickyHeader ? "sticky top-0 z-10 bg-white" : ""}>
|
||||
<TableRow>
|
||||
|
|
@ -605,7 +879,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
key={column.columnName}
|
||||
style={{ width: column.width ? `${column.width}px` : undefined }}
|
||||
className={cn(
|
||||
"cursor-pointer select-none",
|
||||
"cursor-pointer whitespace-nowrap select-none",
|
||||
`text-${column.align}`,
|
||||
column.sortable && "hover:bg-gray-50",
|
||||
)}
|
||||
|
|
@ -650,7 +924,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
onClick={() => handleRowClick(row)}
|
||||
>
|
||||
{visibleColumns.map((column) => (
|
||||
<TableCell key={column.columnName} className={`text-${column.align}`}>
|
||||
<TableCell key={column.columnName} className={cn("whitespace-nowrap", `text-${column.align}`)}>
|
||||
{formatCellValue(row[column.columnName], column.format, column.columnName)}
|
||||
</TableCell>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -10,22 +10,9 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { TableListConfig, ColumnConfig } from "./types";
|
||||
import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||
import {
|
||||
Plus,
|
||||
Trash2,
|
||||
ArrowUp,
|
||||
ArrowDown,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Settings,
|
||||
Columns,
|
||||
Filter,
|
||||
Palette,
|
||||
MousePointer,
|
||||
} from "lucide-react";
|
||||
import { Plus, Trash2, ArrowUp, ArrowDown, Settings, Columns, Filter, Palette, MousePointer } from "lucide-react";
|
||||
|
||||
export interface TableListConfigPanelProps {
|
||||
config: TableListConfig;
|
||||
|
|
@ -321,6 +308,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
|
||||
{/* 기본 설정 탭 */}
|
||||
<TabsContent value="basic" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">연결된 테이블</CardTitle>
|
||||
|
|
@ -482,10 +470,90 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">가로 스크롤 및 컬럼 고정</CardTitle>
|
||||
<CardDescription>컬럼이 많을 때 가로 스크롤과 컬럼 고정 기능을 설정하세요</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="horizontalScrollEnabled"
|
||||
checked={config.horizontalScroll?.enabled}
|
||||
onCheckedChange={(checked) => handleNestedChange("horizontalScroll", "enabled", checked)}
|
||||
/>
|
||||
<Label htmlFor="horizontalScrollEnabled">가로 스크롤 사용</Label>
|
||||
</div>
|
||||
|
||||
{config.horizontalScroll?.enabled && (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="maxVisibleColumns" className="text-sm">
|
||||
최대 표시 컬럼 수
|
||||
</Label>
|
||||
<Input
|
||||
id="maxVisibleColumns"
|
||||
type="number"
|
||||
value={config.horizontalScroll?.maxVisibleColumns || 8}
|
||||
onChange={(e) =>
|
||||
handleNestedChange("horizontalScroll", "maxVisibleColumns", parseInt(e.target.value) || 8)
|
||||
}
|
||||
min={3}
|
||||
max={20}
|
||||
placeholder="8"
|
||||
className="h-8"
|
||||
/>
|
||||
<div className="text-xs text-gray-500">이 수를 넘는 컬럼이 있으면 가로 스크롤이 생성됩니다</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="minColumnWidth" className="text-sm">
|
||||
최소 너비 (px)
|
||||
</Label>
|
||||
<Input
|
||||
id="minColumnWidth"
|
||||
type="number"
|
||||
value={config.horizontalScroll?.minColumnWidth || 100}
|
||||
onChange={(e) =>
|
||||
handleNestedChange("horizontalScroll", "minColumnWidth", parseInt(e.target.value) || 100)
|
||||
}
|
||||
min={50}
|
||||
max={500}
|
||||
placeholder="100"
|
||||
className="h-8"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="maxColumnWidth" className="text-sm">
|
||||
최대 너비 (px)
|
||||
</Label>
|
||||
<Input
|
||||
id="maxColumnWidth"
|
||||
type="number"
|
||||
value={config.horizontalScroll?.maxColumnWidth || 300}
|
||||
onChange={(e) =>
|
||||
handleNestedChange("horizontalScroll", "maxColumnWidth", parseInt(e.target.value) || 300)
|
||||
}
|
||||
min={100}
|
||||
max={800}
|
||||
placeholder="300"
|
||||
className="h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
{/* 컬럼 설정 탭 */}
|
||||
<TabsContent value="columns" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
{!screenTableName ? (
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
|
|
@ -544,7 +612,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
<CardDescription>선택된 컬럼들의 표시 옵션을 설정하세요</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-64">
|
||||
<ScrollArea className="h-96">
|
||||
<div className="space-y-3">
|
||||
{config.columns?.map((column, index) => (
|
||||
<div key={column.columnName} className="space-y-3 rounded-lg border p-3">
|
||||
|
|
@ -661,6 +729,47 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">컬럼 고정</Label>
|
||||
<Select
|
||||
value={column.fixed === false ? "none" : column.fixed || "none"}
|
||||
onValueChange={(value: string) => {
|
||||
const fixedValue = value === "none" ? false : (value as "left" | "right");
|
||||
updateColumn(column.columnName, {
|
||||
fixed: fixedValue,
|
||||
fixedOrder: fixedValue ? column.fixedOrder || 0 : undefined,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">고정 안함</SelectItem>
|
||||
<SelectItem value="left">왼쪽 고정</SelectItem>
|
||||
<SelectItem value="right">오른쪽 고정</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{(column.fixed === "left" || column.fixed === "right") && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">고정 순서</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={column.fixedOrder || 0}
|
||||
onChange={(e) =>
|
||||
updateColumn(column.columnName, {
|
||||
fixedOrder: parseInt(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
placeholder="0"
|
||||
className="h-8"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
|
|
@ -690,10 +799,12 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
{/* Entity 조인 컬럼 추가 탭 */}
|
||||
<TabsContent value="join-columns" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Entity 조인 컬럼 추가</CardTitle>
|
||||
|
|
@ -738,7 +849,10 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div key={colIndex} className="flex items-center justify-between rounded border p-2">
|
||||
<div
|
||||
key={colIndex}
|
||||
className="flex items-center justify-between rounded border p-2"
|
||||
>
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-medium">{column.columnLabel}</div>
|
||||
<div className="text-muted-foreground text-xs">
|
||||
|
|
@ -788,7 +902,9 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{entityJoinColumns.availableColumns.map((column, index) => {
|
||||
const isAlreadyAdded = config.columns?.some((col) => col.columnName === column.joinAlias);
|
||||
const isAlreadyAdded = config.columns?.some(
|
||||
(col) => col.columnName === column.joinAlias,
|
||||
);
|
||||
|
||||
return (
|
||||
<Badge
|
||||
|
|
@ -811,10 +927,12 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
{/* 필터 설정 탭 */}
|
||||
<TabsContent value="filter" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">검색 및 필터</CardTitle>
|
||||
|
|
@ -863,10 +981,12 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
{/* 액션 설정 탭 */}
|
||||
<TabsContent value="actions" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">행 액션</CardTitle>
|
||||
|
|
@ -891,10 +1011,12 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
{/* 스타일 설정 탭 */}
|
||||
<TabsContent value="style" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">테이블 스타일</CardTitle>
|
||||
|
|
@ -967,6 +1089,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,14 @@ export const TableListDefinition = createComponentDefinition({
|
|||
autoWidth: true,
|
||||
stickyHeader: false,
|
||||
|
||||
// 가로 스크롤 및 컬럼 고정 설정
|
||||
horizontalScroll: {
|
||||
enabled: true,
|
||||
maxVisibleColumns: 8, // 8개 컬럼까지는 스크롤 없이 표시
|
||||
minColumnWidth: 100,
|
||||
maxColumnWidth: 300,
|
||||
},
|
||||
|
||||
// 페이지네이션
|
||||
pagination: {
|
||||
enabled: true,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ export interface ColumnConfig {
|
|||
dataType?: string; // 컬럼 데이터 타입 (검색 컬럼 선택에 사용)
|
||||
isEntityJoin?: boolean; // Entity 조인된 컬럼인지 여부
|
||||
entityJoinInfo?: EntityJoinInfo; // Entity 조인 상세 정보
|
||||
|
||||
// 컬럼 고정 관련 속성
|
||||
fixed?: "left" | "right" | false; // 컬럼 고정 위치 (왼쪽, 오른쪽, 고정 안함)
|
||||
fixedOrder?: number; // 고정된 컬럼들 내에서의 순서
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,6 +109,14 @@ export interface TableListConfig extends ComponentConfig {
|
|||
autoWidth: boolean;
|
||||
stickyHeader: boolean;
|
||||
|
||||
// 가로 스크롤 및 컬럼 고정 설정
|
||||
horizontalScroll: {
|
||||
enabled: boolean; // 가로 스크롤 활성화 여부
|
||||
maxVisibleColumns?: number; // 스크롤 없이 표시할 최대 컬럼 수 (이 수를 넘으면 가로 스크롤)
|
||||
minColumnWidth?: number; // 컬럼 최소 너비 (px)
|
||||
maxColumnWidth?: number; // 컬럼 최대 너비 (px)
|
||||
};
|
||||
|
||||
// 페이지네이션
|
||||
pagination: PaginationConfig;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue