[READ-ONLY] Mirror of https://github.com/probablykasper/lumix. Art website (unfinished)
art website
0

Configure Feed

Select the types of activity you want to include in your feed.

Register/login/logout works, started work on upload page

KH (Mar 4, 2018, 9:35 PM +0100) b6a6201c 7095cd23

+342 -169
+130 -10
web/js/forms.js
··· 1 + import qq from "fine-uploader/lib/core/traditional"; 1 2 // register form 2 - if (page == "register") { 3 - $("button.register").on("click", () => { 3 + fold("register form", () => { 4 + 5 + function submitRegisterForm() { 4 6 const req = { 5 7 displayname: $(".register-form input.displayname").val(), 6 8 username: $(".register-form input.username").val(), 7 9 email: $(".register-form input.email").val(), 8 10 password: $(".register-form input.password").val(), 9 - } 11 + }; 10 12 xhr(req, "/register", (res, err) => { 11 13 if (err); // http status code not 2xx 12 14 console.log(res); 15 + if (res.errors.length == 0) { 16 + window.location = "/login"; 17 + } 13 18 }); 19 + } 20 + 21 + // register button click 22 + $("body").on("click", "button.register", () => { 23 + submitRegisterForm(); 14 24 }); 15 - } 25 + 26 + // enter to register 27 + $("body").on("keypress", ".register-form input", (e) => { 28 + if (e.which == 13) submitRegisterForm(); // enter 29 + }); 30 + 31 + }); 16 32 17 - // login form 18 - if (page == "login") { 19 - $("button.login").on("click", () => { 33 + fold("login form", () => { 34 + 35 + function submitLoginForm() { 20 36 const req = { 21 37 email: $(".login-form input.email").val(), 22 38 password: $(".login-form input.password").val(), 23 - } 24 - xhr(req, "/login", (res, err) => { 39 + }; 40 + xhr(req, `/login${window.location.search}`, (res, err) => { 25 41 if (err); // http status code not 2xx 26 42 console.log(res); 43 + if (res.errors.length == 0) { 44 + window.location = res.redirect; 45 + } 27 46 }); 47 + } 48 + 49 + // login button click 50 + $("body").on("click", ".login-form button.login", () => { 51 + submitLoginForm(); 28 52 }); 29 - } 53 + 54 + // enter to login 55 + $("body").on("keypress", ".login-form input", (e) => { 56 + if (e.which == 13) submitLoginForm(); // enter 57 + }); 58 + 59 + }); 60 + 61 + fold("upload form", () => { 62 + 63 + function submitUploadForm() { 64 + const req = { 65 + title: $(".upload-form input.title").val(), 66 + description: $(".upload-form textarea.description").val(), 67 + }; 68 + // xhr(req, "/upload", (res, err) => { 69 + // if (err); // http status code not 2xx 70 + // console.log(res); 71 + // if (res.errors.length == 0) { 72 + // window.lcation = "???" 73 + // } 74 + // }); 75 + } 76 + 77 + fold("file upload", () => { 78 + 79 + // click "select file" button opens file select dialog 80 + $("body").on("click", ".upload-form button.files", () => { 81 + $(".upload-form input#files").click(); 82 + }); 83 + 84 + function containsAFile(e) { 85 + const dtTypes = e.originalEvent.dataTransfer.types; 86 + console.log(e.originalEvent.dataTransfer.files); 87 + // if (dtTypes.length == 1 && dtTypes[0] == "Files") { 88 + // return true; 89 + // } 90 + // return false; 91 + } 92 + 93 + // setup 94 + const setupEvents = "drag dragstart dragend dragover dragenter dragleave drop"; 95 + $(window).on(setupEvents, (e) => { 96 + console.log(containsAFile(e)); 97 + if (containsAFile(e)) { 98 + e.preventDefault(); 99 + e.stopPropagation(); 100 + } 101 + }); 102 + 103 + // const qq = require("fine-uploader/fine-uploader/fine-uploader.core.js"); 104 + const uploader = new qq.FineUploaderBasic({ 105 + debug: true, 106 + element: $(".upload-form button.files").get(0), 107 + request: { 108 + endpoint: "/uploads", 109 + }, 110 + deleteFile: { 111 + enabled: true, 112 + endpoint: "uploads", 113 + }, 114 + retry: { 115 + enableAuto: true, 116 + }, 117 + }); 118 + 119 + }); 120 + 121 + // add tag when comma/enter 122 + $("body").on("keypress", ".upload-form input.add-tag", function(e) { 123 + if (e.which == 44) e.preventDefault(); // comma 124 + if (e.which == 44 || e.which == 13) { // comma || enter 125 + const $inputElement = $(this); 126 + const value = $(this).val(); 127 + $inputElement.val(""); // empty the input 128 + $(` 129 + <div class="tag"> 130 + <div class="tag-text">${value}</div> 131 + <button class="remove-tag">x</div> 132 + </div> 133 + `).insertBefore($inputElement); 134 + } 135 + }); 136 + 137 + // remove tag button 138 + $("body").on("click", ".upload-form .remove-tag", function() { 139 + $(this).parent().remove(); 140 + }); 141 + 142 + // clicking the tags-box focuses the add-tag input element 143 + $("body").on("click", ".upload-form .tags-box", (e) => { 144 + if (e.target == e.currentTarget) { 145 + $(".upload-form input.add-tag").focus(); 146 + } 147 + }); 148 + 149 + });
+13 -14
web/js/ui.js
··· 1 1 // search, press enter 2 2 const searchField = $(".search")[0]; 3 - $(".search").keypress((e) => { 3 + $("body").on("keypress", ".search", (e) => { 4 4 if (e.which == 13) { 5 5 const searchQuery = searchField.value; 6 6 window.location = `/search/${searchQuery}`; ··· 10 10 if (loggedIn) { 11 11 12 12 // press profile pic to toggle account box 13 - $("img.account-icon").on("click", () => { 13 + $("body").on("click", "img.account-icon", () => { 14 14 $(".account-box").toggleClass("visible"); 15 15 }); 16 16 $(document).on("click", (e) => { ··· 21 21 22 22 } 23 23 24 - // function fullPageElement(element) { 25 - // const headerHeight = $(".site-header").height(); 26 - // let windowHeight = $(window).height(); 27 - // element.height(windowHeight - headerHeight); 28 - // $(window).on("resize", () => { 29 - // windowHeight = $(window).height(); 30 - // element.height(windowHeight - headerHeight); 31 - // }); 32 - // } 33 - // if (page == "home") fullPageElement($(".center-container")); 34 - // if (page == "login") fullPageElement($(".form-container")); 35 - // if (page == "register") fullPageElement($(".form-container")); 24 + // auto-resize textareas 25 + $("body").on("input", "textarea.auto-resize", (e) => { 26 + const textarea = e.target; 27 + textarea.style.height = "auto"; 28 + const scrollheight = textarea.scrollHeight; 29 + var computedStyle = getComputedStyle(textarea); 30 + var paddingTop = computedStyle.paddingBottom.slice(0, -2); 31 + var paddingBot = computedStyle.paddingTop.slice(0, -2); 32 + var padding = Number(paddingTop) + Number(paddingBot); 33 + textarea.style.height = textarea.scrollHeight - padding+"px"; 34 + });
+1
web/node/mongoose-models.js
··· 15 15 username: String, 16 16 email: String, 17 17 password: String, 18 + profilePictureURL: String, 18 19 // images: [imageSchema], 19 20 }, { typeKey: "$Type" }); 20 21
+2 -4
web/node/passport-setup.js
··· 6 6 const bcrypt = require("bcryptjs"); 7 7 8 8 passport.serializeUser((user, done) => { 9 - console.log(555); 10 9 done(null, user._id); 11 10 }); 12 11 13 12 passport.deserializeUser((id, done) => { 14 - console.log(444); 15 13 User.findById(id).then((resultUser) => { 16 14 done(null, resultUser); 17 15 }); ··· 23 21 }, (username, password, done) => { 24 22 User.findOne({ username: username }, function(err, resultUser) { 25 23 if (err) return done(err); 26 - if (!resultUser) return done(null, false, {message: "incorrect username"}); 24 + if (!resultUser) return done(null, false, "incorrect email"); 27 25 // match password 28 26 bcrypt.compare(password, resultUser.password, (err, isMatch) => { 29 27 if (err) return done(err); 30 28 if (isMatch) return done(null, resultUser); 31 - if (!isMatch) return done(null, false, {message: "incorrect password"}); 29 + if (!isMatch) return done(null, false, "incorrect password"); 32 30 }); 33 31 34 32 });
+19 -61
web/node/routes.js
··· 15 15 }); 16 16 } 17 17 18 - function jsonRes(res, one, two) { 19 - let resObj = {}; 20 - if (one == "err") { 21 - resObj = { 22 - err: { 23 - code: null, 24 - msg: null 25 - } 26 - }; 27 - } else if (one) { 28 - resObj = one; 29 - } 30 - res.json(resObj); 31 - } 32 - 33 18 module.exports = (app) => { 34 19 35 20 app.all("*", (req, res, next) => { ··· 71 56 variables.page = pugFile; 72 57 variables.loggedIn = res.locals.loggedIn; 73 58 if (res.locals.loggedIn) { 74 - variables.displayName = req.user.displayName; 59 + variables.displayname = req.user.displayname; 60 + variables.username = req.user.username; 61 + variables.email = req.user.email; 75 62 variables.profilePictureURL = req.user.profilePictureURL; 76 - variables.transactions = req.user.transactions; 77 63 render("logged-in/"+pugFile, (err) => { 78 64 // logErr(72001, err); 79 65 render("logged-out/"+loggedOutPugFile, (err) => { ··· 82 68 }); 83 69 } else { 84 70 render("logged-out/"+loggedOutPugFile, (err) => { 71 + res.redirect(`/login?redirect=${path}`); 85 72 logErr(72003, err); 86 73 }); 87 74 } ··· 89 76 }); 90 77 } 91 78 92 - // app.post("/login", passport.authenticate("local", { 93 - // successRedirect: "/", 94 - // failureRedirect: "/login", 95 - // failureFlash: false 96 - // })); 97 - 98 79 app.post("/login", (req, res) => { 99 80 let email = req.body.email; 100 81 let password = req.body.password; 101 82 let errors = []; 102 83 103 84 function sendResponse() { 104 - if (errors.length == 0) { 105 - res.json({ 106 - errors: null, 107 - }); 108 - } else { 109 - res.json({ 110 - errors: errors, 111 - }); 112 - } 85 + res.json({ 86 + errors: errors, 87 + redirect: req.query.redirect || "/", 88 + }); 113 89 } 114 90 115 91 function checkIfUserExists(email, callback) { ··· 131 107 if (errors.length != 0) return sendResponse(); 132 108 133 109 req.body.username = resultUser.username; 134 - console.log(req.body.username); 135 110 passport.authenticate("local", (err, user, info) => { 136 111 if (err) errors.push("unknown 6"); 137 112 if (errors.length != 0) return sendResponse(); 138 113 139 114 if (!user) { 140 - if (info.email) errors.push(info.email); 141 - if (info.password) errors.push(info.password); 115 + if (info) errors.push(info); 142 116 if (errors.length != 0) return sendResponse(); 143 117 } else { 144 118 req.login(user, (err) => { ··· 148 122 } 149 123 })(req, res); 150 124 151 - // passport.authenticate("local", (err, user, info) => { 152 - // if (err) return next(err); 153 - // if (!user) return res.redirect("/login"); 154 - // req.logIn(user, (err) => { 155 - // if (err) return next(err); 156 - // return res.redirect("/users/"+user.username); 157 - // }); 158 - // })(req, res, next); 159 - 160 125 }); 161 126 162 127 ··· 164 129 165 130 app.post("/register", (req, res) => { 166 131 let displayname = req.body.displayname; 167 - let username = req.body.username; 132 + let username = req.body.username.toLowerCase(); 168 133 let email = req.body.email; 169 134 let password = req.body.password; 170 135 let errors = []; ··· 172 137 if (validator.isEmpty(displayname)) errors.push("displayname empty"); 173 138 else if (!validator.isLength(displayname, {max: 30})) errors.push("displayname length"); 174 139 175 - const usernameRegex = new RegExp(/^[a-zA-Z0-9]+$/g); 140 + const usernameRegex = new RegExp(/^[a-z0-9]+$/g); 176 141 if (validator.isEmpty(username)) errors.push("username empty"); 177 142 else if (!username.match(usernameRegex)) errors.push("username chars"); 178 143 else if (!validator.isLength(username, {max: 30})) errors.push("username length"); ··· 185 150 else if (!validator.isLength(email, {max: 60})) errors.push("email length"); 186 151 187 152 function sendResponse() { 188 - if (errors.length == 0) { 189 - res.json({ 190 - errors: null, 191 - }); 192 - } else { 193 - res.json({ 194 - errors: errors, 195 - }); 196 - } 153 + res.json({ 154 + errors: errors, 155 + }); 197 156 } 198 157 199 158 function checkIfUserExists(email, username, callback) { ··· 239 198 username: username, 240 199 email: email, 241 200 password: hashedPassword, 201 + 202 + profilePictureURL: "http://kasp.io/cdn/logos/kh-logo-aevi.png", 242 203 }).save((err) => { 243 204 if (err) errors.push("unknown 5"); 244 205 sendResponse(); ··· 248 209 249 210 }); 250 211 251 - 252 - 253 212 app.get("/logout", (req, res) => { 254 213 req.logout(); 255 214 res.redirect("/"); 256 215 }); 257 216 217 + // logged out 258 218 get("/login", "login"); 259 219 get("/register", "register"); 260 220 get("/", "home"); 261 221 262 - // get("/balance", "balance"); 263 - // get("/gains", "gains"); 264 - // 265 - // get("/transactions", "transactions"); 222 + // logged in 223 + get("/upload", "upload"); 266 224 267 225 }
+22
web/node/upload-routes.js
··· 1 + "use strict"; 2 + const multiparty = require("multiparty"); 3 + 4 + module.exports = (app) => { 5 + 6 + app.post("/uploads", (req, res) => { 7 + const form = new multiparty.Form(); 8 + 9 + form.parse(req, (err, fields, files) => { 10 + let partIndex = fields.qqpartindex; 11 + // text/plain is required to ensure support for IE9 and older 12 + res.set("Content-Type", "text/plain"); 13 + if (partIndex == null) { 14 + onSimpleUpload(fields, files[fileInputName][0], res); 15 + } else { 16 + onChunkedUpload(fields, files[fileInputName][0], res); 17 + } 18 + }); 19 + 20 + }); 21 + 22 + }
+3 -1
web/package.json
··· 36 36 "validator": "9.4.x", 37 37 "bcryptjs": "2.4.x", 38 38 39 - "mongoose": "5.0.x" 39 + "mongoose": "5.0.x", 40 + "fine-uploader": "5.15.x", 41 + "multiparty": "4.1.x" 40 42 } 41 43 }
+17
web/pug/logged-in/upload.pug
··· 1 + .form-container.upload-form.page-width 2 + .form 3 + button.button.files Select file 4 + input(type="file" name="files" id="files") 5 + label(for="title") Title 6 + input.form-field.title(type="text", name="title" id="title") 7 + label(for="description") Description 8 + textarea.form-field.auto-resize.description(type="text", name="description" id="description", rows="3") 9 + .tags-box 10 + .tag 11 + .tag-text Hey 12 + button.remove-tag x 13 + .tag 14 + .tag-text There 15 + button.remove-tag x 16 + input.add-tag(placeholder="Add a tag...") 17 + button.button.upload(tabIndex="0") Upload
+1 -1
web/pug/logged-out/home.pug
··· 1 - .center-container 1 + .center-container.page-width 2 2 p.center-logo Lumix 3 3 p.tagline The place you put your art 4 4 .stuff
+1 -1
web/pug/logged-out/login.pug
··· 1 - .form-container.login-form 1 + .form-container.login-form.page-width 2 2 .form 3 3 label(for="email") Email 4 4 input.form-field.email(type="text", name="email" id="email")
+1 -18
web/pug/logged-out/register.pug
··· 1 - //- form(action="/login", method="post") 2 - //- div 3 - //- label Email: 4 - //- input(type="text", name="username") 5 - //- div 6 - //- label Username: 7 - //- input(type="text", name="username") 8 - //- div 9 - //- label Password: 10 - //- input(type="password", name="password") 11 - //- div 12 - //- label Repeat Password: 13 - //- input(type="password", name="password") 14 - //- div 15 - //- input(type="submit", value="Register") 16 - 17 - 18 - .form-container.register-form 1 + .form-container.register-form.page-width 19 2 .form 20 3 label(for="displayname") Displayname 21 4 input.form-field.displayname(type="text", name="displayname" id="displayname")
+4 -3
web/pug/template.pug
··· 3 3 head 4 4 title Lumix 5 5 link(href='https://fonts.googleapis.com/css?family=Roboto:400,500', rel='stylesheet') 6 - link(href='https://fonts.googleapis.com/css?family=Rubik:400,500', rel='stylesheet') 6 + link(href='https://fonts.googleapis.com/css?family=Rubik:300,400,500', rel='stylesheet') 7 7 // css 8 8 link(rel='stylesheet', type='text/css', href='/global.css') 9 9 body 10 - header.site-header 10 + header.site-header.page-width 11 11 .left-side-header 12 12 a(href="/").header-logo Lumix 13 13 input(placeholder="Search").search ··· 16 16 a.button.menu-bar-item(href="/register") Register 17 17 a.button.menu-bar-item(href="/login") Login 18 18 if loggedIn 19 + a.button.menu-bar-item(href="/upload") Upload 19 20 .account-icon-container 20 21 img.account-icon(src=profilePictureURL) 21 22 .account-box 22 23 .spacer 23 - a.account-box-item.settings(href="/profile/"+displayName) 24 + a.account-box-item.settings(href="/profile/"+username) 24 25 <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> 25 26 <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/> 26 27 <path d="M0 0h24v24H0z" fill="none"/>
+1 -32
web/sass/buttons.sass
··· 1 1 .button 2 2 padding: 12px 8px 3 3 position: relative 4 + display: block 4 5 &:hover::before 5 6 width: 50% 6 7 &:focus::before ··· 17 18 height: 2px 18 19 width: 0% 19 20 border-radius: 5px 20 - 21 - 22 - // .button 23 - // padding: 14px 30px 24 - // position: relative 25 - // background-color: transparent 26 - // &:hover 27 - // &::before 28 - // height: 70% 29 - // &::after 30 - // height: 70% 31 - // &:focus 32 - // &::before 33 - // height: 90% 34 - // transform: translate(4px, -50%) 35 - // &::after 36 - // height: 90% 37 - // transform: translate(-4px, -50%) 38 - // &::before, &::after 39 - // content: "" 40 - // position: absolute 41 - // background-color: $gray-line 42 - // transition: $transition-20 all 43 - // top: 50% 44 - // transform: translate(0px, -50%) 45 - // width: 2px 46 - // height: 50% 47 - // border-radius: 5px 48 - // &::before 49 - // left: 0px 50 - // &::after 51 - // right: 0px
+1 -1
web/sass/defaults.sass
··· 14 14 form, label, legend, fieldset, 15 15 table, tbody, tfoot, thead, tr, th, td, 16 16 aside, canvas, footer, header, menu, nav, 17 - section, audio, video, input, button 17 + section, audio, video, input, button, textarea 18 18 color: $black 19 19 font: inherit 20 20 margin: 0
+54 -1
web/sass/forms.sass
··· 10 10 flex-direction: column 11 11 font-size: 15px 12 12 .form 13 - input.form-field 13 + textarea 14 + resize: none 15 + input.form-field, textarea.form-field, .tags-box 14 16 padding: 10px 14px 15 17 margin-top: 5px 16 18 margin-bottom: 15px ··· 19 21 background-color: $white-dark 20 22 &.login-form 21 23 &.register-form 24 + .form-container.upload-form 25 + button.files 26 + margin: auto 27 + input#files 28 + display: none 29 + input.form-field, textarea.form-field, .tags-box 30 + width: 600px 31 + .tags-box 32 + background-color: $white-dark 33 + display: flex 34 + flex-wrap: wrap 35 + padding-bottom: calc(10px - 6px) 36 + $tag-padding: 5px 37 + cursor: text 38 + .tag 39 + padding: $tag-padding 0px 40 + margin-right: 10px 41 + margin-bottom: 6px 42 + line-height: initial 43 + .tag-text 44 + display: inline-block 45 + word-break: break-word 46 + button.remove-tag 47 + display: inline 48 + padding: 0px $tag-padding 49 + user-select: none 50 + font-weight: 300 51 + cursor: pointer 52 + background-color: transparent 53 + 54 + position: relative 55 + &:hover::before 56 + width: 50% 57 + &:focus::before 58 + width: 80% 59 + transform: translate(-50%, 6px) 60 + &::before 61 + content: "" 62 + position: absolute 63 + transition: $transition-20 all 64 + background-color: $gray-line 65 + bottom: 0px 66 + transform: translate(-50%, 8px) 67 + left: 50% 68 + height: 2px 69 + width: 0% 70 + border-radius: 5px 71 + input.add-tag 72 + padding: $tag-padding $tag-padding 73 + margin-bottom: 6px 74 + background-color: transparent
+5
web/sass/template.sass
··· 5 5 display: grid 6 6 main.page-container 7 7 // margin: 30px 8 + .page-width 9 + min-width: 800px 8 10 9 11 header.site-header 10 12 font-size: 16px ··· 59 61 img.account-icon 60 62 margin: 10px 0px 61 63 cursor: pointer 64 + padding: 8px 65 + width: 35px 66 + height: 35px 62 67 .account-box 63 68 z-index: 10 64 69 background-color: #FFFFFF
+1
web/sass/variables.sass
··· 1 1 $header-height: 70px 2 2 $white: #FFFFFF 3 3 $white-dark: #F3F3F3 4 + $white-darker: #E7E7E7 4 5 $gray-line: #717171 5 6 $black: #3F3F3F 6 7 $tooltip-bg: #3c4554
+1 -18
web/server.js
··· 53 53 // passport 54 54 require("./node/passport-setup.js")(app, mongoose); 55 55 56 - // const passport = require("passport"); 57 - // const passportSetup = require("./node/passport-setup"); 58 - // const session = require("express-session"); 59 - // const MongoStore = require("connect-mongo")(session); 60 - // app.use(session({ 61 - // // key: "sess", 62 - // secret: "tomatonanaboy69:o", 63 - // store: new MongoStore({ 64 - // mongooseConnection: mongoose.connection, 65 - // ttl: 60*60*24*90, 66 - // touchAfter: 60*60*24 67 - // }), 68 - // resave: false, 69 - // saveUninitialized: false 70 - // })); 71 - // app.use(passport.initialize()); 72 - // app.use(passport.session()); 73 - 74 56 // routes 75 57 require("./node/routes")(app); 58 + require("./node/upload-routes")(app); 76 59 77 60 const httpsOptions = { 78 61 key: fs.readFileSync(dir(`letsencrypt/etc/letsencrypt/live/${process.env.CERT_DOMAIN}/privkey.pem`)),
+65 -4
web/static/global.css
··· 19 19 form, label, legend, fieldset, 20 20 table, tbody, tfoot, thead, tr, th, td, 21 21 aside, canvas, footer, header, menu, nav, 22 - section, audio, video, input, button { 22 + section, audio, video, input, button, textarea { 23 23 color: #3F3F3F; 24 24 font: inherit; 25 25 margin: 0; ··· 67 67 body { 68 68 background-color: #FFFFFF; 69 69 display: grid; } 70 + 71 + .page-width { 72 + min-width: 800px; } 70 73 71 74 header.site-header { 72 75 font-size: 16px; ··· 120 123 position: relative; } 121 124 header.site-header .right-side-header .account-icon-container img.account-icon { 122 125 margin: 10px 0px; 123 - cursor: pointer; } 126 + cursor: pointer; 127 + padding: 8px; 128 + width: 35px; 129 + height: 35px; } 124 130 header.site-header .right-side-header .account-icon-container .account-box { 125 131 z-index: 10; 126 132 background-color: #FFFFFF; ··· 178 184 179 185 html .button { 180 186 padding: 12px 8px; 181 - position: relative; } 187 + position: relative; 188 + display: block; } 182 189 html .button:hover::before { 183 190 width: 50%; } 184 191 html .button:focus::before { ··· 206 213 justify-content: center; 207 214 flex-direction: column; 208 215 font-size: 15px; } 209 - html .form-container .form input.form-field { 216 + html .form-container .form textarea { 217 + resize: none; } 218 + html .form-container .form input.form-field, html .form-container .form textarea.form-field, html .form-container .form .tags-box { 210 219 padding: 10px 14px; 211 220 margin-top: 5px; 212 221 margin-bottom: 15px; 213 222 border-radius: 5px; 214 223 width: 250px; 215 224 background-color: #F3F3F3; } 225 + 226 + html .form-container.upload-form button.files { 227 + margin: auto; } 228 + 229 + html .form-container.upload-form input#files { 230 + display: none; } 231 + 232 + html .form-container.upload-form input.form-field, html .form-container.upload-form textarea.form-field, html .form-container.upload-form .tags-box { 233 + width: 600px; } 234 + 235 + html .form-container.upload-form .tags-box { 236 + background-color: #F3F3F3; 237 + display: flex; 238 + flex-wrap: wrap; 239 + padding-bottom: calc(10px - 6px); 240 + cursor: text; } 241 + html .form-container.upload-form .tags-box .tag { 242 + padding: 5px 0px; 243 + margin-right: 10px; 244 + margin-bottom: 6px; 245 + line-height: initial; } 246 + html .form-container.upload-form .tags-box .tag .tag-text { 247 + display: inline-block; 248 + word-break: break-word; } 249 + html .form-container.upload-form .tags-box .tag button.remove-tag { 250 + display: inline; 251 + padding: 0px 5px; 252 + user-select: none; 253 + font-weight: 300; 254 + cursor: pointer; 255 + background-color: transparent; 256 + position: relative; } 257 + html .form-container.upload-form .tags-box .tag button.remove-tag:hover::before { 258 + width: 50%; } 259 + html .form-container.upload-form .tags-box .tag button.remove-tag:focus::before { 260 + width: 80%; 261 + transform: translate(-50%, 6px); } 262 + html .form-container.upload-form .tags-box .tag button.remove-tag::before { 263 + content: ""; 264 + position: absolute; 265 + transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1) all; 266 + background-color: #717171; 267 + bottom: 0px; 268 + transform: translate(-50%, 8px); 269 + left: 50%; 270 + height: 2px; 271 + width: 0%; 272 + border-radius: 5px; } 273 + html .form-container.upload-form .tags-box input.add-tag { 274 + padding: 5px 5px; 275 + margin-bottom: 6px; 276 + background-color: transparent; }
web/static/khlogo.png

This is a binary file and will not be displayed.