Merge origin/main into ksh - resolve conflicts
This commit is contained in:
@@ -47,7 +47,7 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
|
||||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||
selectedRows?: any[];
|
||||
selectedRowsData?: any[];
|
||||
|
||||
|
||||
// 테이블 정렬 정보 (엑셀 다운로드용)
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
@@ -57,10 +57,10 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
|
||||
// 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
|
||||
flowSelectedData?: any[];
|
||||
flowSelectedStepId?: number | null;
|
||||
|
||||
|
||||
// 🆕 같은 화면의 모든 컴포넌트 (TableList 자동 감지용)
|
||||
allComponents?: any[];
|
||||
|
||||
|
||||
// 🆕 부모창에서 전달된 그룹 데이터 (모달에서 부모 데이터 접근용)
|
||||
groupedData?: Record<string, any>[];
|
||||
}
|
||||
@@ -109,11 +109,11 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
const splitPanelContext = useSplitPanelContext(); // 분할 패널 컨텍스트
|
||||
// 🆕 ScreenContext에서 splitPanelPosition 가져오기 (중첩 화면에서도 작동)
|
||||
const splitPanelPosition = screenContext?.splitPanelPosition;
|
||||
|
||||
|
||||
// 🆕 tableName이 props로 전달되지 않으면 ScreenContext에서 가져오기
|
||||
const effectiveTableName = tableName || screenContext?.tableName;
|
||||
const effectiveScreenId = screenId || screenContext?.screenId;
|
||||
|
||||
|
||||
// 🆕 props에서 onSave 추출 (명시적으로 선언되지 않은 경우 ...props에서 추출)
|
||||
const propsOnSave = (props as any).onSave as (() => Promise<void>) | undefined;
|
||||
const finalOnSave = onSave || propsOnSave;
|
||||
@@ -169,10 +169,10 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
if (!shouldFetchStatus) return;
|
||||
|
||||
let isMounted = true;
|
||||
|
||||
|
||||
const fetchStatus = async () => {
|
||||
if (!isMounted) return;
|
||||
|
||||
|
||||
try {
|
||||
const response = await apiClient.post(`/table-management/tables/${statusTableName}/data`, {
|
||||
page: 1,
|
||||
@@ -180,12 +180,12 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
search: { [statusKeyField]: userId },
|
||||
autoFilter: true,
|
||||
});
|
||||
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
|
||||
const rows = response.data?.data?.data || response.data?.data?.rows || response.data?.rows || [];
|
||||
const firstRow = Array.isArray(rows) ? rows[0] : null;
|
||||
|
||||
|
||||
if (response.data?.success && firstRow) {
|
||||
const newStatus = firstRow[statusFieldName];
|
||||
if (newStatus !== vehicleStatus) {
|
||||
@@ -206,10 +206,10 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
// 즉시 실행
|
||||
setStatusLoading(true);
|
||||
fetchStatus();
|
||||
|
||||
|
||||
// 2초마다 갱신
|
||||
const interval = setInterval(fetchStatus, 2000);
|
||||
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
clearInterval(interval);
|
||||
@@ -219,22 +219,22 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
// 버튼 비활성화 조건 계산
|
||||
const isOperationButtonDisabled = useMemo(() => {
|
||||
const actionConfig = component.componentConfig?.action;
|
||||
|
||||
|
||||
if (actionConfig?.type !== "operation_control") return false;
|
||||
|
||||
// 1. 출발지/도착지 필수 체크
|
||||
if (actionConfig?.requireLocationFields) {
|
||||
const departureField = actionConfig.trackingDepartureField || "departure";
|
||||
const destinationField = actionConfig.trackingArrivalField || "destination";
|
||||
|
||||
|
||||
const departure = formData?.[departureField];
|
||||
const destination = formData?.[destinationField];
|
||||
|
||||
// console.log("🔍 [ButtonPrimary] 출발지/도착지 체크:", {
|
||||
// departureField, destinationField, departure, destination,
|
||||
// buttonLabel: component.label
|
||||
|
||||
// console.log("🔍 [ButtonPrimary] 출발지/도착지 체크:", {
|
||||
// departureField, destinationField, departure, destination,
|
||||
// buttonLabel: component.label
|
||||
// });
|
||||
|
||||
|
||||
if (!departure || departure === "" || !destination || destination === "") {
|
||||
// console.log("🚫 [ButtonPrimary] 출발지/도착지 미선택 → 비활성화:", component.label);
|
||||
return true;
|
||||
@@ -246,20 +246,20 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
const statusField = actionConfig.statusCheckField || "status";
|
||||
// API 조회 결과를 우선 사용 (실시간 DB 상태 반영)
|
||||
const currentStatus = vehicleStatus || formData?.[statusField];
|
||||
|
||||
|
||||
const conditionType = actionConfig.statusConditionType || "enableOn";
|
||||
const conditionValues = (actionConfig.statusConditionValues || "")
|
||||
.split(",")
|
||||
.map((v: string) => v.trim())
|
||||
.filter((v: string) => v);
|
||||
|
||||
// console.log("🔍 [ButtonPrimary] 상태 조건 체크:", {
|
||||
// console.log("🔍 [ButtonPrimary] 상태 조건 체크:", {
|
||||
// statusField,
|
||||
// formDataStatus: formData?.[statusField],
|
||||
// apiStatus: vehicleStatus,
|
||||
// currentStatus,
|
||||
// conditionType,
|
||||
// conditionValues,
|
||||
// currentStatus,
|
||||
// conditionType,
|
||||
// conditionValues,
|
||||
// buttonLabel: component.label,
|
||||
// });
|
||||
|
||||
@@ -274,7 +274,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
// console.log("🚫 [ButtonPrimary] 상태값 없음 → 비활성화:", component.label);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (conditionValues.length > 0) {
|
||||
if (conditionType === "enableOn") {
|
||||
// 이 상태일 때만 활성화
|
||||
@@ -551,7 +551,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
*/
|
||||
const handleTransferDataAction = async (actionConfig: any) => {
|
||||
const dataTransferConfig = actionConfig.dataTransfer;
|
||||
|
||||
|
||||
if (!dataTransferConfig) {
|
||||
toast.error("데이터 전달 설정이 없습니다.");
|
||||
return;
|
||||
@@ -565,15 +565,15 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
try {
|
||||
// 1. 소스 컴포넌트에서 데이터 가져오기
|
||||
let sourceProvider = screenContext.getDataProvider(dataTransferConfig.sourceComponentId);
|
||||
|
||||
|
||||
// 🆕 소스 컴포넌트를 찾을 수 없으면, 현재 화면에서 테이블 리스트 자동 탐색
|
||||
// (조건부 컨테이너의 다른 섹션으로 전환했을 때 이전 컴포넌트 ID가 남아있는 경우 대응)
|
||||
if (!sourceProvider) {
|
||||
console.log(`⚠️ [ButtonPrimary] 지정된 소스 컴포넌트를 찾을 수 없음: ${dataTransferConfig.sourceComponentId}`);
|
||||
console.log(`🔍 [ButtonPrimary] 현재 화면에서 DataProvider 자동 탐색...`);
|
||||
|
||||
|
||||
const allProviders = screenContext.getAllDataProviders();
|
||||
|
||||
|
||||
// 테이블 리스트 우선 탐색
|
||||
for (const [id, provider] of allProviders) {
|
||||
if (provider.componentType === "table-list") {
|
||||
@@ -582,16 +582,18 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 테이블 리스트가 없으면 첫 번째 DataProvider 사용
|
||||
if (!sourceProvider && allProviders.size > 0) {
|
||||
const firstEntry = allProviders.entries().next().value;
|
||||
if (firstEntry) {
|
||||
sourceProvider = firstEntry[1];
|
||||
console.log(`✅ [ButtonPrimary] 첫 번째 DataProvider 사용: ${firstEntry[0]} (${sourceProvider.componentType})`);
|
||||
console.log(
|
||||
`✅ [ButtonPrimary] 첫 번째 DataProvider 사용: ${firstEntry[0]} (${sourceProvider.componentType})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!sourceProvider) {
|
||||
toast.error("데이터를 제공할 수 있는 컴포넌트를 찾을 수 없습니다.");
|
||||
return;
|
||||
@@ -599,12 +601,12 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
}
|
||||
|
||||
const rawSourceData = sourceProvider.getSelectedData();
|
||||
|
||||
|
||||
// 🆕 배열이 아닌 경우 배열로 변환
|
||||
const sourceData = Array.isArray(rawSourceData) ? rawSourceData : (rawSourceData ? [rawSourceData] : []);
|
||||
|
||||
const sourceData = Array.isArray(rawSourceData) ? rawSourceData : rawSourceData ? [rawSourceData] : [];
|
||||
|
||||
console.log("📦 소스 데이터:", { rawSourceData, sourceData, isArray: Array.isArray(rawSourceData) });
|
||||
|
||||
|
||||
if (!sourceData || sourceData.length === 0) {
|
||||
toast.warning("선택된 데이터가 없습니다.");
|
||||
return;
|
||||
@@ -612,31 +614,32 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
|
||||
// 1.5. 추가 데이터 소스 처리 (예: 조건부 컨테이너의 카테고리 값)
|
||||
let additionalData: Record<string, any> = {};
|
||||
|
||||
|
||||
// 방법 1: additionalSources 설정에서 가져오기
|
||||
if (dataTransferConfig.additionalSources && Array.isArray(dataTransferConfig.additionalSources)) {
|
||||
for (const additionalSource of dataTransferConfig.additionalSources) {
|
||||
const additionalProvider = screenContext.getDataProvider(additionalSource.componentId);
|
||||
|
||||
|
||||
if (additionalProvider) {
|
||||
const additionalValues = additionalProvider.getSelectedData();
|
||||
|
||||
|
||||
if (additionalValues && additionalValues.length > 0) {
|
||||
// 첫 번째 값 사용 (조건부 컨테이너는 항상 1개)
|
||||
const firstValue = additionalValues[0];
|
||||
|
||||
|
||||
// fieldName이 지정되어 있으면 그 필드만 추출
|
||||
if (additionalSource.fieldName) {
|
||||
additionalData[additionalSource.fieldName] = firstValue[additionalSource.fieldName] || firstValue.condition || firstValue;
|
||||
additionalData[additionalSource.fieldName] =
|
||||
firstValue[additionalSource.fieldName] || firstValue.condition || firstValue;
|
||||
} else {
|
||||
// fieldName이 없으면 전체 객체 병합
|
||||
additionalData = { ...additionalData, ...firstValue };
|
||||
}
|
||||
|
||||
|
||||
console.log("📦 추가 데이터 수집 (additionalSources):", {
|
||||
sourceId: additionalSource.componentId,
|
||||
fieldName: additionalSource.fieldName,
|
||||
value: additionalData[additionalSource.fieldName || 'all'],
|
||||
value: additionalData[additionalSource.fieldName || "all"],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -651,7 +654,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
const conditionalValue = formData.__conditionalContainerValue;
|
||||
const conditionalLabel = formData.__conditionalContainerLabel;
|
||||
const controlField = formData.__conditionalContainerControlField; // 🆕 제어 필드명 직접 사용
|
||||
|
||||
|
||||
// 🆕 controlField가 있으면 그것을 필드명으로 사용 (자동 매핑!)
|
||||
if (controlField) {
|
||||
additionalData[controlField] = conditionalValue;
|
||||
@@ -663,7 +666,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
} else {
|
||||
// controlField가 없으면 기존 방식: formData에서 같은 값을 가진 키 찾기
|
||||
for (const [key, value] of Object.entries(formData)) {
|
||||
if (value === conditionalValue && !key.startsWith('__')) {
|
||||
if (value === conditionalValue && !key.startsWith("__")) {
|
||||
additionalData[key] = conditionalValue;
|
||||
console.log("📦 조건부 컨테이너 값 자동 포함:", {
|
||||
fieldName: key,
|
||||
@@ -673,12 +676,12 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 못 찾았으면 기본 필드명 사용
|
||||
if (!Object.keys(additionalData).some(k => !k.startsWith('__'))) {
|
||||
additionalData['condition_type'] = conditionalValue;
|
||||
if (!Object.keys(additionalData).some((k) => !k.startsWith("__"))) {
|
||||
additionalData["condition_type"] = conditionalValue;
|
||||
console.log("📦 조건부 컨테이너 값 (기본 필드명):", {
|
||||
fieldName: 'condition_type',
|
||||
fieldName: "condition_type",
|
||||
value: conditionalValue,
|
||||
});
|
||||
}
|
||||
@@ -710,7 +713,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
// 4. 매핑 규칙 적용 + 추가 데이터 병합
|
||||
const mappedData = sourceData.map((row) => {
|
||||
const mappedRow = applyMappingRules(row, dataTransferConfig.mappingRules || []);
|
||||
|
||||
|
||||
// 추가 데이터를 모든 행에 포함
|
||||
return {
|
||||
...mappedRow,
|
||||
@@ -730,7 +733,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
if (dataTransferConfig.targetType === "component") {
|
||||
// 같은 화면의 컴포넌트로 전달
|
||||
const targetReceiver = screenContext.getDataReceiver(dataTransferConfig.targetComponentId);
|
||||
|
||||
|
||||
if (!targetReceiver) {
|
||||
toast.error(`타겟 컴포넌트를 찾을 수 없습니다: ${dataTransferConfig.targetComponentId}`);
|
||||
return;
|
||||
@@ -742,7 +745,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
mode: dataTransferConfig.mode || "append",
|
||||
mappingRules: dataTransferConfig.mappingRules || [],
|
||||
});
|
||||
|
||||
|
||||
toast.success(`${sourceData.length}개 항목이 전달되었습니다.`);
|
||||
} else if (dataTransferConfig.targetType === "splitPanel") {
|
||||
// 🆕 분할 패널의 반대편 화면으로 전달
|
||||
@@ -750,17 +753,18 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
toast.error("분할 패널 컨텍스트를 찾을 수 없습니다. 이 버튼이 분할 패널 내부에 있는지 확인하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 🆕 useSplitPanelPosition 훅으로 위치 가져오기 (중첩된 화면에서도 작동)
|
||||
// screenId로 찾는 것은 직접 임베드된 화면에서만 작동하므로,
|
||||
// screenId로 찾는 것은 직접 임베드된 화면에서만 작동하므로,
|
||||
// SplitPanelPositionProvider로 전달된 위치를 우선 사용
|
||||
const currentPosition = splitPanelPosition || (screenId ? splitPanelContext.getPositionByScreenId(screenId) : null);
|
||||
|
||||
const currentPosition =
|
||||
splitPanelPosition || (screenId ? splitPanelContext.getPositionByScreenId(screenId) : null);
|
||||
|
||||
if (!currentPosition) {
|
||||
toast.error("분할 패널 내 위치를 확인할 수 없습니다. screenId: " + screenId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
console.log("📦 분할 패널 데이터 전달:", {
|
||||
currentPosition,
|
||||
splitPanelPositionFromHook: splitPanelPosition,
|
||||
@@ -768,14 +772,14 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
leftScreenId: splitPanelContext.leftScreenId,
|
||||
rightScreenId: splitPanelContext.rightScreenId,
|
||||
});
|
||||
|
||||
|
||||
const result = await splitPanelContext.transferToOtherSide(
|
||||
currentPosition,
|
||||
mappedData,
|
||||
dataTransferConfig.targetComponentId, // 특정 컴포넌트 지정 (선택사항)
|
||||
dataTransferConfig.mode || "append"
|
||||
dataTransferConfig.mode || "append",
|
||||
);
|
||||
|
||||
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
} else {
|
||||
@@ -794,7 +798,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
if (dataTransferConfig.clearAfterTransfer) {
|
||||
sourceProvider.clearSelection();
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error("❌ 데이터 전달 실패:", error);
|
||||
toast.error(error.message || "데이터 전달 중 오류가 발생했습니다.");
|
||||
@@ -828,16 +831,20 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
// 2. groupedData (부모창에서 모달로 전달된 데이터)
|
||||
// 3. modalDataStore (분할 패널 등에서 선택한 데이터)
|
||||
let effectiveSelectedRowsData = selectedRowsData;
|
||||
|
||||
|
||||
// groupedData가 있으면 우선 사용 (모달에서 부모 데이터 접근)
|
||||
if ((!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && groupedData && groupedData.length > 0) {
|
||||
if (
|
||||
(!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) &&
|
||||
groupedData &&
|
||||
groupedData.length > 0
|
||||
) {
|
||||
effectiveSelectedRowsData = groupedData;
|
||||
console.log("🔗 [ButtonPrimaryComponent] groupedData에서 부모창 데이터 가져옴:", {
|
||||
count: groupedData.length,
|
||||
data: groupedData,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// modalDataStore에서 선택된 데이터 가져오기 (분할 패널 등에서 선택한 데이터)
|
||||
if ((!effectiveSelectedRowsData || effectiveSelectedRowsData.length === 0) && effectiveTableName) {
|
||||
try {
|
||||
@@ -845,11 +852,17 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
const dataRegistry = useModalDataStore.getState().dataRegistry;
|
||||
const modalData = dataRegistry[effectiveTableName];
|
||||
if (modalData && modalData.length > 0) {
|
||||
effectiveSelectedRowsData = modalData;
|
||||
// modalDataStore는 {id, originalData, additionalData} 형태로 저장됨
|
||||
// originalData를 추출하여 실제 행 데이터를 가져옴
|
||||
effectiveSelectedRowsData = modalData.map((item: any) => {
|
||||
// originalData가 있으면 그것을 사용, 없으면 item 자체 사용 (하위 호환성)
|
||||
return item.originalData || item;
|
||||
});
|
||||
console.log("🔗 [ButtonPrimaryComponent] modalDataStore에서 선택된 데이터 가져옴:", {
|
||||
tableName: effectiveTableName,
|
||||
count: modalData.length,
|
||||
data: modalData,
|
||||
rawData: modalData,
|
||||
extractedData: effectiveSelectedRowsData,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -859,7 +872,8 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
|
||||
// 삭제 액션인데 선택된 데이터가 없으면 경고 메시지 표시하고 중단
|
||||
const hasDataToDelete =
|
||||
(effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) || (flowSelectedData && flowSelectedData.length > 0);
|
||||
(effectiveSelectedRowsData && effectiveSelectedRowsData.length > 0) ||
|
||||
(flowSelectedData && flowSelectedData.length > 0);
|
||||
|
||||
if (processedConfig.action.type === "delete" && !hasDataToDelete) {
|
||||
toast.warning("삭제할 항목을 먼저 선택해주세요.");
|
||||
@@ -905,8 +919,27 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 분할 패널 우측이면 screenContext.formData와 props.formData를 병합
|
||||
// screenContext.formData: RepeaterFieldGroup 등 컴포넌트가 직접 업데이트한 데이터
|
||||
// props.formData: 부모에서 전달된 폼 데이터
|
||||
const screenContextFormData = screenContext?.formData || {};
|
||||
const propsFormData = formData || {};
|
||||
|
||||
// 병합: props.formData를 기본으로 하고, screenContext.formData로 오버라이드
|
||||
// (RepeaterFieldGroup 데이터는 screenContext에만 있음)
|
||||
const effectiveFormData = { ...propsFormData, ...screenContextFormData };
|
||||
|
||||
console.log("🔍 [ButtonPrimary] formData 선택:", {
|
||||
hasScreenContextFormData: Object.keys(screenContextFormData).length > 0,
|
||||
screenContextKeys: Object.keys(screenContextFormData),
|
||||
hasPropsFormData: Object.keys(propsFormData).length > 0,
|
||||
propsFormDataKeys: Object.keys(propsFormData),
|
||||
splitPanelPosition,
|
||||
effectiveFormDataKeys: Object.keys(effectiveFormData),
|
||||
});
|
||||
|
||||
const context: ButtonActionContext = {
|
||||
formData: formData || {},
|
||||
formData: effectiveFormData,
|
||||
originalData: originalData, // 🔧 빈 객체 대신 undefined 유지 (UPDATE 판단에 사용)
|
||||
screenId: effectiveScreenId, // 🆕 ScreenContext에서 가져온 값 사용
|
||||
tableName: effectiveTableName, // 🆕 ScreenContext에서 가져온 값 사용
|
||||
@@ -996,6 +1029,9 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
flowSelectedStepId: _flowSelectedStepId, // 플로우 선택 스텝 ID 필터링
|
||||
onFlowRefresh: _onFlowRefresh, // 플로우 새로고침 콜백 필터링
|
||||
originalData: _originalData, // 부분 업데이트용 원본 데이터 필터링
|
||||
_originalData: __originalData, // DOM 필터링
|
||||
_initialData: __initialData, // DOM 필터링
|
||||
_groupedData: __groupedData, // DOM 필터링
|
||||
refreshKey: _refreshKey, // 필터링 추가
|
||||
isInModal: _isInModal, // 필터링 추가
|
||||
mode: _mode, // 필터링 추가
|
||||
@@ -1073,15 +1109,14 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
// 🔧 크기에 따른 패딩 조정
|
||||
padding:
|
||||
componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem",
|
||||
padding: componentConfig.size === "sm" ? "0 0.75rem" : componentConfig.size === "lg" ? "0 1.25rem" : "0 1rem",
|
||||
margin: "0",
|
||||
lineHeight: "1.25",
|
||||
boxShadow: finalDisabled ? "none" : "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
||||
// 디자인 모드와 인터랙티브 모드 모두에서 사용자 스타일 적용 (width/height 제외)
|
||||
...(component.style ? Object.fromEntries(
|
||||
Object.entries(component.style).filter(([key]) => key !== 'width' && key !== 'height')
|
||||
) : {}),
|
||||
...(component.style
|
||||
? Object.fromEntries(Object.entries(component.style).filter(([key]) => key !== "width" && key !== "height"))
|
||||
: {}),
|
||||
};
|
||||
|
||||
const buttonContent = processedConfig.text !== undefined ? processedConfig.text : component.label || "버튼";
|
||||
@@ -1103,7 +1138,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
<button
|
||||
type={componentConfig.actionType || "button"}
|
||||
disabled={finalDisabled}
|
||||
className="transition-colors duration-150 hover:opacity-90 active:scale-95 transition-transform"
|
||||
className="transition-colors transition-transform duration-150 hover:opacity-90 active:scale-95"
|
||||
style={buttonElementStyle}
|
||||
onClick={handleClick}
|
||||
onDragStart={onDragStart}
|
||||
|
||||
Reference in New Issue
Block a user