From aaf33996c8b2a90025d7be82bd63793c84bbaab5 Mon Sep 17 00:00:00 2001 From: Mickael KERJEAN Date: Mon, 28 Jan 2019 01:09:03 +1100 Subject: [PATCH] feature (form): form pages --- client/components/formbuilder.js | 31 +++++++++++++++- client/components/formbuilder.scss | 1 + client/helpers/common.js | 10 +++++ client/helpers/form.js | 45 +++++++++++++++++++++++ client/helpers/index.js | 5 +-- client/helpers/text.js | 10 ----- client/pages/filespage/thing-existing.js | 1 + client/pages/viewerpage/formviewer.js | 14 ++++--- client/pages/viewerpage/formviewer.scss | 47 ++++++++++++++++++------ server/common/config.go | 3 ++ 10 files changed, 135 insertions(+), 32 deletions(-) delete mode 100644 client/helpers/text.js diff --git a/client/components/formbuilder.js b/client/components/formbuilder.js index 164691c6..0228c514 100644 --- a/client/components/formbuilder.js +++ b/client/components/formbuilder.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Input, Textarea, Select, Enabler } from './'; -import { FormObjToJSON, bcrypt_password, format } from '../helpers/'; +import { FormObjToJSON, bcrypt_password, format, autocomplete } from '../helpers/'; import "./formbuilder.scss"; @@ -122,7 +122,34 @@ const FormElement = (props) => { } props.onChange(value); }; - $input = ( onTextChange(e.target.value)} {...id} name={struct.label} type="text" value={struct.value || ""} placeholder={struct.placeholder} readOnly={struct.readonly}/> ); + + const list_id = struct.datalist ? "list_"+Math.random() : null; + $input = ( onTextChange(e.target.value)} {...id} name={struct.label} type="text" value={struct.value || ""} placeholder={struct.placeholder} readOnly={struct.readonly}/> ); + if(list_id != null){ + 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 + ); + }; + $input = ( + + { $input } + + { + filtered(struct.multi, struct.datalist, struct.value).map((item,i) => { + return ( + + ); + } break; case "number": const onNumberChange = (value) => { diff --git a/client/components/formbuilder.scss b/client/components/formbuilder.scss index 93d1f240..d6474f0d 100644 --- a/client/components/formbuilder.scss +++ b/client/components/formbuilder.scss @@ -7,6 +7,7 @@ margin-bottom: 10px; opacity: 0.25; font-size: 0.95em; + line-height: 0.95em; } input::placeholder, textarea::placeholder{ diff --git a/client/helpers/common.js b/client/helpers/common.js index b5735bd8..88c7b1f0 100644 --- a/client/helpers/common.js +++ b/client/helpers/common.js @@ -15,3 +15,13 @@ export function copyToClipboard (str){ document.execCommand("copy"); $input.remove(); } + +export function format(str = ""){ + if(str.length === 0) return str; + return str.split("_") + .map((word, index) => { + if(index != 0) return word; + return word[0].toUpperCase() + word.substring(1); + }) + .join(" "); +} diff --git a/client/helpers/form.js b/client/helpers/form.js index a1010f88..0c817857 100644 --- a/client/helpers/form.js +++ b/client/helpers/form.js @@ -52,3 +52,48 @@ export function createFormBackend(backend_available, backend_data){ obj[backend_data.type] = template; return obj; } + +/* + * return a new list of autocompletion candidates considering the current input + */ +export function autocomplete(values, list) { + if(values.length === 0) return list; + let candidates_input = [], + candidates_output = []; + + for(let i=0; i 1) { + return candidates_input.map((candidate) => { + return candidate.join(", "); + }); + } + return candidates_output.map((candidate, idx) => { + return candidates_input[0] + .concat(candidate) + .join(", ") + .replace(/\,\s?$/, ""); + }); +} diff --git a/client/helpers/index.js b/client/helpers/index.js index 188ed2a0..71de462f 100644 --- a/client/helpers/index.js +++ b/client/helpers/index.js @@ -11,8 +11,7 @@ export { invalidate, http_get, http_post, http_delete, http_options } from './aj export { prompt, alert, confirm } from './popup'; export { notify } from './notify'; export { gid, randomString } from './random'; -export { leftPad, copyToClipboard } from './common'; +export { leftPad, format, copyToClipboard } from './common'; export { getMimeType } from './mimetype'; export { settings_get, settings_put } from './settings'; -export { FormObjToJSON, createFormBackend } from './form'; -export { format } from './text'; +export { FormObjToJSON, createFormBackend, autocomplete } from './form'; diff --git a/client/helpers/text.js b/client/helpers/text.js deleted file mode 100644 index d687c341..00000000 --- a/client/helpers/text.js +++ /dev/null @@ -1,10 +0,0 @@ -export function format(str = ""){ - if(str.length === 0) return str; - return str.split("_") - .map((word, index) => { - - if(index != 0) return word; - return word[0].toUpperCase() + word.substring(1); - }) - .join(" "); -} diff --git a/client/pages/filespage/thing-existing.js b/client/pages/filespage/thing-existing.js index b077dc51..9e7ca9a3 100644 --- a/client/pages/filespage/thing-existing.js +++ b/client/pages/filespage/thing-existing.js @@ -341,6 +341,7 @@ const DateTime = (props) => { const FileSize = (props) => { function displaySize(bytes){ + if(bytes === -1) return ""; if(Number.isNaN(bytes) || bytes === undefined){ return ""; }else if(bytes < 1024){ diff --git a/client/pages/viewerpage/formviewer.js b/client/pages/viewerpage/formviewer.js index 1268f52f..22adc254 100644 --- a/client/pages/viewerpage/formviewer.js +++ b/client/pages/viewerpage/formviewer.js @@ -7,10 +7,7 @@ export class FormViewer extends React.Component { constructor(props){ super(props); this.state = { - form: { - "test": {label: "test", type: "text", "value": null, default: "polo", placeholder: "test"}, - "something": {label: "test", type: "text", "value": null, default: "polo", placeholder: "test"} - } + form: {} }; } @@ -23,13 +20,12 @@ export class FormViewer extends React.Component { } render(){ - console.log(this.state.form); return (
-
+ { return (
+
+ +
+ { struct.description ? (
{struct.description}
) : null } +
+
); }}/> diff --git a/client/pages/viewerpage/formviewer.scss b/client/pages/viewerpage/formviewer.scss index e584e578..de822843 100644 --- a/client/pages/viewerpage/formviewer.scss +++ b/client/pages/viewerpage/formviewer.scss @@ -13,18 +13,43 @@ flex: 1; width: 100%; - .formbuilder{ - label.no-select > div { - display: flex; - line-height: 30px; + .box{ + padding: 25px 20px; + border-radius: 2px; - > span { - display: inline-block; - width: 150px; - max-width: 150px; - text-align: right; - padding-right: 15px; - color: var(--emphasis); + + .formbuilder{ + > div { + margin-bottom: 10px; + @media screen and (max-width: 470px) { margin-bottom: 20px; } + + + label.no-select > div { + display: flex; + line-height: 30px; + @media screen and (max-width: 470px) { + display: block; + line-height: inherit; + span.nothing{ position: absolute; } + } + + > span { + display: inline-block; + width: 160px; + max-width: 160px; + text-align: right; + padding-right: 15px; + color: var(--emphasis); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + + @media screen and (max-width: 470px) { + text-align: left; + font-weight: bold; + } + } + } } } } diff --git a/server/common/config.go b/server/common/config.go index 914414c7..58f52b28 100644 --- a/server/common/config.go +++ b/server/common/config.go @@ -42,6 +42,9 @@ type FormElement struct { ReadOnly bool `json:"readonly"` Default interface{} `json:"default"` Value interface{} `json:"value"` + MultiValue bool `json:"multi,omitempty"` + Datalist []string `json:"datalist,omitempty"` + Order int `json:"-"` } func init() {