commit bfc470af516500c70d6dca7cf42ecc2423b35751
parent 312097e6f1ae7261375f61e99aa40838cdc57515
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Wed, 7 Aug 2024 16:47:24 -0700
Add floorplan metadata editing
Diffstat:
3 files changed, 167 insertions(+), 16 deletions(-)
diff --git a/files/floorplans/main.css b/files/floorplans/main.css
@@ -44,3 +44,15 @@
#floorplans.grid > li > .floorplan:hover > footer {
opacity: 100%;
}
+
+input.fp_name {
+ font-size: larger;
+}
+
+input.fp_address {
+ font-style: italic;
+}
+
+input {
+ display: block;
+}
diff --git a/files/floorplans/main.js b/files/floorplans/main.js
@@ -2,6 +2,9 @@ import * as api from "/lib/api.js"
import * as etc from "/lib/etc.js"
import * as ui from "/lib/ui.js"
+// These are in the order they should appear
+const editables = [ "name", "synopsis", "address" ]
+
function init() {
etc.authorize()
etc.bar()
@@ -30,12 +33,120 @@ function gridview() {
document.getElementById("floorplans").setAttribute("class", "grid")
}
-function edit_floorplan_func(item, floorplan) {
+function commit_editable_floorplan_func(element, data) {
+ let update_display = function() {
+ let parent = element.querySelector("header")
+ for (let i in editables) {
+ let c = floorplan_info_class(editables[i])
+ let field = parent.querySelector("." + c)
+ if (!field) {
+ throw new Error("Expected ." + c + ", got nothing")
+ }
+ if (!field.value) {
+ field.remove()
+ } else {
+ let creator = create_field[editables[i]]
+ if (!creator) {
+ throw new Error("Expected " + editables[i] + "in create_field")
+ }
+ field.replaceWith(creator(field.value))
+ }
+ }
+ }
+
+ return function () {
+ let patches = []
+ let fields = Array.from(element.querySelectorAll("header > input"))
+ for (let i in fields) {
+ let name = floorplan_info_name(fields[i].getAttribute("class"))
+ let value = fields[i].value
+ console.debug(fields[i], name, value)
+ if (value === data[name]) {
+ continue;
+ } else if (value) {
+ patches.push({ op: "add", path: name, value: value })
+ } else if (!value) {
+ patches.push({ op: "remove", path: name })
+ }
+ }
+
+ if (patches.length == 0) {
+ console.debug("No changes, skipping PATCH")
+ update_display()
+ return
+ }
+
+ return api.fetch("PATCH", "floorplans/" + localStorage.getItem("username") + "/" + data.name, patches)
+ .then(function(data) {
+ for (let i in data) {
+ data[i] = data[i]
+ }
+ update_display()
+ })
+ .catch(function(err) {
+ etc.error(err, element)
+ throw err
+ })
+ }
+}
+
+function editable_floorplan_func(element, data) {
return function() {
- etc.error("Edit not implemented", item)
+ let prev
+ let parent = element.querySelector("header")
+ for (let i in editables) {
+ let input
+ let c = floorplan_info_class(editables[i])
+ let e = parent.querySelector("." + c) // .getElementsByClassName()
+ if (e) {
+ input = make_input(editables[i], { value: e.textContent })
+ input.setAttribute("class", c)
+ e.replaceWith(input)
+ } else {
+ input = make_input(editables[i])
+ input.setAttribute("class", c)
+ if (prev) {
+ prev.after(input)
+ } else {
+ parent.append(input)
+ }
+ }
+ prev = input
+ }
}
}
+function floorplan_info_class(name) {
+ return "fp_" + name;
+}
+
+function floorplan_info_name(classname) {
+ if (!classname.match("^fp_")) {
+ throw new Error("Expected floorplan info class")
+ }
+ return classname.substring(3)
+}
+
+function make_input(name, options) {
+ if (!name) {
+ throw new Error("No name provided")
+ }
+ if (!options) {
+ options = {}
+ }
+
+ let input = document.createElement("input")
+ input.name = name
+ input.placeholder = name
+ if (options["type"]) {
+ input.type = options["type"]
+ }
+ if (options["value"]) {
+ input.value = options["value"]
+ }
+ return input
+}
+
function delete_floorplan_func(item, floorplan) {
return function() {
api.fetch("DELETE", "floorplans/" + floorplan.user + "/" + floorplan.name)
@@ -49,34 +160,34 @@ function delete_floorplan_func(item, floorplan) {
}
function create_floorplan(floorplan) {
+ if (!floorplan.name) {
+ throw new Error("Expected floorplan name")
+ }
+
let root = document.createElement("div")
root.setAttribute("class", "floorplan")
let aside = document.createElement("aside")
- aside.append(ui.button("Edit", "Edit floorplan", "create", edit_floorplan_func(root, floorplan)))
+ aside.append(
+ ui.toggle(
+ ui.button("Edit", "Edit floorplan", "create"), editable_floorplan_func(root, floorplan),
+ ui.button("Save", "Save floorplan", "save"), commit_editable_floorplan_func(root, floorplan),
+ )
+ )
+
aside.append(ui.button("Delete", "Delete floorplan", "trash", delete_floorplan_func(root, floorplan)))
root.append(aside)
let header = document.createElement("header")
- let heading = document.createElement("h2")
- header.append(heading)
- let link = document.createElement("a")
- heading.append(link)
+ header.append(create_field.name(floorplan.name))
if (floorplan.synopsis) {
- let synopsis = document.createElement("span")
- synopsis.setAttribute("class", "synopsis")
- synopsis.appendChild(document.createTextNode(floorplan.synopsis))
- header.append(synopsis)
+ header.append(create_field.synopsis(floorplan.synopsis))
}
if (floorplan.address) {
- let address = document.createElement("address")
- address.appendChild(document.createTextNode(floorplan.address))
- header.append(address)
+ header.append(create_field.address(floorplan.address))
}
- link.href = "floorplans/" + localStorage.getItem("username") + "/" + floorplan.name
- link.appendChild(document.createTextNode(floorplan.name))
root.append(header)
if (floorplan.user != localStorage.getItem("username")) {
@@ -89,6 +200,32 @@ function create_floorplan(floorplan) {
return root
}
+var create_field = {
+ name: function(text) {
+ let heading = document.createElement("h2")
+ heading.setAttribute("class", floorplan_info_class("name"))
+ let link = document.createElement("a")
+ link.href = "floorplans/" + localStorage.getItem("username") + "/" + text
+ link.appendChild(document.createTextNode(text))
+ heading.append(link)
+ return heading
+ },
+
+ synopsis: function(text) {
+ let synopsis = document.createElement("span")
+ synopsis.setAttribute("class", floorplan_info_class("synopsis"))
+ synopsis.appendChild(document.createTextNode(text))
+ return synopsis
+ },
+
+ address: function(text) {
+ let address = document.createElement("address")
+ address.setAttribute("class", floorplan_info_class("address"))
+ address.appendChild(document.createTextNode(text))
+ return address
+ }
+}
+
function show_floorplans(floorplans) {
let list = document.getElementById("floorplans")
if (!list) {
diff --git a/files/icons/save-outline.svg b/files/icons/save-outline.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path d="M380.93,57.37A32,32,0,0,0,358.3,48H94.22A46.21,46.21,0,0,0,48,94.22V417.78A46.21,46.21,0,0,0,94.22,464H417.78A46.36,46.36,0,0,0,464,417.78V153.7a32,32,0,0,0-9.37-22.63ZM256,416a64,64,0,1,1,64-64A63.92,63.92,0,0,1,256,416Zm48-224H112a16,16,0,0,1-16-16V112a16,16,0,0,1,16-16H304a16,16,0,0,1,16,16v64A16,16,0,0,1,304,192Z" style="fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>
+\ No newline at end of file