feature (form): form pages

This commit is contained in:
Mickael KERJEAN
2019-01-28 01:09:03 +11:00
parent 9e6ab92201
commit aaf33996c8
10 changed files with 135 additions and 32 deletions

View File

@ -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 = ( <Input onChange={(e) => 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 = ( <Input list={list_id} onChange={(e) => 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 = (
<span>
{ $input }
<datalist id={list_id}>
{
filtered(struct.multi, struct.datalist, struct.value).map((item,i) => {
return ( <option key={i} value={item} /> );
})
}
</datalist>
</span>
);
}
break;
case "number":
const onNumberChange = (value) => {

View File

@ -7,6 +7,7 @@
margin-bottom: 10px;
opacity: 0.25;
font-size: 0.95em;
line-height: 0.95em;
}
input::placeholder, textarea::placeholder{

View File

@ -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(" ");
}

View File

@ -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<list.length; i++){
const last_value = values[values.length - 1];
if(list[i].indexOf(last_value) === 0){
let tmp = JSON.parse(JSON.stringify(values))
tmp[values.length - 1] = list[i];
if(list[i] === last_value){
candidates_input = [tmp];
} else {
candidates_input.push(tmp)
}
continue
}
if(values.indexOf(list[i]) === -1){
candidates_output.push(list[i]);
}
}
if(candidates_input.length === 0){
candidates_input = [values]
}
candidates_output = [""].concat(candidates_output);
if(candidates_input.length > 1) {
return candidates_input.map((candidate) => {
return candidate.join(", ");
});
}
return candidates_output.map((candidate, idx) => {
return candidates_input[0]
.concat(candidate)
.join(", ")
.replace(/\,\s?$/, "");
});
}

View File

@ -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';

View File

@ -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(" ");
}

View File

@ -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){

View File

@ -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 (
<div className="component_formviewer">
<MenuBar title={this.props.filename} download={this.props.data} />
<div className="formviewer_container">
<Container>
<form className="sticky">
<form className="sticky box">
<FormBuilder form={this.state.form} onChange={this.onChange.bind(this)} render={ ($input, props, struct, onChange) => {
return (
<label className={"no-select"}>
@ -41,6 +37,12 @@ export class FormViewer extends React.Component {
{ $input }
</div>
</div>
<div>
<span className="nothing"></span>
<div style={{width: '100%'}}>
{ struct.description ? (<div className="description">{struct.description}</div>) : null }
</div>
</div>
</label>
);
}}/>

View File

@ -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;
}
}
}
}
}
}

View File

@ -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() {