feat: add next-themes package and update styles

- Added `next-themes` package for theme management.
- Updated various components to use `hsl(var(--foreground))` for color consistency.
- Changed background colors from `bg-white` to `bg-card` in multiple components for better theming support.

Made-with: Cursor
This commit is contained in:
DDD1542
2026-03-10 15:24:05 +09:00
parent 42673f57a0
commit fa6f76bff1
16 changed files with 168 additions and 54 deletions

View File

@@ -1474,7 +1474,7 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
>
<div
data-screen-runtime="true"
className="relative m-auto bg-white"
className="relative m-auto bg-card"
style={{
width: screenDimensions?.width || 800,
// 조건부 레이어가 활성화되면 높이 자동 확장

View File

@@ -2217,7 +2217,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
const labelStyle: React.CSSProperties = {
fontSize: component.style?.labelFontSize || "14px",
color: component.style?.labelColor || "#212121",
color: component.style?.labelColor || "hsl(var(--foreground))",
fontWeight: component.style?.labelFontWeight || "500",
backgroundColor: component.style?.labelBackgroundColor || "transparent",
padding: component.style?.labelPadding || "0",

View File

@@ -1288,7 +1288,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
className="text-sm font-medium leading-none"
style={{
fontSize: style?.labelFontSize || "14px",
color: style?.labelColor || "#212121",
color: style?.labelColor || "hsl(var(--foreground))",
fontWeight: style?.labelFontWeight || "500",
...(isHorizLabel ? { whiteSpace: "nowrap" as const, display: "flex", alignItems: "center" } : {}),
...(labelPos === "bottom" ? { marginTop: style?.labelMarginBottom || "4px" } : {}),
@@ -1344,7 +1344,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
? { right: "100%", marginRight: labelGapValue }
: { left: "100%", marginLeft: labelGapValue }),
fontSize: style?.labelFontSize || "14px",
color: style?.labelColor || "#212121",
color: style?.labelColor || "hsl(var(--foreground))",
fontWeight: style?.labelFontWeight || "500",
whiteSpace: "nowrap",
}}

View File

@@ -221,7 +221,7 @@ export const ScreenNode: React.FC<{ data: ScreenNodeData }> = ({ data }) => {
</div>
{/* 화면 미리보기 영역 (컴팩트) */}
<div className="h-[140px] overflow-hidden bg-slate-50 p-2">
<div className="h-[140px] overflow-hidden bg-muted/50 p-2">
{layoutSummary ? (
<ScreenPreview layoutSummary={layoutSummary} screenType={screenType} />
) : (
@@ -233,11 +233,11 @@ export const ScreenNode: React.FC<{ data: ScreenNodeData }> = ({ data }) => {
</div>
{/* 필드 매핑 영역 */}
<div className="flex-1 overflow-hidden border-t border-slate-200 bg-white px-2 py-1.5">
<div className="mb-1 flex items-center gap-1 text-[9px] font-medium text-slate-500">
<div className="flex-1 overflow-hidden border-t border-border bg-card px-2 py-1.5">
<div className="mb-1 flex items-center gap-1 text-[9px] font-medium text-muted-foreground">
<Columns3 className="h-3 w-3" />
<span> </span>
<span className="ml-auto text-[8px] text-slate-400">
<span className="ml-auto text-[8px] text-muted-foreground/70">
{layoutSummary?.layoutItems?.filter(i => i.label && !i.componentKind?.includes('button')).length || 0}
</span>
</div>
@@ -319,7 +319,7 @@ const ScreenPreview: React.FC<{ layoutSummary: ScreenLayoutSummary; screenType:
// 그리드 화면 일러스트
if (screenType === "grid") {
return (
<div className="flex h-full flex-col gap-2 rounded-lg border border-slate-200 bg-muted/30 p-3">
<div className="flex h-full flex-col gap-2 rounded-lg border border-border bg-muted/30 p-3">
{/* 상단 툴바 */}
<div className="flex items-center gap-2">
<div className="h-4 w-16 rounded bg-pink-400/80 shadow-sm" />
@@ -337,19 +337,19 @@ const ScreenPreview: React.FC<{ layoutSummary: ScreenLayoutSummary; screenType:
{/* 테이블 행들 */}
<div className="flex flex-1 flex-col gap-1 overflow-hidden">
{[...Array(7)].map((_, i) => (
<div key={i} className={`flex gap-1 rounded px-2 py-1.5 ${i % 2 === 0 ? "bg-slate-100" : "bg-white"}`}>
<div key={i} className={`flex gap-1 rounded px-2 py-1.5 ${i % 2 === 0 ? "bg-muted" : "bg-card"}`}>
{[...Array(5)].map((_, j) => (
<div key={j} className="h-2 flex-1 rounded bg-slate-300/70" />
<div key={j} className="h-2 flex-1 rounded bg-muted-foreground/30" />
))}
</div>
))}
</div>
{/* 페이지네이션 */}
<div className="flex items-center justify-center gap-2 pt-1">
<div className="h-2.5 w-4 rounded bg-slate-300" />
<div className="h-2.5 w-4 rounded bg-muted-foreground/40" />
<div className="h-2.5 w-4 rounded bg-primary" />
<div className="h-2.5 w-4 rounded bg-slate-300" />
<div className="h-2.5 w-4 rounded bg-slate-300" />
<div className="h-2.5 w-4 rounded bg-muted-foreground/40" />
<div className="h-2.5 w-4 rounded bg-muted-foreground/40" />
</div>
{/* 컴포넌트 수 */}
<div className="absolute bottom-2 right-2 rounded-md bg-slate-800/80 px-2 py-1 text-[10px] font-medium text-white shadow-sm">
@@ -362,17 +362,17 @@ const ScreenPreview: React.FC<{ layoutSummary: ScreenLayoutSummary; screenType:
// 폼 화면 일러스트
if (screenType === "form") {
return (
<div className="flex h-full flex-col gap-3 rounded-lg border border-slate-200 bg-muted/30 p-3">
<div className="flex h-full flex-col gap-3 rounded-lg border border-border bg-muted/30 p-3">
{/* 폼 필드들 */}
{[...Array(6)].map((_, i) => (
<div key={i} className="flex items-center gap-3">
<div className="h-2.5 w-14 rounded bg-slate-400" />
<div className="h-5 flex-1 rounded-md border border-slate-300 bg-white shadow-sm" />
<div className="h-2.5 w-14 rounded bg-muted-foreground/50" />
<div className="h-5 flex-1 rounded-md border border-border bg-card shadow-sm" />
</div>
))}
{/* 버튼 영역 */}
<div className="mt-auto flex justify-end gap-2 border-t border-slate-100 pt-3">
<div className="h-5 w-14 rounded-md bg-slate-300 shadow-sm" />
<div className="mt-auto flex justify-end gap-2 border-t border-border pt-3">
<div className="h-5 w-14 rounded-md bg-muted-foreground/40 shadow-sm" />
<div className="h-5 w-14 rounded-md bg-primary shadow-sm" />
</div>
{/* 컴포넌트 수 */}
@@ -386,7 +386,7 @@ const ScreenPreview: React.FC<{ layoutSummary: ScreenLayoutSummary; screenType:
// 대시보드 화면 일러스트
if (screenType === "dashboard") {
return (
<div className="grid h-full grid-cols-2 gap-2 rounded-lg border border-slate-200 bg-muted/30 p-3">
<div className="grid h-full grid-cols-2 gap-2 rounded-lg border border-border bg-muted/30 p-3">
{/* 카드/차트들 */}
<div className="rounded-lg bg-emerald-100 p-2 shadow-sm">
<div className="mb-2 h-2.5 w-10 rounded bg-emerald-400" />
@@ -419,15 +419,15 @@ const ScreenPreview: React.FC<{ layoutSummary: ScreenLayoutSummary; screenType:
// 액션 화면 일러스트 (버튼 중심)
if (screenType === "action") {
return (
<div className="flex h-full flex-col items-center justify-center gap-4 rounded-lg border border-slate-200 bg-muted/30 p-3">
<div className="rounded-full bg-slate-100 p-4 text-slate-400">
<div className="flex h-full flex-col items-center justify-center gap-4 rounded-lg border border-border bg-muted/30 p-3">
<div className="rounded-full bg-muted p-4 text-muted-foreground">
<MousePointer2 className="h-10 w-10" />
</div>
<div className="flex gap-3">
<div className="h-7 w-16 rounded-md bg-primary shadow-sm" />
<div className="h-7 w-16 rounded-md bg-slate-300 shadow-sm" />
<div className="h-7 w-16 rounded-md bg-muted-foreground/40 shadow-sm" />
</div>
<div className="text-xs font-medium text-slate-400"> </div>
<div className="text-xs font-medium text-muted-foreground"> </div>
{/* 컴포넌트 수 */}
<div className="absolute bottom-2 right-2 rounded-md bg-slate-800/80 px-2 py-1 text-[10px] font-medium text-white shadow-sm">
{totalComponents}
@@ -695,7 +695,7 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
if (filterRefs.length === 0 && lookupRefs.length === 0) return null;
return (
<div className="flex items-center gap-1.5 px-2 py-1 mb-1.5 rounded border border-slate-300 bg-slate-50 text-[9px]">
<div className="flex items-center gap-1.5 px-2 py-1 mb-1.5 rounded border border-border bg-muted text-[9px]">
{/* 필터 뱃지 */}
{filterRefs.length > 0 && (
<span
@@ -861,7 +861,7 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
export const LegacyScreenNode = ScreenNode;
export const AggregateNode: React.FC<{ data: any }> = ({ data }) => {
return (
<div className="rounded-lg border-2 border-purple-300 bg-white p-3 shadow-lg">
<div className="rounded-lg border-2 border-purple-300 bg-card p-3 shadow-lg">
<Handle type="target" position={Position.Left} id="left" className="!h-3 !w-3 !bg-purple-500" />
<Handle type="source" position={Position.Right} id="right" className="!h-3 !w-3 !bg-purple-500" />
<div className="flex items-center gap-2 text-purple-600">

View File

@@ -690,7 +690,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
type: "smoothstep",
label: `${i + 1}`,
labelStyle: { fontSize: 11, fill: "#0ea5e9", fontWeight: 600 },
labelBgStyle: { fill: "white", stroke: "#e2e8f0", strokeWidth: 1 },
labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
labelBgPadding: [4, 2] as [number, number],
markerEnd: { type: MarkerType.ArrowClosed, color: "#0ea5e9" },
animated: true,
@@ -1007,7 +1007,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
type: "smoothstep",
label: rel.relation_type === "join" ? "조인" : rel.crud_operations || "",
labelStyle: { fontSize: 9, fill: "#10b981" },
labelBgStyle: { fill: "white", stroke: "#e2e8f0", strokeWidth: 1 },
labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
labelBgPadding: [3, 2] as [number, number],
style: { stroke: "#10b981", strokeWidth: 1.5 },
});
@@ -1030,7 +1030,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
animated: true,
label: flow.flow_label || flow.flow_type || "이동",
labelStyle: { fontSize: 10, fill: "#8b5cf6", fontWeight: 500 },
labelBgStyle: { fill: "white", stroke: "#e2e8f0", strokeWidth: 1 },
labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
labelBgPadding: [4, 2] as [number, number],
markerEnd: { type: MarkerType.ArrowClosed, color: "#8b5cf6" },
style: { stroke: "#8b5cf6", strokeWidth: 2 },
@@ -2333,7 +2333,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
maxZoom={1.5}
proOptions={{ hideAttribution: true }}
>
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#e2e8f0" />
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color="hsl(var(--border))" />
<Controls position="bottom-right" />
</ReactFlow>
</div>

View File

@@ -404,7 +404,7 @@ export function ScreenSettingModal({
{/* 2컬럼 레이아웃: 왼쪽 탭(좁게) + 오른쪽 프리뷰(넓게) */}
<div className="flex min-h-0 flex-1 gap-3">
{/* 왼쪽: 탭 컨텐츠 (40%) */}
<div className="flex min-h-0 w-[40%] flex-col rounded-lg border bg-white">
<div className="flex min-h-0 w-[40%] flex-col rounded-lg border bg-card">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
@@ -511,7 +511,7 @@ export function ScreenSettingModal({
</div>
{/* 오른쪽: 화면 프리뷰 (60%, 항상 표시) */}
<div className="flex min-h-0 w-[60%] flex-col overflow-hidden rounded-lg border bg-white">
<div className="flex min-h-0 w-[60%] flex-col overflow-hidden rounded-lg border bg-card">
<PreviewTab
screenId={currentScreenId}
screenName={currentScreenName}
@@ -926,7 +926,7 @@ function TableColumnAccordion({
{/* 펼쳐진 내용 */}
{isOpen && (
<div className={`border-t border-${themeColor}-100 p-3 space-y-3 bg-white/50`}>
<div className={`border-t border-${themeColor}-100 p-3 space-y-3 bg-card/50`}>
{/* 필터 연결 정보 (필터 테이블만) */}
{!isMain && filterKeyMapping && (
<div className="flex items-center gap-1 text-xs">
@@ -2529,7 +2529,7 @@ function FieldMappingTab({
{componentColumns.map((comp, idx) => (
<div
key={idx}
className="rounded-lg border bg-white overflow-hidden"
className="rounded-lg border bg-card overflow-hidden"
>
{/* 컴포넌트 헤더 */}
<div className="flex items-center justify-between px-3 py-2 bg-muted border-b">
@@ -4282,7 +4282,7 @@ function ControlManagementTab({
);
const tableName = (flow as any).tableType || (flow as any).tableName;
return (
<div key={`flow-list-${flow.flowId}`} className="flex items-center gap-2 px-2 py-1.5 rounded bg-white/50 hover:bg-white">
<div key={`flow-list-${flow.flowId}`} className="flex items-center gap-2 px-2 py-1.5 rounded bg-card/50 hover:bg-card">
{/* 플로우 이름 - 일반 텍스트 */}
<Workflow className="h-3.5 w-3.5 text-purple-500 shrink-0" />
<span className="text-xs font-medium text-foreground shrink-0">
@@ -4744,7 +4744,7 @@ function PreviewTab({ screenId, screenName, companyCode, iframeKey = 0, canvasWi
className="relative min-h-0 flex-1 overflow-hidden flex items-center justify-center bg-muted"
>
{loading && (
<div className="absolute inset-0 z-10 flex items-center justify-center bg-white/80">
<div className="absolute inset-0 z-10 flex items-center justify-center bg-background/80">
<div className="text-center">
<Loader2 className="mx-auto h-8 w-8 animate-spin text-primary" />
<p className="mt-2 text-sm text-muted-foreground"> ...</p>
@@ -4797,7 +4797,7 @@ function PreviewTab({ screenId, screenName, companyCode, iframeKey = 0, canvasWi
key={iframeKey}
id="screen-preview-iframe"
src={previewUrl}
className="border-0 shadow-lg rounded bg-white pointer-events-none"
className="border-0 shadow-lg rounded bg-card pointer-events-none"
style={{
width: "100%",
height: "100%",