Mercurial Hosting > linkmystyle
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 | |
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 |