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 |
