mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-28 03:05:35 +01:00
feature (admin): form creation
This commit is contained in:
parent
246de58de4
commit
b65365b99b
7 changed files with 114 additions and 28 deletions
|
|
@ -20,11 +20,6 @@
|
|||
border-color: var(--emphasis-primary);
|
||||
}
|
||||
|
||||
.component_input:disabled, .component_select:disabled {
|
||||
background: rgba(0,0,0,0.2)!important;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input.component_input[readonly], textarea.component_textarea[readonly] {
|
||||
border-bottom-style: dashed;
|
||||
cursor: pointer;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { createElement } from "../lib/skeleton/index.js";
|
||||
import { safe } from "../lib/dom.js";
|
||||
import { gid } from "../lib/random.js";
|
||||
|
||||
export function formTmpl(withAutocomplete) {
|
||||
return {
|
||||
|
|
@ -17,58 +19,131 @@ export function formTmpl(withAutocomplete) {
|
|||
`);
|
||||
},
|
||||
renderInput: $renderInput,
|
||||
formatLabel: format,
|
||||
};
|
||||
};
|
||||
|
||||
export async function $renderInput({ autocomplete, type, path = [] }) {
|
||||
export async function $renderInput(props) {
|
||||
const {
|
||||
id = null,
|
||||
type,
|
||||
value = null,
|
||||
placeholder = "",
|
||||
required = false,
|
||||
readonly = false,
|
||||
path = [],
|
||||
autocomplete = false,
|
||||
datalist = null,
|
||||
options = null,
|
||||
} = props
|
||||
let attr = `name="${path.join(".")}" `;
|
||||
if (value) attr += `value="${value}" `;
|
||||
if (id) attr += `id=${id} `;
|
||||
if (placeholder) attr += `placeholder=${placeholder} `;
|
||||
if (!autocomplete) attr += `autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="off" `;
|
||||
if (required) attr += "required ";
|
||||
if (readonly) attr += "readonly ";
|
||||
|
||||
switch(type) {
|
||||
case "text":
|
||||
return createElement(`
|
||||
<input
|
||||
case "text": // TODO
|
||||
const dataListId = gid("list_");
|
||||
const $input = createElement(`
|
||||
<input ${safe(attr)}
|
||||
type="text"
|
||||
class="component_input"
|
||||
name="${path.join(".")}"
|
||||
/>
|
||||
`);
|
||||
if (!datalist) return $input;
|
||||
const $wrapper = window.document.createElement("span");
|
||||
const $datalist = window.document.createElement("datalist")
|
||||
$wrapper.appendChild($input);
|
||||
$datalist.setAttribute("id", dataListId);
|
||||
// const filtered = function(multi, datalist, currentValue) {
|
||||
// if (multi !== true || currentValue == null) return datalist;
|
||||
// return autocomplete(
|
||||
// currentValue
|
||||
// .split(",")
|
||||
// .map((t) => t.trim())
|
||||
// .filter((t) => t),
|
||||
// datalist,
|
||||
// );
|
||||
// };
|
||||
// fitlered()
|
||||
return $wrapper;
|
||||
case "number":
|
||||
return createElement(`
|
||||
<input
|
||||
${safe(attr)}
|
||||
type="number"
|
||||
class="component_input"
|
||||
name="${path.join(".")}"
|
||||
/>
|
||||
`);
|
||||
case "password":
|
||||
// TODO: click eye
|
||||
return createElement(`
|
||||
<div class="formbuilder_password">
|
||||
<input
|
||||
${safe(attr)}
|
||||
type="password"
|
||||
class="component_input"
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
case "select": // TODO
|
||||
return createElement(`
|
||||
<select class="component_select" name="${path.join(".")}">
|
||||
<option hidden=""></option>
|
||||
<option name="base">base</option>
|
||||
</select>
|
||||
`);
|
||||
// case "long_password":
|
||||
// // TODO
|
||||
// case "long_text":
|
||||
// return // TODO
|
||||
// case "bcrypt": import("./bcrypt.js")
|
||||
// // TODO
|
||||
case "hidden":
|
||||
return createElement(`
|
||||
<input
|
||||
type="hidden"
|
||||
name="${path.join(".")}"
|
||||
type="hidden" value=${safe(value)}
|
||||
name="${safe(path.join("."))}"
|
||||
/>
|
||||
`);
|
||||
case "boolean":
|
||||
return createElement(`
|
||||
<div class="component_checkbox">
|
||||
<input ${safe(attr)} type="checkbox" />
|
||||
<span className="indicator"></span>
|
||||
</div>
|
||||
`);
|
||||
case "select":
|
||||
const renderOption = (name) => `<option name="${safe(name)}">${safe(name)}</option>`;
|
||||
return createElement(`
|
||||
<select class="component_select" ${safe(attr)}>
|
||||
${(options || []).map(renderOption)}
|
||||
</select>
|
||||
`);
|
||||
case "date":
|
||||
return createElement(`
|
||||
<input
|
||||
${safe(attr)}
|
||||
type="date"
|
||||
class="component_input"
|
||||
/>
|
||||
`);
|
||||
case "datetime":
|
||||
return createElement(`
|
||||
<input
|
||||
${safe(attr)}
|
||||
type="datetime-local"
|
||||
class="component_input"
|
||||
/>
|
||||
`);
|
||||
case "image":
|
||||
return createElement(` <img id="${safe(id)}" src="${save(value)}" />`);
|
||||
// case "file":
|
||||
// return createElement() // TODO
|
||||
default:
|
||||
return createElement(`
|
||||
<input
|
||||
value="unknown element type ${type}"
|
||||
type="text"
|
||||
class="component_input"
|
||||
path="${path.join(".")}"
|
||||
disabled
|
||||
path="${safe(path.join("."))}"
|
||||
readonly
|
||||
/>
|
||||
`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
"/logout": "/pages/ctrl_logout.js",
|
||||
|
||||
"/login": "/pages/connectpage/connectpage.js",
|
||||
|
||||
//"/": "/pages/home/index.js",
|
||||
"": "/pages/ctrl_notfound.js",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,3 +9,9 @@ export function qsa($node, selector) {
|
|||
if (!$node) throw new Error("undefined node");
|
||||
return $node.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
export function safe(str) {
|
||||
const $div = window.document.createElement("div");
|
||||
$div.textContent = str;
|
||||
return $div.innerHTML;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ async function createFormNodes(node, { renderNode, renderLeaf, renderInput, path
|
|||
}
|
||||
const $list = [];
|
||||
for (const key of Object.keys(node)) {
|
||||
|
||||
if (typeof node[key] !== "object") {
|
||||
$list.push(createElement(`<div>ERR: node[${typeof node[key]}] path[${path.join(".")}] level[${level}]</div>`));
|
||||
}
|
||||
|
|
@ -38,14 +37,12 @@ async function createFormNodes(node, { renderNode, renderLeaf, renderInput, path
|
|||
const $chunk = renderNode({ level, label: key });
|
||||
const $children = $chunk.querySelector(`[data-bind="children"]`) || $chunk;
|
||||
$children.removeAttribute("data-bind");
|
||||
const $nodes = await createForm(node[key], {
|
||||
const $nested = await createForm(node[key], {
|
||||
path: path.concat(key), level: level + 1, label: key,
|
||||
renderNode, renderLeaf, renderInput,
|
||||
});
|
||||
$nodes.childNodes.forEach(($node) => {
|
||||
$children.appendChild($node);
|
||||
});
|
||||
$list.push($chunk);
|
||||
$children.appendChild($nested);
|
||||
$list.push($children);
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
|
|
|
|||
6
public/lib/random.js
Normal file
6
public/lib/random.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export function gid(prefix = "") {
|
||||
let id = prefix;
|
||||
id += new Date().getTime().toString(32);
|
||||
id += Math.random().toString(32).replace(/^0\./, "");
|
||||
return id;
|
||||
}
|
||||
5
public/pages/connectpage/connectpage.js
Normal file
5
public/pages/connectpage/connectpage.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { createElement } from "../../lib/skeleton/index.js";
|
||||
|
||||
export default function(render) {
|
||||
render(createElement(`<div> LOGIN </div>`));
|
||||
}
|
||||
Loading…
Reference in a new issue