Skip to content

Instantly share code, notes, and snippets.

@DavidKloucek
Last active January 16, 2022 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DavidKloucek/f3cff595c2ec554b16037baef3e1cf78 to your computer and use it in GitHub Desktop.
Save DavidKloucek/f3cff595c2ec554b16037baef3e1cf78 to your computer and use it in GitHub Desktop.
import { useCallback, useEffect, useState, useRef, Fragment} from 'react';
import React from 'react';
import Select from 'react-select';
const defaultData = {
type: 'GroupControl',
data: {
operator: 'plus',
},
children: [
{
type: 'ManufacturerControl',
data: {
value: []
}
},
{
type: 'GroupControl',
data: {
operator: 'minus'
},
children: [
{
type: 'RadioControl',
data: {
value: "1"
}
},
{
type: 'RadioControl',
data: {
value: "1"
}
},
]
}
]
}
const Dump = (data) => (
<pre style={{'fontSize': '13px'}}>{JSON.stringify(data, null, 4)}</pre>
)
function createDataGroup() {
return {
type: 'GroupControl',
data: {
'operator': 'plus'
},
children: []
}
}
function createDataRadio() {
return {
type: 'RadioControl',
data: {
'value': '1'
}
}
}
function createDataManufacturer() {
return {
type: 'ManufacturerControl',
data: {
'value': []
}
}
}
function fetchManufacturers() {
return new Promise((success, fail) => {
window.setTimeout(() => success([
{label: 'Altisport', value: 1},
{label: 'Alpine Pro', value: 2},
{label: 'Kixmi', value: 3},
]), 1000)
})
}
function ManufacturerControl({setData, depth, deleteMe, ...obj}) {
const [isLoading, setIsLoading] = useState(false)
const [options, setOptions] = useState(null)
async function handleFocus() {
if (options === null) {
setIsLoading(true)
setOptions(await fetchManufacturers())
setIsLoading(false)
}
}
function handleChange(v) {
setData(d => ({...d, data: {
value: v === null ? [] : v.map(x => x.value)
}}))
}
const optionsArr = options === null ? [] : options;
return (
<div>
<label>Značka: </label>
<span style={{width: '350px', display: 'inline-block'}}>
<Select
onFocus={handleFocus}
onChange={handleChange}
value={optionsArr.filter(o => obj.data.value.includes(o.value))}
noOptionsMessage={() => 'Nenalezeno'}
isLoading={isLoading}
loadingMessage={() => 'Načítám..'}
options={optionsArr}
placeholder="Vyberte.."
isMulti
/>
</span>
<button type="button" onClick={deleteMe}>×</button>
</div>
)
}
function GroupControl({setData, depth, deleteMe, ...obj}) {
function toggle() {
console.log('toggle')
setData(d => ({...d,
data: {
...d.data,
operator: d.data.operator === 'plus' ? 'minus' : 'plus'
}
}))
}
function addGroup() {
setData(d => ({...d,
children: d.children.concat(createDataGroup())
}))
}
function addRadio() {
setData(d => ({...d,
children: d.children.concat(createDataRadio())
}))
}
function addManufacturer() {
setData(d => ({...d,
children: d.children.concat(createDataManufacturer())
}))
}
return (
<div style={{
'padding': '9px',
'margin': '9px 6px',
'border': '2px solid '+'rgba(0,0,0,.1)',
'borderRadius': '3px'
}}>
{obj.children.length === 0 && (
<p>Přidejte podmínku nebo skupinu</p>
)}
{obj.children.map((sub, index) => (
<Fragment key={index}>
<Walker
depth={depth}
data={sub}
deleteMe={() => {
setData(d => ({...d, children: d.children.filter((x, i) => i !== index)}))
}}
setData={(d) => {
setData(s => {
return {...s, children: s.children.map((x, i) => {
return i === index ? d(x) : x
})}
})
}}
/>
{index+1 !== obj.children.length ? (
<p onClick={toggle} className={"op-"+obj.data.operator}>
{obj.data.operator === 'plus' ? (
<strong style={{'color': 'green'}}>Zároveň</strong>
) : (
<strong style={{'color': 'red'}}>Nebo</strong>
)}
</p>
) : (
<Fragment>
</Fragment>
)}
</Fragment>
))}
<div style={{
'padding': '10px 0 0 0',
'margin': '15px 0 0 0',
'borderTop': '2px solid rgba(0,0,0,.1)'
}}>
Přidat:
<button type="button" onClick={addGroup}>Skupinu</button>
<button type="button" onClick={addManufacturer}>Značku</button>
<button type="button" onClick={addRadio}>Radio</button>
.......
{depth > 1 && (
<button type="button" onClick={deleteMe}>×</button>
)}
</div>
</div>
)
}
function RadioControl({data, setData, depth, deleteMe, ...other}) {
function onChange(v) {
setData(d => ({...d, data: {
value: v.target.value
}}))
}
return (
<div>
<label>
<input type="radio" onChange={onChange} value="1" checked={data.value === '1'} /> Ano
</label>
<label style={{'margin': '0 0 0 15px'}}>
<input type="radio" onChange={onChange} value="2" checked={data.value === '2'} /> Ne
</label>
<button type="button" onClick={deleteMe}>×</button>
</div>
)
}
const components = {
GroupControl, RadioControl, ManufacturerControl
}
function Walker({data, setData, depth, deleteMe}) {
if (!('type' in data)) {
console.log(data)
throw new Error('Chybi "type" v '+JSON.stringify(data, null, 4));
}
if (!(data.type in components)) {
console.log(data.type, components)
throw new Error()
}
let comp = React.createElement(components[data.type], {
...data,
depth: depth+1,
deleteMe,
setData: (d) => {
setData(s => d(s))
}
})
return (
<Fragment>
{comp}
</Fragment>
)
}
export function Rules() {
const [data, setData] = useState(defaultData)
return (
<div>
<Walker
data={data}
setData={(d) => {
setData(s => d(s))
}}
depth={0}
/>
<Dump {...data} />
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment