render.app.frontend/src/components/forms/formGenerator.js

670 lines
21 KiB
JavaScript

import React, { useState } from 'react'
import passwordVisibleImg from '../../images/password-visible.png'
import passwordHiddenImg from '../../images/password-hidden.png'
/**
*
* @param { [ {}, {}, ...{} ] } inputList - list of dicts with info about input
* @param { [] } refList - react ref objects list for handler validation
* @param { } action - fetch method
*/
export const FormGenerator = ({
inputList, refList,
action
}) => {
const handler = async (event) => {
event.preventDefault()
if ( inputList[0].action === 'Async' ) {
await action(refList)
} else if (
inputList[0].action === 'Download'
|| inputList[0].action === 'Upload'
) {
await action()
} else {
for (let i = 0; i < refList.length; i++) {
if (refList[i].current.value === ''
&& inputList[0].action !== 'Update'
|| i === 0
&& refList.length !== 1
) {
refList[i].current.focus()
} else if (i === refList.length - 1) {
await action(refList)
}
}
}
}
let info
return (
<form onSubmit={event => handler(event)} className="form">
{
inputList.map((input, key) => {
if (input.type === 'info') {
info = input
} else if (input.type === 'text') {
return (
<TextInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'password') {
return (
<PasswordInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'links-listing') {
return (
<DownloadFilesListInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'file') {
return (
<UploadInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'choice-listing') {
return (
<ChoiceListingGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'range') {
return (
<RangeInputGenerator
input={input}
info={info}
key={key}
/>
)
} else if (input.type === 'vector') {
return (
<VectorInputGenerator
input={input}
info={info}
key={key}
/>
)
}
})
}
{
info.button_value === ''
? <></>
: <button
type='submit'
disabled={ info.allowButtonAction }
className={ info.allowButtonAction === false ? "button-disabled" : "" }
>
{ info.button_value }
</button>
}
</form>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'text',
* name: 'name',
* ref: React.createRef(),
* onChange: null OR validationFunc
* validationInfo: null OR useState("")
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update',
* endpoint: 'Album'
* } } info - information about form
*/
const TextInputGenerator = ({
input, info
}) => {
const [textInputValidationInfo, setTextInputValidationInfo] = useState("Empty")
const inputRegex = /^[^'";<>=]+$/
const defaultValidation = (event) => {
if (event.target.value === "") {
setTextInputValidationInfo("Empty")
} else if (!inputRegex.test(event.target.value)) {
setTextInputValidationInfo("Please provide correct value")
} else {
setTextInputValidationInfo("Success")
}
}
return (
<div className="form-field">
<label>
{input.name}
</label>
<input
id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off'
ref={input.ref}
onChange={ input.onChange === null ? defaultValidation : input.onChange}
className={
[ "Empty", "Success"].includes(
input.validationInfo === null ? textInputValidationInfo : input.validationInfo
) ? "" : "input-incorrect"
}
/>
<div
className="popup"
style={
[ "Empty", "Success"].includes(
input.validationInfo === null ? textInputValidationInfo : input.validationInfo
) ? {"display": "none", "height": "0px"} : {"display": "block"}
}
>
<div className="popup-content">
{ input.validationInfo === null ? textInputValidationInfo : input.validationInfo }
</div>
</div>
</div>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'password',
* name: 'name',
* ref: React.createRef()
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const PasswordInputGenerator = ({
input, info
}) => {
const [contentIsHidden, setContentIsHidden] = useState(true)
return (
<div className="form-field">
<label>
{ input.name }
</label>
<div className="horizontal-container-input">
<input
id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off'
ref={input.ref}
type={ contentIsHidden ? 'password' : 'text' }
onChange={input.onChange}
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-incorrect" }
/>
<img
src={ contentIsHidden ? passwordHiddenImg : passwordVisibleImg }
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-visible-incorrect" }
onClick={ () => { setContentIsHidden(!contentIsHidden) } }
/>
</div>
<div
className="popup"
style={ [ "Empty", "Success"].includes(input.validationInfo) ? {"display": "none", "height": "0px"} : {"display": "block"} }
>
<div className="popup-content">
{ input.validationInfo }
</div>
</div>
</div>
)
}
const ObjectIterator = ({
object, divPlacer
}) => {
return(
<>
{
typeof object == "object" ?
Object.keys( object ).map(
( key ) => {
return (
<div key={ key }>
{ divPlacer( object[key] ) }
</div>
)
}
)
:
object
}
</>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'drop-box',
* name: 'name',
* values: list,
* link: link to the file
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const DownloadFilesListInputGenerator = ({
input, info
}) => {
return (
<div
id={input.name + info.action + info.endpoint + 'DropBox'}
>
{input.name + ':'}
{
input.values.length == 0 ?
() => {
return (
<div>
empty
</div>
)
}
: input.values.map( (item, index) => {
return (
<>
<div
key={ info.action + '_element_' + index }
ls={ item }
>
{
typeof item == 'string' ?
item
: Object.keys(item).map(
( key, index ) => {
return(
<div style={{ paddingLeft: '10px' }}>
{ key + ': ' }
{
typeof item[key] === "object" ?
Object.keys( item[key] ).map(
( key_two, index ) => {
return (
<div style={{ paddingLeft: '20px' }}>
{ key_two + ': '}
{
typeof item[key][key_two] == 'object' ?
Object.keys( item[key][key_two] ).map(
(key_three, index) => {
return (
<div style={{ paddingLeft: '30px' }}>
{ key_three + ": " }
{ "x: " + item[key][key_two][key_three].x + ", " }
{ "y: " + item[key][key_two][key_three].y + ", " }
{ "z: " + item[key][key_two][key_three].z + ", " }
</div>
)
}
)
:
item[key][key_two]
}
</div>
)
}
)
:
item[key]
}
</div>
)
}
)
}
<br />
<br />
<a
href={ input.link + index + '/' }
>
download
</a>
</div>
<div style={{ width: '100%', height: '1px', backgroundColor: '#008000' }}>
</div>
</>
)
}
)
}
</div>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'chice-listing',
* name: 'name',
* values: list,
* ref: React.createRef()
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const ChoiceListingGenerator = ({
input, info
}) => {
const __handleRef = ( event ) => {
event.preventDefault()
input.ref.current = {
value: event.target.value
}
}
return (
<div
id={input.name + info.action + info.endpoint + 'ChoiceListing'}
>
{input.name + ':'}
<select
onChange={ (event) => __handleRef( event ) }
>
<option
value={ '--Select-Model--' }
>
--Select-Model--
</option>
{
input.values.map( (item) => {
return (
<option
key={ item.model }
value={ item.model }
>
{ item.model.replace('.blend', '') }
</option>
)
}
)
}
</select>
</div>
)
}
/**
* Upload file input generator, example:
* @param {
* {
* type: 'file',
* name: 'name',
* endpoint: 'Album',
* fileType: 'image' or 'audio',
* dropInfo: dropInfo, setDropInfo: setDropInfo(), #useState
* file: file, setFile: setFile() #useState
* } } input -
*/
const UploadInputGenerator = ({
input, info
}) => {
const onLoadFile = async (event) => {
event.preventDefault()
let data = event.target.files[0]
// input.setFile(await toBase64(data))
input.setFile( data )
setDropInfos(data.name, data.size)
}
const onLoadFileDrop = async (event) => {
event.preventDefault()
event.persist()
let data = event.dataTransfer.files[0]
// input.setFile(await toBase64(data))
input.setFile( data )
setDropInfos(data.name, data.size)
}
const toBase64 = async (file) => new Promise((resolve, reject) => {
let fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = () => resolve(fileReader.result)
fileReader.onerror = error => reject(error)
})
const setDropInfos = (name, size) => {
input.setDropInfo(
'name: "'
+ name
+ '"\nsize: '
+ (Math.round(size / 100 + 'e-2') / 100)
+ ' MB'
)
}
return (
<div onDrop={event => onLoadFileDrop(event)} >
<pre style={{ marginLeft: '40px' }}>
{input.dropInfo}
</pre>
<input
style={{ marginTop: '-55px' }}
id={input.name + info.action + info.endpoint + 'Input'}
className='upload_input'
type='file'
accept={input.fileType + '/*'}
autoComplete='off'
onChange={event => onLoadFile(event)}
/>
</div>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'range',
* name: 'name',
* min: min range value,
* max: max range value,
* step: step of value,
* unit: unit of range value,
* ref: React.createRef()
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const RangeInputGenerator = ({
input, info
}) => {
let name = input.name + info.action + info.endpoint + 'Input'
const [value, setValue] = useState(0)
return (
<div>
<div>
{input.name + ': ' + value + ' ' + input.unit }
</div>
<input
style={ { width: '380px' } }
id={name}
name={name}
min={input.min}
max={input.max}
defaultValue={input.min}
step={input.step}
ref={input.ref}
type='range'
onChange={ event => setValue( event.target.value ) }
/>
</div>
)
}
const RangeGenerator = ({
key,
label,
labelStyle,
valueStyle,
style,
name,
unit,
min,
max,
defaultValue,
step,
reference
}) => {
const [value, setValue] = useState(0)
return (
<div style={ { display: 'flex' } }>
<div style={ labelStyle }>
{ label + ': ' }
</div>
<input
key={key}
style={ style }
id={ name }
name={ name }
min={min}
max={max}
defaultValue={ defaultValue }
step={step}
ref={reference}
type='range'
onChange={ event => setValue( event.target.value ) }
/>
<div style={ valueStyle }>
{ value + ' ' + unit }
</div>
</div>
)
}
/**
* Text input generator, example:
* @param {
* {
* type: 'vector',
* name: 'name',
* refDict:
* {
* x: React.createRef(),
* y: React.createRef(),
* z: React.createRef()
* }
* } } input - basic text input
* @param {
* {
* type: 'info',
* action: 'Update'
* endpoint: 'Album'
* } } info - information about form
*/
const VectorInputGenerator = ({
input, info
}) => {
return (
<div style={ { width: '' } }>
<div style={ { width: '100%' } }>
{ input.name }
</div>
<div style={ { display: 'flex' } }>
{
Object.keys(input.refDict).map( (key) => {
let name = input.name + key + info.action + info.endpoint + 'Input'
return (
<div style={ { display: 'flex', width: '140px' } }>
<RangeGenerator
key={key}
label={key}
labelStyle={ { width: '5px', marginTop: '15px' } }
valueStyle={ { width: '5px', marginTop: '15px', marginLeft: '5px' } }
style={ { width: '80px' } }
name={name}
unit={ '' }
min={input.min[key]}
max={input.max[key]}
defaultValue={ input.min[key] > 0 ? input.min[key] : 0 }
step={0.1}
reference={input.refDict[key]}
/>
</div>
)
}
)
}
</div>
</div>
)
}
export default FormGenerator