123123
This commit is contained in:
@@ -1410,7 +1410,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
const buttonElementStyle: React.CSSProperties = {
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
minHeight: undefined, // 비율 스케일링 시 래퍼 높이를 정확히 따르도록 제거
|
||||
minHeight: "32px", // 🔧 최소 높이를 32px로 줄임
|
||||
// 커스텀 테두리 스타일 (StyleEditor 설정 우선, shorthand 사용 안 함)
|
||||
borderWidth: style?.borderWidth || "0",
|
||||
borderStyle: (style?.borderStyle as React.CSSProperties["borderStyle"]) || (style?.borderWidth ? "solid" : "none"),
|
||||
|
||||
@@ -309,8 +309,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
const [leftFilters, setLeftFilters] = useState<TableFilter[]>([]);
|
||||
const [leftGrouping, setLeftGrouping] = useState<string[]>([]);
|
||||
const [leftColumnVisibility, setLeftColumnVisibility] = useState<ColumnVisibility[]>([]);
|
||||
const [leftColumnOrder, setLeftColumnOrder] = useState<string[]>([]); // 🔧 컬럼 순서
|
||||
const [leftGroupSumConfig, setLeftGroupSumConfig] = useState<GroupSumConfig | null>(null); // 🆕 그룹별 합산 설정
|
||||
const [leftColumnOrder, setLeftColumnOrder] = useState<string[]>([]);
|
||||
const [leftGroupSumConfig, setLeftGroupSumConfig] = useState<GroupSumConfig | null>(null);
|
||||
// 좌측 패널 컬럼 헤더 드래그
|
||||
const [leftDraggedColumnIndex, setLeftDraggedColumnIndex] = useState<number | null>(null);
|
||||
const [leftDropTargetColumnIndex, setLeftDropTargetColumnIndex] = useState<number | null>(null);
|
||||
const [rightFilters, setRightFilters] = useState<TableFilter[]>([]);
|
||||
const [rightGrouping, setRightGrouping] = useState<string[]>([]);
|
||||
const [rightColumnVisibility, setRightColumnVisibility] = useState<ColumnVisibility[]>([]);
|
||||
@@ -2520,6 +2523,47 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
}
|
||||
}, [selectedLeftItem, customLeftSelectedData, componentConfig, companyCode, toast, loadLeftData]);
|
||||
|
||||
// 좌측 패널 컬럼 헤더 드래그
|
||||
const handleLeftColumnDragStart = useCallback(
|
||||
(e: React.DragEvent, columnIndex: number) => {
|
||||
setLeftDraggedColumnIndex(columnIndex);
|
||||
e.dataTransfer.effectAllowed = "move";
|
||||
e.dataTransfer.setData("text/plain", `left-col-${columnIndex}`);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const handleLeftColumnDragOver = useCallback((e: React.DragEvent, columnIndex: number) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = "move";
|
||||
setLeftDropTargetColumnIndex(columnIndex);
|
||||
}, []);
|
||||
const handleLeftColumnDragEnd = useCallback(() => {
|
||||
setLeftDraggedColumnIndex(null);
|
||||
setLeftDropTargetColumnIndex(null);
|
||||
}, []);
|
||||
const handleLeftColumnDrop = useCallback(
|
||||
(e: React.DragEvent, targetIndex: number) => {
|
||||
e.preventDefault();
|
||||
const fromIdx = leftDraggedColumnIndex;
|
||||
if (fromIdx === null || fromIdx === targetIndex) {
|
||||
handleLeftColumnDragEnd();
|
||||
return;
|
||||
}
|
||||
const leftColumns = componentConfig.leftPanel?.columns || [];
|
||||
const colNames = leftColumns
|
||||
.filter((c: any) => typeof c === "string" || c.name || c.columnName)
|
||||
.map((c: any) => typeof c === "string" ? c : c.name || c.columnName);
|
||||
if (colNames.length > 0 && fromIdx >= 0 && fromIdx < colNames.length && targetIndex >= 0 && targetIndex < colNames.length) {
|
||||
const reordered = [...colNames];
|
||||
const [removed] = reordered.splice(fromIdx, 1);
|
||||
reordered.splice(targetIndex, 0, removed);
|
||||
setLeftColumnOrder(reordered);
|
||||
}
|
||||
handleLeftColumnDragEnd();
|
||||
},
|
||||
[leftDraggedColumnIndex, componentConfig, handleLeftColumnDragEnd],
|
||||
);
|
||||
|
||||
// 우측 패널 컬럼 헤더 드래그 (디자인 모드에서 컬럼 순서 변경)
|
||||
const handleRightColumnDragStart = useCallback(
|
||||
(e: React.DragEvent, columnIndex: number, source: "main" | number) => {
|
||||
@@ -3568,6 +3612,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
(componentConfig.leftPanel?.showEdit !== false) ||
|
||||
(componentConfig.leftPanel?.showDelete !== false)
|
||||
);
|
||||
const canDragLeftGroupedColumns = !isDesignMode && columnsToShow.length > 1;
|
||||
if (groupedLeftData.length > 0) {
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
@@ -3579,18 +3624,33 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
<table className="divide-y divide-border table-fixed" style={{ width: leftTotalColWidth > 100 ? `${leftTotalColWidth}%` : '100%' }}>
|
||||
<thead className="bg-muted">
|
||||
<tr>
|
||||
{columnsToShow.map((col, idx) => (
|
||||
{columnsToShow.map((col, idx) => {
|
||||
const isDropTarget = canDragLeftGroupedColumns && leftDropTargetColumnIndex === idx;
|
||||
const isDragging = canDragLeftGroupedColumns && leftDraggedColumnIndex === idx;
|
||||
return (
|
||||
<th
|
||||
key={idx}
|
||||
className="px-3 py-[7px] text-left text-[9px] font-bold tracking-[0.04em] text-muted-foreground uppercase whitespace-nowrap"
|
||||
className={cn(
|
||||
"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",
|
||||
canDragLeftGroupedColumns && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "opacity-50",
|
||||
)}
|
||||
style={{
|
||||
width: col.width && col.width <= 100 ? `${col.width}%` : "auto",
|
||||
textAlign: col.align || "left",
|
||||
}}
|
||||
draggable={canDragLeftGroupedColumns}
|
||||
onDragStart={(e) => canDragLeftGroupedColumns && handleLeftColumnDragStart(e, idx)}
|
||||
onDragOver={(e) => canDragLeftGroupedColumns && handleLeftColumnDragOver(e, idx)}
|
||||
onDragEnd={handleLeftColumnDragEnd}
|
||||
onDrop={(e) => canDragLeftGroupedColumns && handleLeftColumnDrop(e, idx)}
|
||||
>
|
||||
{canDragLeftGroupedColumns && <GripVertical className="text-muted-foreground/40 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 opacity-0 transition-opacity group-hover/th:opacity-100" />}
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
{hasGroupedLeftActions && (
|
||||
<th className="bg-muted sticky right-0 z-10 px-3 py-[7px] text-right text-[9px] font-bold tracking-[0.04em] text-muted-foreground uppercase whitespace-nowrap" style={{ width: "80px" }}>
|
||||
</th>
|
||||
@@ -3671,23 +3731,39 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
(componentConfig.leftPanel?.showEdit !== false) ||
|
||||
(componentConfig.leftPanel?.showDelete !== false)
|
||||
);
|
||||
const canDragLeftColumns = !isDesignMode && columnsToShow.length > 1;
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="divide-y divide-border table-fixed" style={{ width: leftTotalColWidth > 100 ? `${leftTotalColWidth}%` : '100%' }}>
|
||||
<thead className="sticky top-0 z-10 bg-muted">
|
||||
<tr>
|
||||
{columnsToShow.map((col, idx) => (
|
||||
{columnsToShow.map((col, idx) => {
|
||||
const isDropTarget = canDragLeftColumns && leftDropTargetColumnIndex === idx;
|
||||
const isDragging = canDragLeftColumns && leftDraggedColumnIndex === idx;
|
||||
return (
|
||||
<th
|
||||
key={idx}
|
||||
className="px-3 py-[7px] text-left text-[9px] font-bold tracking-[0.04em] text-muted-foreground uppercase whitespace-nowrap"
|
||||
className={cn(
|
||||
"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",
|
||||
canDragLeftColumns && "cursor-grab active:cursor-grabbing",
|
||||
isDragging && "opacity-50",
|
||||
)}
|
||||
style={{
|
||||
width: col.width && col.width <= 100 ? `${col.width}%` : "auto",
|
||||
textAlign: col.align || "left",
|
||||
}}
|
||||
draggable={canDragLeftColumns}
|
||||
onDragStart={(e) => canDragLeftColumns && handleLeftColumnDragStart(e, idx)}
|
||||
onDragOver={(e) => canDragLeftColumns && handleLeftColumnDragOver(e, idx)}
|
||||
onDragEnd={handleLeftColumnDragEnd}
|
||||
onDrop={(e) => canDragLeftColumns && handleLeftColumnDrop(e, idx)}
|
||||
>
|
||||
{canDragLeftColumns && <GripVertical className="text-muted-foreground/40 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 opacity-0 transition-opacity group-hover/th:opacity-100" />}
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
{hasLeftTableActions && (
|
||||
<th className="bg-muted sticky right-0 z-10 px-3 py-[7px] text-right text-[9px] font-bold tracking-[0.04em] text-muted-foreground uppercase whitespace-nowrap" style={{ width: "80px" }}>
|
||||
</th>
|
||||
@@ -4161,9 +4237,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
// 테이블 모드로 표시 (행 클릭 시 상세 정보 펼치기)
|
||||
if (currentTabConfig?.displayMode === "table") {
|
||||
const hasTabActions = currentTabConfig?.showEdit || currentTabConfig?.showDelete;
|
||||
// showInSummary가 false가 아닌 것만 메인 테이블에 표시
|
||||
const tabSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
const tabIndex = activeTabIndex - 1;
|
||||
let tabSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
if (!isDesignMode) {
|
||||
tabSummaryColumns = applyRuntimeOrder(tabSummaryColumns, tabIndex);
|
||||
}
|
||||
const canDragTabColumns = tabSummaryColumns.length > 0;
|
||||
return (
|
||||
<div className="h-full overflow-auto">
|
||||
@@ -4188,7 +4266,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" />}
|
||||
{canDragTabColumns && <GripVertical className="text-muted-foreground/40 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 opacity-0 transition-opacity group-hover/th:opacity-100" />}
|
||||
{col.label || col.name}
|
||||
</th>
|
||||
);
|
||||
@@ -4298,9 +4376,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
// 리스트 모드도 테이블형으로 통일 (행 클릭 시 상세 정보 표시)
|
||||
{
|
||||
const hasTabActions = currentTabConfig?.showEdit || currentTabConfig?.showDelete;
|
||||
// showInSummary가 false가 아닌 것만 메인 테이블에 표시
|
||||
const listSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
const listTabIndex = activeTabIndex - 1;
|
||||
let listSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
|
||||
if (!isDesignMode) {
|
||||
listSummaryColumns = applyRuntimeOrder(listSummaryColumns, listTabIndex);
|
||||
}
|
||||
const canDragListTabColumns = listSummaryColumns.length > 0;
|
||||
return (
|
||||
<div className="h-full overflow-auto">
|
||||
@@ -4325,7 +4405,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" />}
|
||||
{canDragListTabColumns && <GripVertical className="text-muted-foreground/40 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 opacity-0 transition-opacity group-hover/th:opacity-100" />}
|
||||
{col.label || col.name}
|
||||
</th>
|
||||
);
|
||||
@@ -4752,7 +4832,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" />}
|
||||
{isDraggable && <GripVertical className="text-muted-foreground/40 absolute top-1/2 left-0.5 h-3 w-3 -translate-y-1/2 opacity-0 transition-opacity group-hover/th:opacity-100" />}
|
||||
{col.label}
|
||||
</th>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user