comparison src/theme.html.luan @ 0:8f4df159f06b

start public repo
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 11 Jul 2025 20:57:49 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:8f4df159f06b
1 local Luan = require "luan:Luan.luan"
2 local error = Luan.error
3 local pairs = Luan.pairs or error()
4 local parse = Luan.parse or error()
5 local Html = require "luan:Html.luan"
6 local html_encode = Html.encode or error()
7 local Io = require "luan:Io.luan"
8 local Http = require "luan:http/Http.luan"
9 local Shared = require "site:/lib/Shared.luan"
10 local head = Shared.head or error()
11 local body_header = Shared.body_header or error()
12 local footer = Shared.footer or error()
13 local fields = Shared.theme_fields or error()
14 local User = require "site:/lib/User.luan"
15 local get_background_img_url = User.get_background_img_url or error()
16 local Logging = require "luan:logging/Logging.luan"
17 local logger = Logging.logger "theme.html"
18
19
20 local google_explanation = [[All Google fonts use this URL format. You can change the font name and edit the font URL to use other Google fonts not listed here.]]
21
22 local fonts = {
23 ["Sans-Serif"] = {}
24 ["Times New Roman"] = {}
25 ["Optima"] = {}
26 ["Apple Chancery"] = {}
27 ["Courier"] = {}
28 ["Copperplate"] = {}
29 ["Bitter"] = {
30 title = "Google: Bitter"
31 url = "https://fonts.googleapis.com/css?family=Bitter"
32 explanation = google_explanation
33 }
34 ["Genos"] = {
35 title = "Google: Genos"
36 url = "https://fonts.googleapis.com/css?family=Genos"
37 explanation = google_explanation
38 }
39 ["Emblema One"] = {
40 title = "Google: Emblema One"
41 url = "https://fonts.googleapis.com/css?family=Emblema+One"
42 explanation = google_explanation
43 }
44 }
45 for name, font in pairs(fonts) do
46 font.name = name
47 font.title = font.title or name
48 font.url = font.url or ""
49 font.explanation = font.explanation or ""
50 end
51
52 local function color_input(user_data,color)
53 local value = user_data[color]
54 value = value and html_encode(value)
55 local default = fields[color]
56 local v = value or default
57 %>
58 <div>
59 <input type=color <%= v=="" and "" or [[value="]]..v..[["]] %> oninput="colorChange(this)">
60 <span color <%= v=="" and "" or [[style="background-color:]]..v..[["]] %> onclick="colorClick(this)"></span>
61 <input type=text name="<%=color%>" value="<%= value or "" %>" placeholder="<%=default%>" onchange="colorInputChange(this)">
62 </div>
63 <%
64 end
65
66 local function radio_input(user_data,name,value)
67 local current = user_data[name] or fields[name]
68 local checked = value==current and "checked" or ""
69 %><input type=radio name="<%=name%>" value="<%=value%>" <%=checked%> ><%
70 end
71
72 return function()
73 local user = User.current_required()
74 if user==nil then return end
75 local user_data = user.theme_data
76 user_data = user_data and parse(user_data) or {}
77 local message
78 Io.stdout = Http.response.text_writer()
79 %>
80 <!doctype html>
81 <html lang="en">
82 <head>
83 <% head() %>
84 <title>Link My Style</title>
85 <style>
86 <%
87 for _, font in pairs(fonts) do
88 if font.url ~= "" then
89 %>
90 @import "<%=font.url%>";
91 <%
92 end
93 %>
94 <%
95 end
96 %>
97
98 h1 {
99 text-align: center;
100 }
101 div[body] {
102 max-width: 600px;
103 margin-left: auto;
104 margin-right: auto;
105 }
106 @media (max-width: 700px) {
107 div[body] {
108 max-width: 90%;
109 }
110 }
111 label {
112 display: block;
113 }
114 form > div {
115 display: flex;
116 margin-top: 1px;
117 margin-bottom: 10px;
118 position: relative;
119 }
120 input[type="color"] {
121 /* hide this useless modern piece of junk */
122 position: absolute;
123 z-index: 1;
124 width: 42.5px;
125 height: 42.5px;
126 cursor: pointer;
127 opacity: 0;
128 }
129 span[color] {
130 width: 42.5px;
131 cursor: pointer;
132 border: 1px solid #E0E0E0;
133 }
134 form span[pulldown] {
135 border: 1px solid #E0E0E0;
136 display: flex;
137 align-items: center;
138 }
139 form span[pulldown] > span {
140 padding: 4px;
141 }
142 span[shape] a {
143 margin: 12px;
144 padding: 12px 100px;
145 border: 1px solid black;
146 }
147 span[shape] a:hover {
148 text-decoration: none;
149 }
150 input[type="text"] {
151 display: initial;
152 margin: 0;
153 border-radius: 0;
154 }
155 label[clickable] {
156 border: 1px solid grey;
157 padding: 4px;
158 padding-right: 8px;
159 }
160 label[white] {
161 color: white;
162 background-color: black;
163 }
164 div[background_img] {
165 gap: 8px;
166 }
167 button[background_img] {
168 width: 42.5px;
169 height: 42.5px;
170 padding: 0;
171 border: 1px solid #E0E0E0;
172 }
173 button[background_img] img {
174 display: block;
175 width: 100%;
176 height: 100%;
177 object-fit: cover;
178 }
179 div[background_img] button[small] {
180 flex-grow: 1;
181 background-color: #E0E0E0;
182 color: black;
183 }
184 div[background_img] button[small]:hover {
185 background-color: #C0C0C0;
186 }
187 div[font] {
188 gap: 8px;
189 }
190 div[font] input[name="font"] {
191 width: 33%;
192 }
193 div[font] input[name="font_url"] {
194 width: 66%;
195 }
196 div[font_explanation] {
197 color: #808080;
198 }
199 </style>
200 <script>
201 'use strict';
202
203 function colorClick(colorSpan) {
204 console.log('colorClick');
205 let colorInput = colorSpan.parentNode.querySelector('input[type="color"]');
206 colorInput.click();
207 }
208 function colorChange(colorInput) {
209 let parent = colorInput.parentNode;
210 let color = colorInput.value;
211 parent.querySelector('input[type="text"]').value = color;
212 parent.querySelector('span[color]').style['background-color'] = color;
213 }
214 function colorInputChange(input) {
215 let parent = input.parentNode;
216 let color = input.value || input.placeholder;
217 parent.querySelector('input[type="color"]').value = color;
218 parent.querySelector('span[color]').style['background-color'] = color;
219 }
220 function setField(name,value) {
221 document.querySelector('input[type="text"][name="'+name+'"]').value = value;
222 }
223 function setFont(font,url,explanation) {
224 setField('font',font);
225 setField('font_url',url);
226 document.querySelector('div[font_explanation]').textContent = explanation;
227 }
228
229 function uploaded(uuid,filename) {
230 document.querySelector('input[name="background_img_uuid"]').value = uuid;
231 document.querySelector('input[name="background_img_filename"]').value = filename;
232 document.querySelector('div[background_img] img').src = uploadcareUrl(uuid);
233 }
234 function clearImg() {
235 document.querySelector('input[name="background_img_uuid"]').value = '';
236 document.querySelector('input[name="background_img_filename"]').value = '';
237 document.querySelector('div[background_img] img').src = '/images/nothing.svg';
238 }
239 </script>
240 </head>
241 <body>
242 <div full>
243 <% body_header() %>
244 <h1>My Theme</h1>
245 <p top>Click the left side of every section to make quick changes or put exactly what you'd like in the input field. To remove a color or shadow, delete the text from the input field.</p>
246 <div body>
247 <form onsubmit="ajaxForm('/theme.js',this)" action="javascript:">
248 <label>Background Image</label>
249 <div background_img>
250 <button type=button background_img onclick="uploadcare.upload(uploaded)">
251 <img src="<%= get_background_img_url(user_data) or "/images/nothing.svg" %>" >
252 </button>
253 <input type=hidden name="background_img_uuid" value="<%= user_data.background_img_uuid or "" %>">
254 <input type=hidden name="background_img_filename" value="<%= user_data.background_img_filename or "" %>">
255 <button type=button small onclick="uploadcare.upload(uploaded)">Add</button>
256 <button type=button small onclick="clearImg()">Clear</button>
257 </div>
258 <label>Background Color</label>
259 <% color_input(user_data,"background_color") %>
260 <label>Button Color</label>
261 <% color_input(user_data,"link_background_color") %>
262 <label>Button Hover Color</label>
263 <% color_input(user_data,"link_hover_background_color") %>
264 <label>Button Text Color</label>
265 <% color_input(user_data,"link_text_color") %>
266 <label>Title Color</label>
267 <% color_input(user_data,"title_color") %>
268 <label>Bio Color</label>
269 <% color_input(user_data,"bio_color") %>
270 <label>Social Icon Color</label>
271 <div>
272 <label clickable black><% radio_input(user_data,"icon_color","") %>Black</label>
273 &nbsp; &nbsp;
274 <label clickable white><% radio_input(user_data,"icon_color","white") %>White</label>
275 </div>
276 <label>Button Shape</label>
277 <%
278 local field = "link_border_radius"
279 local value = user_data[field]
280 value = value and html_encode(value)
281 local default = fields[field]
282 local value_or_default = value or default
283 %>
284 <div>
285 <span shape pulldown>
286 <span onclick="clickMenu(this)">Select...</span>
287 <div pulldown_menu>
288 <a style="border-radius:22px / 50%" href="javascript:setField('<%=field%>','22px / 50%')">Link</a>
289 <a style="border-radius:12px / 50%" href="javascript:setField('<%=field%>','12px / 50%')">Link</a>
290 <a style="border-radius:10px" href="javascript:setField('<%=field%>','10px')">Link</a>
291 <a style="border-radius:0" href="javascript:setField('<%=field%>','0')">Link</a>
292 <a style="border-radius:10px 100px" href="javascript:setField('<%=field%>','10px 100px')">Link</a>
293 </div>
294 </span>
295 <input type=text name="<%=field%>" value="<%= value or "" %>" placeholder="<%=default%>">
296 </div>
297 <label>Button Border Color</label>
298 <% color_input(user_data,"link_border_color") %>
299 <label>Button Shadow</label>
300 <%
301 local field = "link_shadow"
302 local value = user_data[field]
303 value = value and html_encode(value)
304 local default = fields[field]
305 local value_or_default = value or default
306 %>
307 <div>
308 <span shape pulldown>
309 <span onclick="clickMenu(this)">Select...</span>
310 <div pulldown_menu>
311 <a style="box-shadow:5px 5px 2px 1px" href="javascript:setField('<%=field%>','5px 5px 2px 1px')">Link</a>
312 <a style="box-shadow:1px 1px 10px 1px" href="javascript:setField('<%=field%>','1px 1px 10px 1px')">Link</a>
313 </div>
314 </span>
315 <input type=text name="<%=field%>" value="<%= value or "" %>" placeholder="<%=default%>">
316 </div>
317 <label>Button Shadow Color</label>
318 <% color_input(user_data,"link_shadow_color") %>
319 <label>Font</label>
320 <%
321 local value = user_data.font
322 value = value and html_encode(value)
323 local value_url = user_data.font_url
324 value_url = value_url and html_encode(value_url)
325 local default = fields.font
326 local explanation = fonts[value] and fonts[value].explanation or ""
327 %>
328 <div font>
329 <span pulldown>
330 <span onclick="clickMenu(this)">Select...</span>
331 <div pulldown_menu>
332 <% for _, font in pairs(fonts) do %>
333 <a style="font-family:<%=font.name%>" href="javascript:setFont('<%=font.name%>','<%=font.url%>','<%=font.explanation%>')"><%=font.title%></a>
334 <% end %>
335 </div>
336 </span>
337 <input type=text name="font" value="<%= value or "" %>" placeholder="<%=default%>">
338 <input type=text name="font_url" value="<%= value_url or "" %>" placeholder="URL">
339 </div>
340 <div font_explanation><%=explanation%></div>
341 <button type=submit big>Save</button>
342 <%
343 local msg = message and message.info
344 %>
345 <div success><%= msg or "" %></div>
346 </form>
347 </div>
348 <% footer() %>
349 </div>
350 </body>
351 </html>
352 <%
353 end