Skip to content
This repository was archived by the owner on Nov 22, 2022. It is now read-only.

Commit 3b9ee93

Browse files
Port factorial design generator
1 parent 4a6ce63 commit 3b9ee93

File tree

2 files changed

+158
-7
lines changed

2 files changed

+158
-7
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import React, { Fragment, Component } from 'react'
2+
3+
import { Formik, Form, Field } from 'formik'
4+
import { ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'
5+
6+
import Modal from '../../../../../Modal'
7+
import { Table, DefaultRow } from '../../../../../Form/table'
8+
import { Input } from '../../../../../Form'
9+
10+
// Logic -----------------------------------------------------------------------
11+
// (copied from the library combinatorics implementation)
12+
13+
const product = function* (...sets) {
14+
let thresholds = sets
15+
.map(s => Math.max(s.length, 1)).reverse()
16+
.reduce(
17+
(acc, current, i) => acc.concat([
18+
(acc[i - 1] || 1) * current
19+
]), []
20+
)
21+
.reverse()
22+
23+
for (let counter = 0; counter < thresholds[0]; counter++) {
24+
yield sets.map(
25+
(s, i) => s[Math.floor(counter / (thresholds[i + 1] || 1)) % s.length]
26+
)
27+
}
28+
}
29+
30+
const generateTable = (factors) => {
31+
const names = factors.map(f => f.name)
32+
const levels = factors.map(f => f.levels.split('\n').filter(c => c !== ''))
33+
34+
return {
35+
columns: names.map(name => ({ name, type: 'string' })),
36+
rows: Array.from(product(...levels)),
37+
}
38+
}
39+
40+
// UI --------------------------------------------------------------------------
41+
42+
const TableRow = ({ name, arrayHelpers }) =>
43+
<DefaultRow name={ name } arrayHelpers={ arrayHelpers } wrapper={ Fragment }>
44+
<td>
45+
<Field
46+
name={ `${ name }.name` }
47+
component={ Input }
48+
placeholder="Parameter name"
49+
className="text-monospace"
50+
/>
51+
</td>
52+
<td>
53+
<Field
54+
name={ `${ name }.levels` }
55+
component="textarea"
56+
placeholder="Levels (one line each)"
57+
className="form-control text-monospace"
58+
style={{
59+
height: `${ (2 + 1.5) * 1.6 }em`, // TODO: Re-enable dynamic sizing
60+
minHeight: '80px',
61+
maxHeight: '150px',
62+
}}
63+
/>
64+
</td>
65+
</DefaultRow>
66+
67+
const Header = () =>
68+
<thead>
69+
<tr>
70+
<th />
71+
<th className="px-2 pt-3">Factor</th>
72+
<th className="px-2 pt-3">Levels</th>
73+
<th />
74+
</tr>
75+
</thead>
76+
77+
class FactorialModal extends Component {
78+
constructor(props) {
79+
super(props)
80+
this.state = { isOpen: false }
81+
this.promiseHandlers = {}
82+
}
83+
84+
toggle() {
85+
this.setState({ isOpen: !this.state.isOpen })
86+
}
87+
88+
async show() {
89+
this.toggle()
90+
return new Promise(
91+
(resolve, reject) => {
92+
this.promiseHandlers = {
93+
resolve: result => {
94+
this.setState({ isOpen: false })
95+
return resolve(result)
96+
},
97+
reject
98+
}
99+
}
100+
)
101+
}
102+
103+
render() {
104+
return <Modal
105+
isOpen={ this.state.isOpen }
106+
modalProps={{ size: 'lg' }}
107+
>
108+
<Formik
109+
initialValues={{
110+
factors: [
111+
{ name: 'Factor A', levels: 'Level A1\nLevel A2' },
112+
{ name: 'Factor B', levels: 'Level B1\nLevel B2' },
113+
]
114+
}}
115+
onSubmit={ (values) => {
116+
console.log('in Onsubmit')
117+
const table = generateTable(values.factors)
118+
if (this.promiseHandlers.resolve) {
119+
this.promiseHandlers.resolve(table)
120+
}
121+
} }
122+
>
123+
<Form>
124+
<div className="modal-content">
125+
<ModalHeader toggle={ this.toggle }>
126+
Generate factorial design
127+
</ModalHeader>
128+
<ModalBody>
129+
<p><strong>Please specify the factors in your design, as well as their levels.</strong> <span className="text-muted">The resulting design is fully crossed, meaning that in the loop, every level of a factor is combined with every level of every other factor.</span></p>
130+
<hr style={{ marginBottom: '0' }} />
131+
</ModalBody>
132+
<Table
133+
name="factors"
134+
columns={ 2 }
135+
row={ TableRow }
136+
header={ Header }
137+
defaultItem={{ name: '', levels: '' }}
138+
className="grid border-top-0 mb-0"
139+
/>
140+
<ModalFooter>
141+
<Button outline color="secondary" onClick={ this.toggle }>
142+
Close
143+
</Button>
144+
<Button outline color="primary" type="submit">
145+
Generate
146+
</Button>
147+
</ModalFooter>
148+
</div>
149+
</Form>
150+
</Formik>
151+
</Modal>
152+
}
153+
}
154+
155+
export default FactorialModal

packages/builder/src/components/ComponentOptions/components/Content/Loop/Grid/footer.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import React, { useState } from 'react'
1+
import React, { useState, createRef } from 'react'
22

33
import { ButtonDropdown, Button,
44
DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'
55
import { range } from 'lodash'
66

77
import Icon from '../../../../../Icon'
8-
//import FactorialModal from './components/FactorialModal'
8+
import FactorialModal from '../DesignWizard/FactorialModal'
99
import { useArrayContext } from '../../../../../Form/array'
1010

1111
import Uploader from '../../../../../Uploader'
@@ -29,17 +29,15 @@ const exportGrid = (data, columns) => {
2929

3030
export default ({ addItem, columns }) => {
3131
const [dropdownOpen, setDropdownOpen] = useState(false)
32-
//const factorialModal = createRef()
32+
const factorialModal = createRef()
3333
const { dispatch, overwriteAll } = useArrayContext()
3434

3535
return (
3636
<tfoot>
3737
<tr>
3838
<td />
3939
<td colSpan={ columns }>
40-
{ /*
4140
<FactorialModal ref={ factorialModal } />
42-
*/ }
4341
<ButtonDropdown
4442
size="sm"
4543
className="w-100"
@@ -67,7 +65,6 @@ export default ({ addItem, columns }) => {
6765
}}
6866
/>
6967
<DropdownMenu right>
70-
{ /*
7168
<DropdownItem header>Generate</DropdownItem>
7269
<DropdownItem
7370
onClick={ () => factorialModal.current.show().then(result => {
@@ -76,7 +73,6 @@ export default ({ addItem, columns }) => {
7673
>
7774
Factorial design
7875
</DropdownItem>
79-
*/ }
8076
<DropdownItem divider />
8177
<DropdownItem header>Repeat</DropdownItem>
8278
<DropdownItem

0 commit comments

Comments
 (0)