Skip to content

Commit 8724127

Browse files
authored
Merge pull request #5 from pythonkr/feature/cms-nodejs
feat: Section body 부분 nojs preview 추가
2 parents 2432bb7 + 6e4d33a commit 8724127

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

app/cms/admin.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
from cms.models import Page, Section, Sitemap
2+
from django import forms
3+
from django.contrib import admin
4+
from django.utils.html import format_html
5+
6+
_CODE_PREVIEW_TEMPLATE = """
7+
<style>
8+
/* ── 래퍼 기본 ── */
9+
.code-preview-wrapper {{
10+
display: flex;
11+
flex-direction: column;
12+
width: 800px;
13+
gap: 8px;
14+
background: #fff;
15+
}}
16+
.code-preview-wrapper > textarea {{ display: none !important; }}
17+
18+
/* ── 툴바 ── */
19+
.cp-toolbar {{
20+
display: flex;
21+
align-items: center;
22+
gap: 6px;
23+
padding: 4px;
24+
background: #333;
25+
border-bottom: 1px solid #444;
26+
position: relative;
27+
z-index: 2001; /* admin nav 위 */
28+
}}
29+
.cp-toolbar button {{
30+
padding: 4px 8px;
31+
font-size: 13px;
32+
border: 1px solid #555;
33+
background: #444;
34+
color: #fff;
35+
cursor: pointer;
36+
}}
37+
.cp-toolbar .preview-toggle {{ display: inline-block; }}
38+
.cp-toolbar .lang-toggle,
39+
.cp-toolbar .theme-toggle {{ display: none !important; }}
40+
.code-preview-wrapper.fullscreen .cp-toolbar .lang-toggle,
41+
.code-preview-wrapper.fullscreen .cp-toolbar .theme-toggle {{
42+
display: inline-block !important;
43+
}}
44+
.lang-toggle.js-mode.active {{ background: #f1c40f; border-color: #d4ac0d; color: #333; }}
45+
.lang-toggle.ts-mode.active {{ background: #3498db; border-color: #2e86c1; color: #fff; }}
46+
.theme-toggle.active {{ background: #888; border-color: #666; color: #fff; }}
47+
48+
/* ── 에디터·프리뷰 컨테이너 ── */
49+
.cp-main {{
50+
display: flex;
51+
flex-direction: column;
52+
gap: 8px;
53+
min-height: 0;
54+
}}
55+
.code-preview-wrapper:not(.fullscreen) .cp-main {{
56+
display: none !important;
57+
}}
58+
59+
/* 에디터 블록 */
60+
.editor-block {{
61+
flex: 1;
62+
min-height: 0;
63+
position: relative;
64+
}}
65+
.editor-block .CodeMirror,
66+
.editor-block .CodeMirror-scroll {{
67+
height: 100% !important;
68+
overflow: auto !important;
69+
}}
70+
71+
/* 프리뷰 iframe */
72+
.cp-main iframe {{
73+
flex: 1;
74+
min-height: 0;
75+
overflow: auto;
76+
}}
77+
78+
/* ── 풀스크린 모드 ── */
79+
.code-preview-wrapper.fullscreen {{
80+
position: fixed !important;
81+
top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
82+
width: 100vw !important;
83+
height: 100vh !important;
84+
margin: 0; padding: 0;
85+
display: flex;
86+
flex-direction: column;
87+
background: #fff;
88+
z-index: 2000;
89+
}}
90+
.code-preview-wrapper.fullscreen .cp-main {{
91+
flex: 1;
92+
display: flex;
93+
flex-direction: row;
94+
min-height: 0;
95+
}}
96+
.code-preview-wrapper.fullscreen .editor-block {{
97+
max-height: none !important;
98+
}}
99+
.code-preview-wrapper.fullscreen .cp-toolbar {{
100+
flex-shrink: 0;
101+
}}
102+
103+
/* ── 다크모드 ── */
104+
.dark-editor .CodeMirror {{ background: #2d2d2d !important; color: #ccc; }}
105+
.dark-preview iframe {{ background: #2d2d2d; }}
106+
</style>
107+
108+
<div class="code-preview-wrapper" id="cpw_{name}">
109+
<div class="cp-toolbar">
110+
<button type="button" class="preview-toggle">Preview Mode</button>
111+
<button type="button" class="lang-toggle js-mode active" data-lang="js">JS</button>
112+
<button type="button" class="lang-toggle ts-mode" data-lang="ts">TS</button>
113+
<button type="button" class="theme-toggle" data-target="editor">Editor Dark</button>
114+
<button type="button" class="theme-toggle" data-target="preview">Preview Dark</button>
115+
</div>
116+
{ta}
117+
<div class="cp-main">
118+
<div class="editor-block" id="editor_{name}"></div>
119+
<iframe id="preview_{name}"></iframe>
120+
</div>
121+
</div>
122+
"""
123+
124+
125+
class CodeEditorWidget(forms.Textarea):
126+
class Media:
127+
css = {
128+
"all": (
129+
"https://unpkg.com/codemirror@5.65.5/lib/codemirror.css",
130+
"https://unpkg.com/codemirror@5.65.5/theme/dracula.css",
131+
)
132+
}
133+
js = (
134+
"https://unpkg.com/codemirror@5.65.5/lib/codemirror.js",
135+
"https://unpkg.com/codemirror@5.65.5/mode/javascript/javascript.js",
136+
"https://unpkg.com/@babel/standalone/babel.min.js",
137+
"/static/admin/js/editor.js",
138+
)
139+
140+
def render(self, name, value, attrs=None, renderer=None):
141+
ta = super().render(name, value, attrs, renderer)
142+
return format_html(_CODE_PREVIEW_TEMPLATE, name=name, ta=ta)
143+
144+
145+
class SectionAdminForm(forms.ModelForm):
146+
class Meta:
147+
model = Section
148+
fields = "__all__"
149+
widgets = {"body": CodeEditorWidget()}
150+
151+
152+
@admin.register(Section)
153+
class SectionAdmin(admin.ModelAdmin):
154+
form = SectionAdminForm
155+
156+
157+
admin.site.register(Page)
158+
admin.site.register(Sitemap)

0 commit comments

Comments
 (0)