테마 적용
This commit is contained in:
+44
-37
@@ -1070,8 +1070,8 @@ function EditorPageInner() {
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="h-screen flex flex-col overflow-hidden">
|
||||
<header className="border-b border-slate-800 px-6 py-4 flex items-center justify-between relative z-20 bg-slate-950/80 backdrop-blur">
|
||||
<main className="h-screen flex flex-col overflow-hidden bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-50">
|
||||
<header className="border-b border-slate-200 px-6 py-4 flex items-center justify-between relative z-20 bg-white/80 backdrop-blur dark:border-slate-800 dark:bg-slate-950/80">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-slate-900 border border-sky-700 shadow-sm">
|
||||
<Pencil className="w-4 h-4 text-sky-400" aria-hidden="true" />
|
||||
@@ -1233,12 +1233,12 @@ function EditorPageInner() {
|
||||
|
||||
{activeModal === "project" && (
|
||||
<div className="fixed inset-0 z-30 flex items-center justify-center bg-black/60">
|
||||
<div className="w-full max-w-md rounded-lg border border-slate-700 bg-slate-900 p-4 text-xs text-slate-100 shadow-xl">
|
||||
<div className="w-full max-w-md rounded-lg border border-slate-200 bg-white p-4 text-xs text-slate-900 shadow-xl dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-medium">프로젝트 저장 / 불러오기</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="text-slate-400 hover:text-slate-100 text-sm"
|
||||
className="text-slate-400 hover:text-slate-700 text-sm dark:hover:text-slate-100"
|
||||
onClick={() => setActiveModal(null)}
|
||||
>
|
||||
✕
|
||||
@@ -1246,17 +1246,17 @@ function EditorPageInner() {
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="flex flex-col gap-1">
|
||||
<span className="text-slate-400">프로젝트 제목</span>
|
||||
<span className="text-slate-600 dark:text-slate-400">프로젝트 제목</span>
|
||||
<input
|
||||
className="w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 text-xs outline-none focus:border-sky-500"
|
||||
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
value={projectConfig.title ?? ""}
|
||||
onChange={(e) => updateProjectConfig({ title: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1">
|
||||
<span className="text-slate-400">프로젝트 주소 (예: my-landing)</span>
|
||||
<span className="text-slate-600 dark:text-slate-400">프로젝트 주소 (예: my-landing)</span>
|
||||
<input
|
||||
className="w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 text-xs outline-none focus:border-sky-500"
|
||||
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
value={projectConfig.slug ?? ""}
|
||||
onChange={(e) => updateProjectConfig({ slug: e.target.value })}
|
||||
/>
|
||||
@@ -1264,21 +1264,21 @@ function EditorPageInner() {
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="flex-1 rounded border border-sky-700 bg-sky-950 px-2 py-1 text-sky-100 hover:bg-sky-900"
|
||||
className="flex-1 rounded border border-sky-500 bg-sky-50 px-2 py-1 text-sky-700 hover:bg-sky-100 dark:border-sky-700 dark:bg-sky-950 dark:text-sky-100 dark:hover:bg-sky-900"
|
||||
onClick={handleSaveProject}
|
||||
>
|
||||
저장 (로컬 + 서버)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-100 hover:bg-slate-800"
|
||||
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
|
||||
onClick={handleLoadProject}
|
||||
>
|
||||
불러오기
|
||||
</button>
|
||||
</div>
|
||||
{projectMessage && (
|
||||
<p className="text-[11px] text-slate-300 mt-1">{projectMessage}</p>
|
||||
<p className="text-[11px] text-slate-600 mt-1 dark:text-slate-300">{projectMessage}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1289,12 +1289,17 @@ function EditorPageInner() {
|
||||
|
||||
{activeModal === "json" && (
|
||||
<div className="fixed inset-0 z-30 flex items-center justify-center bg-black/60">
|
||||
<div className="w-full max-w-2xl rounded-lg border border-slate-700 bg-slate-900 p-4 text-xs text-slate-100 shadow-xl">
|
||||
<div className="w-full max-w-2xl rounded-lg border border-slate-200 bg-white p-4 text-xs text-slate-900 shadow-xl dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-medium">JSON Export / Import <span className="text-xs text-slate-400">* 에디터 내용을 가져오거나 내보낼 수 있습니다.</span></h3>
|
||||
<h3 className="text-sm font-medium">
|
||||
JSON Export / Import
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
||||
* 에디터 내용을 가져오거나 내보낼 수 있습니다.
|
||||
</span>
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="text-slate-400 hover:text-slate-100 text-sm"
|
||||
className="text-slate-400 hover:text-slate-700 text-sm dark:hover:text-slate-100"
|
||||
onClick={() => setActiveModal(null)}
|
||||
>
|
||||
✕
|
||||
@@ -1304,14 +1309,14 @@ function EditorPageInner() {
|
||||
<div className="flex gap-2 mb-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 hover:bg-slate-800"
|
||||
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
|
||||
onClick={handleExportJson}
|
||||
>
|
||||
JSON 내보내기
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-red-700 bg-red-950 px-2 py-1 text-red-100 hover:bg-red-900"
|
||||
className="rounded border border-red-300 bg-white px-2 py-1 text-red-700 hover:bg-red-50 dark:border-red-700 dark:bg-red-950 dark:text-red-100 dark:hover:bg-red-900"
|
||||
onClick={handleClearCanvas}
|
||||
>
|
||||
캔버스 초기화
|
||||
@@ -1319,23 +1324,23 @@ function EditorPageInner() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<label className="flex flex-col gap-1">
|
||||
<span className="text-slate-400">에디터 상태 JSON</span>
|
||||
<span className="text-slate-600 dark:text-slate-400">에디터 상태 JSON</span>
|
||||
<textarea
|
||||
className="w-full h-48 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[10px] font-mono outline-none focus:border-sky-500"
|
||||
className="w-full h-48 rounded border border-slate-300 bg-white px-2 py-1 text-[10px] font-mono text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
value={exportJson}
|
||||
readOnly
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1">
|
||||
<span className="text-slate-400">JSON에서 불러오기</span>
|
||||
<span className="text-slate-600 dark:text-slate-400">JSON에서 불러오기</span>
|
||||
<textarea
|
||||
className="w-full h-48 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[10px] font-mono outline-none focus:border-sky-500"
|
||||
className="w-full h-48 rounded border border-slate-300 bg-white px-2 py-1 text-[10px] font-mono text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
value={importJson}
|
||||
onChange={(e) => setImportJson(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-1 w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 hover:bg-slate-800"
|
||||
className="mt-1 w-full rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
|
||||
onClick={handleApplyImportJson}
|
||||
>
|
||||
JSON 적용하기
|
||||
@@ -1509,8 +1514,10 @@ function SortableEditorBlock({
|
||||
data-selected={isSelected ? "true" : "false"}
|
||||
aria-selected={isSelected ? "true" : "false"}
|
||||
className={`rounded border px-3 py-2 cursor-text transition-colors ${alignClass} ${sizeClass} ${leadingClass} ${weightClass} ${colorClass} ${maxWidthClass} ${decorationClass} ${
|
||||
isSelected ? "border-sky-500 bg-slate-900/80" : "border-slate-700 bg-slate-900"
|
||||
}`}
|
||||
isSelected
|
||||
? "border-sky-500 bg-sky-50 text-slate-900 dark:bg-slate-900/80 dark:text-slate-100"
|
||||
: "border-slate-200 bg-white text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
|
||||
}`}
|
||||
onClick={(event) => {
|
||||
// 멀티 선택(MVP): Cmd/Ctrl+클릭 시 선택 토글, 그 외에는 단일 선택으로 전환한다.
|
||||
const isMeta = event.metaKey || event.ctrlKey;
|
||||
@@ -1541,7 +1548,7 @@ function SortableEditorBlock({
|
||||
<div className="flex items-start gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="mt-0.5 h-4 w-4 rounded border border-slate-700 bg-slate-900 text-[10px] flex items-center justify-center text-slate-400 hover:bg-slate-800"
|
||||
className="mt-0.5 h-4 w-4 rounded border border-slate-300 bg-slate-50 text-[10px] flex items-center justify-center text-slate-500 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400 dark:hover:bg-slate-800"
|
||||
aria-label="블록 드래그 핸들"
|
||||
{...listeners}
|
||||
{...attributes}
|
||||
@@ -1610,7 +1617,7 @@ function SortableEditorBlock({
|
||||
className="h-4 w-auto"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-slate-200 shrink-0">{inputProps.label}</span>
|
||||
<span className="shrink-0">{inputProps.label}</span>
|
||||
)
|
||||
)}
|
||||
{(() => {
|
||||
@@ -1715,7 +1722,7 @@ function SortableEditorBlock({
|
||||
className="h-4 w-auto"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-slate-200">{selectProps.label}</span>
|
||||
<span>{selectProps.label}</span>
|
||||
)}
|
||||
<div className="w-full">
|
||||
<select
|
||||
@@ -1792,14 +1799,14 @@ function SortableEditorBlock({
|
||||
className="inline-block max-w-full h-auto"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-slate-200">{radioProps.groupLabel}</span>
|
||||
<span>{radioProps.groupLabel}</span>
|
||||
)}
|
||||
<div style={optionsStyle} className={optionsLayoutClass}>
|
||||
{options.map((opt) => (
|
||||
<label key={opt.value} className="pb-form-option">
|
||||
<input
|
||||
type="radio"
|
||||
className="h-4 w-4 border-slate-700 bg-slate-900 text-sky-500"
|
||||
className="h-4 w-4"
|
||||
name={radioProps.formFieldName}
|
||||
value={opt.value}
|
||||
readOnly
|
||||
@@ -1863,8 +1870,8 @@ function SortableEditorBlock({
|
||||
: { rowGap: `${optionGapPx}px` }
|
||||
: {};
|
||||
const groupContainerClassName = isInlineLayout
|
||||
? "flex flex-row items-center text-xs text-slate-200"
|
||||
: "flex flex-col gap-1 text-xs text-slate-200";
|
||||
? "flex flex-row items-center text-xs"
|
||||
: "flex flex-col gap-1 text-xs";
|
||||
|
||||
return (
|
||||
<div className={groupContainerClassName} style={groupContainerStyle}>
|
||||
@@ -1877,14 +1884,14 @@ function SortableEditorBlock({
|
||||
className="inline-block max-w-full h-auto"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-slate-200">{checkboxProps.groupLabel}</span>
|
||||
<span>{checkboxProps.groupLabel}</span>
|
||||
)}
|
||||
<div className={optionsLayoutClass} style={optionsStyle}>
|
||||
{options.map((opt) => (
|
||||
<label key={opt.value} className="pb-form-option">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-4 w-4 rounded border-slate-700 bg-slate-900 text-sky-500"
|
||||
className="h-4 w-4 rounded"
|
||||
name={checkboxProps.formFieldName}
|
||||
value={opt.value}
|
||||
readOnly
|
||||
@@ -2050,7 +2057,7 @@ function SortableEditorBlock({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`w-full border border-dashed border-slate-700 bg-slate-900/40 rounded flex items-center overflow-hidden ${alignClass}`}
|
||||
className={`w-full border border-dashed border-slate-700 bg-slate-500/40 rounded flex items-center overflow-hidden ${alignClass}`}
|
||||
>
|
||||
{hasSrc ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
@@ -2061,7 +2068,7 @@ function SortableEditorBlock({
|
||||
style={{ ...containerStyle, ...imageStyle }}
|
||||
/>
|
||||
) : (
|
||||
<span className="text-[10px] text-slate-500 px-2 py-4">
|
||||
<span className="text-[10px] text-slate-900 dark:text-slate-500 px-2 py-4">
|
||||
이미지 URL 을 입력하거나 파일을 업로드하면 여기에서 미리보기가 표시됩니다.
|
||||
</span>
|
||||
)}
|
||||
@@ -2162,7 +2169,7 @@ function SortableEditorBlock({
|
||||
style={index < nodes.length - 1 ? { marginBottom: gapPx } : undefined}
|
||||
>
|
||||
<span className="break-words align-middle">{node.text}</span>
|
||||
<span className="ml-2 inline-flex items-center gap-1 text-[10px] text-slate-300 align-middle">
|
||||
<span className="ml-2 inline-flex items-center gap-1 text-[10px] text-slate-900 dark:text-slate-300 align-middle">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-slate-700 px-1 py-0.5 hover:bg-slate-800"
|
||||
@@ -2367,13 +2374,13 @@ function ColumnDroppable({ id, sectionId, columnId, basis, blocks, renderBlocks,
|
||||
style={{ flexBasis: basis }}
|
||||
>
|
||||
<div
|
||||
className={`border rounded flex flex-col gap-2 items-stretch justify-start px-2 py-2 bg-slate-950/40 ${
|
||||
className={`border rounded flex flex-col gap-2 items-stretch justify-start px-2 py-2 bg-slate-100/40 dark:bg-slate-950/40 ${
|
||||
isActiveColumn ? "border-sky-500" : "border-slate-800/80"
|
||||
}`}
|
||||
style={{}}
|
||||
>
|
||||
{blocks.length === 0 ? (
|
||||
<span className="text-[11px] px-2 text-center text-slate-500">
|
||||
<span className="text-[11px] px-2 text-center text-slate-900 dark:text-slate-300">
|
||||
{`컬럼 영역 (span ${span}/12)`}
|
||||
</span>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user