jskim-node #421
|
|
@ -359,20 +359,10 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
|||
return `${actualHeight}px`;
|
||||
}
|
||||
|
||||
// 런타임 모드에서 컴포넌트 타입별 높이 처리
|
||||
// 런타임 모드: ResponsiveGridRenderer가 ratio 기반으로 래퍼 높이를 설정하므로,
|
||||
// 안쪽 컴포넌트는 "100%"로 래퍼를 채워야 비율이 정확하게 맞음
|
||||
if (!isDesignMode) {
|
||||
const compType = (component as any).componentType || component.componentConfig?.type || "";
|
||||
// 테이블: 부모 flex 컨테이너가 높이 관리 (flex: 1)
|
||||
const flexGrowTypes = [
|
||||
"table-list", "v2-table-list",
|
||||
"split-panel-layout", "split-panel-layout2",
|
||||
"v2-split-panel-layout", "screen-split-panel",
|
||||
"v2-tab-container", "tab-container",
|
||||
"tabs-widget", "v2-tabs-widget",
|
||||
];
|
||||
if (flexGrowTypes.some(t => compType === t)) {
|
||||
return "100%";
|
||||
}
|
||||
const autoHeightTypes = [
|
||||
"table-search-widget", "v2-table-search-widget",
|
||||
"flow-widget",
|
||||
|
|
@ -380,9 +370,11 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
|||
if (autoHeightTypes.some(t => compType === t || compType.includes(t))) {
|
||||
return "auto";
|
||||
}
|
||||
// 나머지 모든 타입: 래퍼의 비율 스케일링을 따르도록 100%
|
||||
return "100%";
|
||||
}
|
||||
|
||||
// 1순위: size.height가 있으면 우선 사용
|
||||
// 디자인 모드: 고정 픽셀 사용 (캔버스 내 절대 좌표 배치)
|
||||
if (size?.height && size.height > 0) {
|
||||
if (component.componentConfig?.type === "table-list") {
|
||||
return `${Math.max(size.height, 200)}px`;
|
||||
|
|
@ -390,17 +382,14 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
|||
return `${size.height}px`;
|
||||
}
|
||||
|
||||
// 2순위: componentStyle.height (컴포넌트 정의에서 온 기본 스타일)
|
||||
if (componentStyle?.height) {
|
||||
return typeof componentStyle.height === "number" ? `${componentStyle.height}px` : componentStyle.height;
|
||||
}
|
||||
|
||||
// 3순위: 기본값
|
||||
if (component.componentConfig?.type === "table-list") {
|
||||
return "200px";
|
||||
}
|
||||
|
||||
// 기본 높이
|
||||
return "10px";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1410,7 +1410,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
const buttonElementStyle: React.CSSProperties = {
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
minHeight: "32px", // 🔧 최소 높이를 32px로 줄임
|
||||
minHeight: undefined, // 비율 스케일링 시 래퍼 높이를 정확히 따르도록 제거
|
||||
// 커스텀 테두리 스타일 (StyleEditor 설정 우선, shorthand 사용 안 함)
|
||||
borderWidth: style?.borderWidth || "0",
|
||||
borderStyle: (style?.borderStyle as React.CSSProperties["borderStyle"]) || (style?.borderWidth ? "solid" : "none"),
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
FileSpreadsheet,
|
||||
List,
|
||||
PanelRight,
|
||||
GripVertical,
|
||||
} from "lucide-react";
|
||||
import { dataApi } from "@/lib/api/data";
|
||||
import { entityJoinApi } from "@/lib/api/entityJoin";
|
||||
|
|
@ -313,10 +314,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
const [rightFilters, setRightFilters] = useState<TableFilter[]>([]);
|
||||
const [rightGrouping, setRightGrouping] = useState<string[]>([]);
|
||||
const [rightColumnVisibility, setRightColumnVisibility] = useState<ColumnVisibility[]>([]);
|
||||
// 우측 패널 컬럼 헤더 드래그 (디자인 모드에서 순서 변경)
|
||||
// 우측 패널 컬럼 헤더 드래그 (디자인 + 런타임 순서 변경)
|
||||
const [rightDraggedColumnIndex, setRightDraggedColumnIndex] = useState<number | null>(null);
|
||||
const [rightDropTargetColumnIndex, setRightDropTargetColumnIndex] = useState<number | null>(null);
|
||||
const [rightDragSource, setRightDragSource] = useState<"main" | number | null>(null);
|
||||
const [runtimeColumnOrder, setRuntimeColumnOrder] = useState<Record<string, number[]>>({});
|
||||
|
||||
// 데이터 상태
|
||||
const [leftData, setLeftData] = useState<any[]>([]);
|
||||
|
|
@ -2544,55 +2546,67 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
if (!onUpdateComponent) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const rightPanel = componentConfig.rightPanel || {};
|
||||
if (source === "main") {
|
||||
const allColumns = rightPanel.columns || [];
|
||||
const visibleColumns = allColumns.filter((c: any) => c.showInSummary !== false);
|
||||
const hiddenColumns = allColumns.filter((c: any) => c.showInSummary === false);
|
||||
if (fromIdx < 0 || fromIdx >= visibleColumns.length || targetIndex < 0 || targetIndex >= visibleColumns.length) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
|
||||
if (onUpdateComponent) {
|
||||
// 디자인 모드: config에 영구 저장
|
||||
const rightPanel = componentConfig.rightPanel || {};
|
||||
if (source === "main") {
|
||||
const allColumns = rightPanel.columns || [];
|
||||
const visibleColumns = allColumns.filter((c: any) => c.showInSummary !== false);
|
||||
const hiddenColumns = allColumns.filter((c: any) => c.showInSummary === false);
|
||||
if (fromIdx < 0 || fromIdx >= visibleColumns.length || targetIndex < 0 || targetIndex >= visibleColumns.length) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const reordered = [...visibleColumns];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
const columns = [...reordered, ...hiddenColumns];
|
||||
onUpdateComponent({
|
||||
...component,
|
||||
componentConfig: {
|
||||
...componentConfig,
|
||||
rightPanel: { ...rightPanel, columns },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const tabs = [...(rightPanel.additionalTabs || [])];
|
||||
const tabConfig = tabs[source];
|
||||
if (!tabConfig || !Array.isArray(tabConfig.columns)) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const allTabCols = tabConfig.columns;
|
||||
const visibleTabCols = allTabCols.filter((c: any) => c.showInSummary !== false);
|
||||
const hiddenTabCols = allTabCols.filter((c: any) => c.showInSummary === false);
|
||||
if (fromIdx < 0 || fromIdx >= visibleTabCols.length || targetIndex < 0 || targetIndex >= visibleTabCols.length) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const reordered = [...visibleTabCols];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
const columns = [...reordered, ...hiddenTabCols];
|
||||
const newTabs = tabs.map((t, i) => (i === source ? { ...t, columns } : t));
|
||||
onUpdateComponent({
|
||||
...component,
|
||||
componentConfig: {
|
||||
...componentConfig,
|
||||
rightPanel: { ...rightPanel, additionalTabs: newTabs },
|
||||
},
|
||||
});
|
||||
}
|
||||
const reordered = [...visibleColumns];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
const columns = [...reordered, ...hiddenColumns];
|
||||
onUpdateComponent({
|
||||
...component,
|
||||
componentConfig: {
|
||||
...componentConfig,
|
||||
rightPanel: { ...rightPanel, columns },
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const tabs = [...(rightPanel.additionalTabs || [])];
|
||||
const tabConfig = tabs[source];
|
||||
if (!tabConfig || !Array.isArray(tabConfig.columns)) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const allTabCols = tabConfig.columns;
|
||||
const visibleTabCols = allTabCols.filter((c: any) => c.showInSummary !== false);
|
||||
const hiddenTabCols = allTabCols.filter((c: any) => c.showInSummary === false);
|
||||
if (fromIdx < 0 || fromIdx >= visibleTabCols.length || targetIndex < 0 || targetIndex >= visibleTabCols.length) {
|
||||
handleRightColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const reordered = [...visibleTabCols];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
const columns = [...reordered, ...hiddenTabCols];
|
||||
const newTabs = tabs.map((t, i) => (i === source ? { ...t, columns } : t));
|
||||
onUpdateComponent({
|
||||
...component,
|
||||
componentConfig: {
|
||||
...componentConfig,
|
||||
rightPanel: { ...rightPanel, additionalTabs: newTabs },
|
||||
},
|
||||
// 런타임 모드: 로컬 상태로 순서 변경
|
||||
const key = String(source);
|
||||
setRuntimeColumnOrder((prev) => {
|
||||
const existing = prev[key];
|
||||
const maxLen = 100;
|
||||
const order = existing || Array.from({ length: maxLen }, (_, i) => i);
|
||||
const reordered = [...order];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
return { ...prev, [key]: reordered };
|
||||
});
|
||||
}
|
||||
handleRightColumnDragEnd();
|
||||
|
|
@ -2604,9 +2618,29 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
component,
|
||||
onUpdateComponent,
|
||||
handleRightColumnDragEnd,
|
||||
setRuntimeColumnOrder,
|
||||
],
|
||||
);
|
||||
|
||||
// 런타임 컬럼 순서 적용 헬퍼
|
||||
const applyRuntimeOrder = useCallback(
|
||||
<T,>(columns: T[], source: "main" | number): T[] => {
|
||||
const key = String(source);
|
||||
const order = runtimeColumnOrder[key];
|
||||
if (!order) return columns;
|
||||
const result: T[] = [];
|
||||
for (const idx of order) {
|
||||
if (idx < columns.length) result.push(columns[idx]);
|
||||
}
|
||||
// order에 없는 나머지 컬럼 추가
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
if (!order.includes(i)) result.push(columns[i]);
|
||||
}
|
||||
return result.length > 0 ? result : columns;
|
||||
},
|
||||
[runtimeColumnOrder],
|
||||
);
|
||||
|
||||
// 수정 모달 저장
|
||||
const handleEditModalSave = useCallback(async () => {
|
||||
const tableName =
|
||||
|
|
@ -3946,11 +3980,10 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
{resizable && (
|
||||
<div
|
||||
onMouseDown={handleMouseDown}
|
||||
className="group flex w-1.5 cursor-col-resize flex-col items-center justify-center gap-0.5 bg-border transition-colors hover:bg-primary"
|
||||
className="group flex w-1.5 cursor-col-resize flex-col items-center justify-center gap-0.5 bg-border/60 transition-colors hover:bg-primary/25"
|
||||
aria-label="분할선 드래그"
|
||||
>
|
||||
<div className="h-7 w-0.5 rounded-full bg-muted-foreground/40 transition-colors group-hover:bg-primary-foreground/80" />
|
||||
<div className="h-7 w-0.5 rounded-full bg-muted-foreground/40 transition-colors group-hover:bg-primary-foreground/80" />
|
||||
<div className="h-7 w-0.5 rounded-full bg-muted-foreground/30 transition-opacity group-hover:opacity-70" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -4107,7 +4140,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
// showInSummary가 false가 아닌 것만 메인 테이블에 표시
|
||||
const tabSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
const tabIndex = activeTabIndex - 1;
|
||||
const canDragTabColumns = isDesignMode && tabSummaryColumns.length > 0 && !!onUpdateComponent;
|
||||
const canDragTabColumns = tabSummaryColumns.length > 0;
|
||||
return (
|
||||
<div className="h-full overflow-auto">
|
||||
<table className="w-full text-sm">
|
||||
|
|
@ -4120,7 +4153,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<th
|
||||
key={col.name}
|
||||
className={cn(
|
||||
"text-muted-foreground px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em]",
|
||||
"group/th text-muted-foreground relative px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em]",
|
||||
isDropTarget && "border-l-[3px] border-l-primary bg-primary/5",
|
||||
canDragTabColumns && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "opacity-50",
|
||||
|
|
@ -4131,6 +4164,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
onDragEnd={handleRightColumnDragEnd}
|
||||
onDrop={(e) => canDragTabColumns && handleRightColumnDrop(e, idx, tabIndex)}
|
||||
>
|
||||
{canDragTabColumns && <GripVertical className="text-muted-foreground/30 group-hover/th:text-muted-foreground/60 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 transition-opacity" />}
|
||||
{col.label || col.name}
|
||||
</th>
|
||||
);
|
||||
|
|
@ -4158,7 +4192,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<tr
|
||||
className={cn(
|
||||
"group/action cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/50 hover:bg-accent" : "hover:bg-accent",
|
||||
isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/20 hover:bg-accent" : "hover:bg-accent",
|
||||
)}
|
||||
onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)}
|
||||
>
|
||||
|
|
@ -4243,7 +4277,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
// showInSummary가 false가 아닌 것만 메인 테이블에 표시
|
||||
const listSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
const listTabIndex = activeTabIndex - 1;
|
||||
const canDragListTabColumns = isDesignMode && listSummaryColumns.length > 0 && !!onUpdateComponent;
|
||||
const canDragListTabColumns = listSummaryColumns.length > 0;
|
||||
return (
|
||||
<div className="h-full overflow-auto">
|
||||
<table className="w-full text-sm">
|
||||
|
|
@ -4256,7 +4290,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<th
|
||||
key={col.name}
|
||||
className={cn(
|
||||
"text-muted-foreground px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em]",
|
||||
"group/th text-muted-foreground relative px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em]",
|
||||
isDropTarget && "border-l-[3px] border-l-primary bg-primary/5",
|
||||
canDragListTabColumns && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "opacity-50",
|
||||
|
|
@ -4267,6 +4301,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
onDragEnd={handleRightColumnDragEnd}
|
||||
onDrop={(e) => canDragListTabColumns && handleRightColumnDrop(e, idx, listTabIndex)}
|
||||
>
|
||||
{canDragListTabColumns && <GripVertical className="text-muted-foreground/30 group-hover/th:text-muted-foreground/60 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 transition-opacity" />}
|
||||
{col.label || col.name}
|
||||
</th>
|
||||
);
|
||||
|
|
@ -4293,7 +4328,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<tr
|
||||
className={cn(
|
||||
"group/action cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/50 hover:bg-accent" : "hover:bg-accent",
|
||||
isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/20 hover:bg-accent" : "hover:bg-accent",
|
||||
)}
|
||||
onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)}
|
||||
>
|
||||
|
|
@ -4646,6 +4681,14 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
}));
|
||||
}
|
||||
|
||||
// 런타임 컬럼 순서 적용
|
||||
if (!isDesignMode && runtimeColumnOrder["main"]) {
|
||||
const keyColCount = columnsToShow.filter((c: any) => c._isKeyColumn).length;
|
||||
const keyCols = columnsToShow.slice(0, keyColCount);
|
||||
const dataCols = columnsToShow.slice(keyColCount);
|
||||
columnsToShow = [...keyCols, ...applyRuntimeOrder(dataCols, "main")];
|
||||
}
|
||||
|
||||
// 컬럼 너비 합계 계산 (작업 컬럼 제외, 100% 초과 시 스크롤)
|
||||
const rightTotalColWidth = columnsToShow.reduce((sum, col) => {
|
||||
const w = col.width && col.width <= 100 ? col.width : 0;
|
||||
|
|
@ -4653,7 +4696,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
}, 0);
|
||||
|
||||
const rightConfigColumnStart = columnsToShow.filter((c: any) => c._isKeyColumn).length;
|
||||
const canDragRightColumns = isDesignMode && displayColumns.length > 0 && !!onUpdateComponent;
|
||||
const canDragRightColumns = displayColumns.length > 0;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col">
|
||||
|
|
@ -4670,7 +4713,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<th
|
||||
key={idx}
|
||||
className={cn(
|
||||
"text-muted-foreground px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em] whitespace-nowrap",
|
||||
"group/th text-muted-foreground relative px-3 py-[7px] text-left text-[9px] font-bold uppercase tracking-[0.04em] whitespace-nowrap",
|
||||
isDropTarget && "border-l-[3px] border-l-primary bg-primary/5",
|
||||
isDraggable && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "opacity-50",
|
||||
|
|
@ -4685,6 +4728,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
onDragEnd={handleRightColumnDragEnd}
|
||||
onDrop={(e) => isDraggable && handleRightColumnDrop(e, configColIndex, "main")}
|
||||
>
|
||||
{isDraggable && <GripVertical className="text-muted-foreground/30 group-hover/th:text-muted-foreground/60 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 transition-opacity" />}
|
||||
{col.label}
|
||||
</th>
|
||||
);
|
||||
|
|
@ -4707,7 +4751,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
const rightDeleteVisible = (componentConfig.rightPanel?.showDelete ?? componentConfig.rightPanel?.deleteButton?.enabled) !== false;
|
||||
|
||||
return (
|
||||
<tr key={itemId} className={cn("group/action border-b border-border/50 transition-[background] duration-75 hover:bg-accent", idx % 2 === 1 && "bg-muted/50")}>
|
||||
<tr key={itemId} className={cn("group/action border-b border-border/50 transition-[background] duration-75 hover:bg-accent", idx % 2 === 1 && "bg-muted/20")}>
|
||||
{columnsToShow.map((col, colIdx) => (
|
||||
<td
|
||||
key={colIdx}
|
||||
|
|
@ -4851,7 +4895,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
|||
<tr
|
||||
className={cn(
|
||||
"group/action cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
isExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/50 hover:bg-accent" : "hover:bg-accent",
|
||||
isExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/20 hover:bg-accent" : "hover:bg-accent",
|
||||
)}
|
||||
onClick={() => toggleRightItemExpansion(itemId)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
>
|
||||
<TableHeader
|
||||
className={cn("border-b border-border/60", tableConfig?.stickyHeader && "sticky top-0 z-30 shadow-sm")}
|
||||
style={{ backgroundColor: "hsl(var(--muted) / 0.8)" }}
|
||||
style={{ backgroundColor: "hsl(var(--muted) / 0.4)" }}
|
||||
>
|
||||
<TableRow className="border-b border-border/60">
|
||||
{actualColumns.map((column, colIndex) => {
|
||||
|
|
@ -136,7 +136,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
? "h-9 border-0 px-3 py-1.5 text-center align-middle sm:px-4 sm:py-2"
|
||||
: "text-muted-foreground hover:text-foreground h-9 cursor-pointer border-0 px-3 py-1.5 text-left align-middle text-[10px] font-bold uppercase tracking-[0.04em] whitespace-nowrap transition-all duration-200 select-none sm:px-4 sm:py-2 sm:text-xs",
|
||||
`text-${column.align}`,
|
||||
column.sortable && "hover:bg-muted/70",
|
||||
column.sortable && "hover:bg-muted/50",
|
||||
// 고정 컬럼 스타일
|
||||
column.fixed === "left" && "border-border bg-background sticky z-40 border-r shadow-sm",
|
||||
column.fixed === "right" && "border-border bg-background sticky z-40 border-l shadow-sm",
|
||||
|
|
@ -151,7 +151,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap", // 텍스트 줄바꿈 방지
|
||||
backgroundColor: "hsl(var(--muted) / 0.8)",
|
||||
backgroundColor: "hsl(var(--muted) / 0.4)",
|
||||
// sticky 위치 설정
|
||||
...(column.fixed === "left" && { left: leftFixedWidth }),
|
||||
...(column.fixed === "right" && { right: rightFixedWidth }),
|
||||
|
|
@ -230,7 +230,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
key={`row-${index}`}
|
||||
className={cn(
|
||||
"cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/70",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/20",
|
||||
tableConfig.tableStyle?.hoverEffect !== false && "hover:bg-accent",
|
||||
)}
|
||||
onClick={(e) => handleRowClick?.(row, index, e)}
|
||||
|
|
|
|||
|
|
@ -5819,8 +5819,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
{/* 🆕 Multi-Level Headers (Column Bands) */}
|
||||
{columnBandsInfo?.hasBands && (
|
||||
<tr
|
||||
className="border-primary/10 bg-muted/70 h-8 border-b sm:h-10"
|
||||
style={{ backgroundColor: "hsl(var(--muted) / 0.7)" }}
|
||||
className="border-border/60 bg-muted/40 h-8 border-b sm:h-10"
|
||||
style={{ backgroundColor: "hsl(var(--muted) / 0.4)" }}
|
||||
>
|
||||
{visibleColumns.map((column, colIdx) => {
|
||||
// 이 컬럼이 속한 band 찾기
|
||||
|
|
@ -5863,7 +5863,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
<tr
|
||||
className="bg-muted/80 h-10 border-b border-border/60 sm:h-12"
|
||||
style={{
|
||||
backgroundColor: "hsl(var(--muted) / 0.8)",
|
||||
backgroundColor: "hsl(var(--muted) / 0.4)",
|
||||
}}
|
||||
>
|
||||
{visibleColumns.map((column, columnIndex) => {
|
||||
|
|
@ -5895,7 +5895,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
column.columnName === "__checkbox__" ? "px-0 py-1" : "px-3 py-2",
|
||||
column.sortable !== false &&
|
||||
column.columnName !== "__checkbox__" &&
|
||||
"hover:text-foreground hover:bg-muted/70 cursor-pointer transition-colors",
|
||||
"hover:text-foreground hover:bg-muted/50 cursor-pointer transition-colors",
|
||||
sortColumn === column.columnName && "!text-primary",
|
||||
isFrozen && "sticky z-40 shadow-[2px_0_4px_rgba(0,0,0,0.1)]",
|
||||
// 🆕 Column Reordering 스타일
|
||||
|
|
@ -5916,7 +5916,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
minWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
|
||||
maxWidth: column.columnName === "__checkbox__" ? "48px" : undefined,
|
||||
userSelect: "none",
|
||||
backgroundColor: "hsl(var(--muted) / 0.8)",
|
||||
backgroundColor: "hsl(var(--muted) / 0.4)",
|
||||
...(isFrozen && { left: `${leftPosition}px` }),
|
||||
}}
|
||||
// 🆕 Column Reordering 이벤트
|
||||
|
|
@ -6167,7 +6167,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
key={index}
|
||||
className={cn(
|
||||
"hover:bg-accent cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/70",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/20",
|
||||
)}
|
||||
onClick={(e) => handleRowClick(row, index, e)}
|
||||
>
|
||||
|
|
@ -6306,9 +6306,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
key={index}
|
||||
className={cn(
|
||||
"hover:bg-accent cursor-pointer border-b border-border/50 transition-[background] duration-75",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/70",
|
||||
isRowSelected && "!bg-primary/15 hover:!bg-primary/20",
|
||||
isRowSelected && "[&_td]:!border-b-primary/30",
|
||||
index % 2 === 0 ? "bg-background" : "bg-muted/20",
|
||||
isRowSelected && "!bg-primary/10 hover:!bg-primary/15",
|
||||
isRowFocused && "ring-primary/50 ring-1 ring-inset",
|
||||
isDragEnabled && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "bg-muted opacity-50",
|
||||
|
|
@ -6566,7 +6565,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
: undefined,
|
||||
...(isFrozen && {
|
||||
left: `${leftPosition}px`,
|
||||
backgroundColor: "hsl(var(--muted) / 0.8)",
|
||||
backgroundColor: "hsl(var(--muted) / 0.4)",
|
||||
}),
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue