feat: add table and filter nodule

This commit is contained in:
ysandler 2020-07-14 23:27:26 -05:00 committed by Joshua Shoemaker
parent 62502b3b46
commit 9ca49b9ef0
15 changed files with 438 additions and 15 deletions

21
package-lock.json generated
View File

@ -1852,6 +1852,11 @@
}
}
},
"@types/uuid": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz",
"integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw=="
},
"@types/yargs": {
"version": "13.0.9",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.9.tgz",
@ -12841,6 +12846,22 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
},
"uuidv4": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.1.1.tgz",
"integrity": "sha512-ZplGb1SHFMVH3l7PUQl2Uwo+FpJQV6IPOoU+MjjbqrNYQolqbGwv+/sn9F+AGMsMOgGz3r9JN3ztGUi0VzMxmw==",
"requires": {
"@types/uuid": "8.0.0",
"uuid": "8.2.0"
},
"dependencies": {
"uuid": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz",
"integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q=="
}
}
},
"v8-compile-cache": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",

View File

@ -11,7 +11,8 @@
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^0.88.2"
"semantic-ui-react": "^0.88.2",
"uuidv4": "^6.1.1"
},
"scripts": {
"start": "react-scripts start",

View File

@ -0,0 +1,40 @@
import { FilterNodule, JoinNodule, TransformNodule } from 'dmein'
import { uuid } from 'uuidv4'
let instance = null
class Nodules {
constructor () {
if (!instance) instance = this
this.collection = []
return instance
}
addNewFilterNodule = props => {
try {
const newFilterNodule = new FilterNodule({
id: props.id || uuid(),
label: props.label,
tables: props.tables,
filterParams: props.filterParams,
filterType: props.filterType
})
this.collection.push(newFilterNodule)
} catch (err) {
console.error(err)
}
}
removeById = id => {
const indexToRemove = this.collection.findIndex(n => n.id === id)
if (this.collection.length === 1 && indexToRemove > -1) {
this.collection = []
}
else {
const modifiedCollection = this.collection.splice(indexToRemove, 1)
this.collection = modifiedCollection
}
}
}
export default Nodules

View File

@ -1,14 +1,42 @@
import { Table } from 'dmein'
import { uuid } from 'uuidv4'
let instance = null
class Tables {
constructor () {
if (!instance) instance = this
console.log(d)
this.collection = []
return instance
}
addNewTable = table => {
try {
const newTable = new Table({
id: table.id || uuid(),
label: table.label,
rows: table.rows
})
this.collection.push(newTable)
} catch (err) {
console.error(err)
}
}
removeTableById = id => {
const indexToRemove = this.collection.findIndex(t => t.id === id)
if (this.collection.length === 1 && indexToRemove > -1) {
this.collection = []
}
else {
const modifiedCollection = this.collection.splice(indexToRemove, 1)
this.collection = modifiedCollection
}
}
getCollectionProps = () => this.collection.map(table => table.getProperties())
getTableByLabel = label => this.collection.find(t => label === t.label)
}
export default Tables

View File

@ -0,0 +1,33 @@
import Tables from '../Collections/Tables'
import Nodules from '../Collections/Nodules'
class CreateNoduleController {
constructor() {
this.nodules = new Nodules()
this.tables = new Tables()
this.updatedNodulesEvent = new Event('updateNodules')
}
addNewFilterNodule = (props) => {
const { label, filterType, filterParams, tablesToImportByLabel } = props
const tables = tablesToImportByLabel.map(label => {
return this.tables.getTableByLabel(label)
})
this.nodules.addNewFilterNodule({
label,
filterType,
filterParams,
tables
})
console.log(this.nodules)
document.dispatchEvent(this.updatedNodulesEvent)
}
deleteNodule = id => {
this.nodules.removeTableById(id)
document.dispatchEvent(this.updatedNodulesEvent)
}
}
export default CreateNoduleController

View File

@ -2,9 +2,22 @@ import Tables from '../Collections/Tables'
import FileAccess from '../Services/FileAccess'
class CreateTableController {
constructor(props) {
constructor() {
this.tables = new Tables()
this.fileAccess = new FileAccess()
this.updatedTablesEvent = new Event('updateTables')
}
submitLocalFile = async submition => {
const { label, file } = submition
this.fileAccess.setFile(file)
const fileData = await this.fileAccess.readFile()
this.tables.addNewTable({
label: label,
rows: fileData
})
document.dispatchEvent(this.updatedTablesEvent)
}
}

View File

@ -0,0 +1,15 @@
import Tables from '../Collections/Tables'
class TableListController {
constructor() {
this.tables = new Tables()
this.updatedTablesEvent = new Event('updateTables')
}
deleteTable = id => {
this.tables.removeTableById(id)
document.dispatchEvent(this.updatedTablesEvent)
}
}
export default TableListController

View File

@ -2,12 +2,16 @@ import React, { Component } from 'react'
import './App.css'
import 'semantic-ui-css/semantic.min.css'
import CreateTableForm from './CreateTable/CreateTableForm'
import TableList from './TableList/TableList'
import CreateNodule from './CreateNodule/CreateNodule'
class App extends Component {
render = () => {
return (
<div className='App'>
<CreateTableForm />
<CreateNodule />
<TableList />
</div>
)
}

View File

@ -0,0 +1,76 @@
import React, { Component } from 'react'
import { Input, Dropdown, Grid } from 'semantic-ui-react'
import './CreateNodule.css'
class CreateFilterNoduleForm extends Component {
constructor () {
super()
this.state = { filterType: '' }
this.keyInput1 = React.createRef()
this.keyInput2 = React.createRef()
this.keyInput3 = React.createRef()
this.valueInput1 = React.createRef()
this.valueInput2 = React.createRef()
this.valueInput3 = React.createRef()
document.addEventListener('updateTables', this.updateTableList)
}
handleChange = (e, value) => {
this.setState({ filterType: value.value })
}
getFilterProperties = () => {
let filterParams = {}
if (this.keyInput1.current.inputRef.current.value)
filterParams[this.keyInput1.current.inputRef.current.value] = this.valueInput1.current.inputRef.current.value
if (this.keyInput2.current.inputRef.current.value)
filterParams[this.keyInput2.current.inputRef.current.value] = this.valueInput2.current.inputRef.current.value
if (this.keyInput3.current.inputRef.current.value)
filterParams[this.keyInput3.current.inputRef.current.value] = this.valueInput3.current.inputRef.current.value
return {
filterType: this.state.filterType,
filterParams
}
}
render = () => {
return (
<div className='CreateFiltrerNoduleForm'>
<Dropdown
placeholder='Select a Comparison Type'
fluid
selection
options={[
{key: 'EQUAL', text: 'EQUAL', value: 'EQUAL'},
{key: 'GREATER', text: 'GREATER THAN', value: 'GREATER'},
{key: 'GREATEREQUAL', text: 'GREATER THEN EQUAL TO', value: 'GREATEREQUAL'},
{key: 'LESSER', text: 'LESSER THAN', value: 'LESSER'},
{key: 'LESSEREQUAL', text: 'LESSER THEN EQUAL TO', value: 'LESSEREQUAL'}
]}
onChange={this.handleChange}
/>
<Grid columns={2} relaxed='very' stackable>
<Grid.Column>
<Input placeholder='Key' ref={this.keyInput1} style={{ width: '115px' }} />
<Input placeholder='Key' ref={this.keyInput2} style={{ width: '115px' }} />
<Input placeholder='Key' ref={this.keyInput3} style={{ width: '115px' }} />
</Grid.Column>
<Grid.Column>
<Input placeholder='Value' ref={this.valueInput1} style={{ width: '115px' }} />
<Input placeholder='Value' ref={this.valueInput2} style={{ width: '115px' }} />
<Input placeholder='Value' ref={this.valueInput3} style={{ width: '115px' }} />
</Grid.Column>
</Grid>
</div>
)
}
}
export default CreateFilterNoduleForm

View File

@ -0,0 +1,6 @@
.CreateNodule {
width: 380px;
padding: 40px;
border-radius: 6px;
border: solid rgb(240, 240, 240) 1px;
}

View File

@ -0,0 +1,84 @@
import React, { Component } from 'react'
import { Button, Input, Header, Dropdown, Table } from 'semantic-ui-react'
import CreateFilterNoduleForm from './CreateFilterNoduleForm'
import './CreateNodule.css'
import Tables from '../../Collections/Tables'
import CreateNoduleController from '../../Controllers/CreateNoduleController'
import TableSelect from './TableSelect'
class CreateNodule extends Component {
constructor () {
super()
this.state = {
noduleType: '',
tablesToImportByLabel: []
}
this.tables = new Tables()
this.controller = new CreateNoduleController()
this.filterNoduleForm = React.createRef()
document.addEventListener('updateTables', this.updateTableList)
}
handleChange = (e, value) => {
this.setState({ noduleType: value.value })
}
handleSubmit = () => {
if (this.state.noduleType === 'filter')
console.log(this.filterNoduleForm.current.getFilterProperties())
}
updateTableList = () => {
this.setState({tables: this.tables.getCollectionProps()})
}
renderNoduleForm = () => {
if (this.state.noduleType === 'filter') return <CreateFilterNoduleForm ref={this.filterNoduleForm} />
else return ''
}
render = () => {
return (
<div className='CreateNodule'>
<Header as='h3'>Create Nodule</Header>
<Input
placeholder='Nodule Label'
ref={this.tableLabelInput}
icon='tags'
style={{ width: '300px' }}
/>
<br />
<Dropdown
placeholder='Select a Nodule Type'
options={[
{key: 'Join Nodule', text: 'Join Nodule', value: 'join', icon: 'sitemap'},
{key: 'Filter Nodule', text: 'Filter Nodule', value: 'filter', icon: 'filter'},
{key: 'Transform Nodule', text: 'Transform Nodule', value: 'transform', icon: 'sliders horizontal'},
]}
fluid
selection
style={{ width: '300px' }}
onChange={this.handleChange}
/>
<TableSelect />
{ this.renderNoduleForm() }
<div className='creatTableFormSubmitButtons'>
<Button content='Cancel' secondary />
<Button content='Confirm' primary onClick={this.handleSubmit} />
</div>
</div>
)
}
}
export default CreateNodule

View File

@ -0,0 +1,49 @@
import React, { Component } from 'react'
import { Label, Icon } from 'semantic-ui-react'
import './CreateNodule.css'
import Tables from '../../Collections/Tables'
class TableSelect extends Component {
constructor () {
super()
this.tables = new Tables()
this.state = {
selectedTables: [],
tables: this.tables.getCollectionProps()
}
document.addEventListener('updateTables', this.updateTableList)
}
toggleSelect = label => {
console.log(label)
}
updateTableList = () => {
this.setState({tables: this.tables.getCollectionProps()})
}
renderTableLabels = () => {
const { selectedTables, tables } = this.state
const tableLabelElements = tables.map(t => {
const isSelected = label => selectedTables.includes(label)
return (
<Label onClick={() => this.toggleSelect(t.label)}>
{t.label}
{ isSelected ? <Icon name='delete' /> : '' }
</Label>
)
})
return tableLabelElements
}
render = () => {
return (
<div className='TableSelect'>
{ this.renderTableLabels() }
</div>
)
}
}
export default TableSelect

View File

@ -2,30 +2,29 @@ import React, { Component } from 'react'
import { Button, Input, Header } from 'semantic-ui-react'
import './CreateTableForm.css'
import FileAccess from '../../Services/FileAccess'
const fileAccess = new FileAccess()
import CreateTableController from '../../Controllers/CreateTableController'
class CreateTableForm extends Component {
constructor () {
super()
this.controller = new CreateTableController()
this.tableLabelInput = React.createRef()
this.tableFileInput = React.createRef()
}
handleSubmit = async e => {
e.preventDefault()
const label = this.tableLabelInput.current.inputRef.current.value
const file = this.tableFileInput.current.inputRef.current.files[0]
fileAccess.setFile(this.tableFileInput.current.inputRef.current.files[0])
const fileData = await fileAccess.readFile()
console.log(fileData)
this.controller.submitLocalFile({ label, file })
}
render = () => {
return (
<div className='CreateTableForm'>
<Header as='h3'>Create Table From File</Header>
<form onSubmit={this.handleSubmit}>
<div>
<Input
placeholder='Table Label'
ref={this.tableLabelInput}
@ -42,11 +41,11 @@ class CreateTableForm extends Component {
style={{ width: '300px' }}
/>
<div className='creatTableFormSubmitButtons'>
<Button content='Confirm' primary />
<Button content='Confirm' secondary />
<div className='creatTableFormSubmitButtons'>
<Button content='Cancel' secondary />
<Button content='Confirm' primary onClick={this.handleSubmit} />
</div>
</div>
</form>
</div>
)
}

View File

View File

@ -0,0 +1,54 @@
import React, { Component } from 'react'
import { Card, Icon, CardContent } from 'semantic-ui-react'
import './TableList.css'
import Tables from '../../Collections/Tables'
import TableListController from '../../Controllers/TableListController'
class TableList extends Component {
constructor () {
super()
this.tables = new Tables()
this.controller = new TableListController()
this.state = { tables: this.tables.getCollectionProps() }
document.addEventListener('updateTables', this.updateTableList)
}
updateTableList = () => {
this.setState({tables: this.tables.getCollectionProps()})
}
renderTableListElements = () => {
const { tables } = this.state
const tableListElements = tables.map(t =>
<Card key={t.id} id={t.id}>
<Card.Content>
<Card.Header>{ t.label }</Card.Header>
<Card.Meta>{`${t.rows.length} rows`}</Card.Meta>
</Card.Content>
<CardContent
extra
onClick={() => { this.controller.deleteTable(t.id) }}
style={{ cursor: 'pointer' }}
>
Delete <Icon name='trash' />
</CardContent>
</Card>
)
return tableListElements
}
render = () => {
return (
<div className='TableList'>
<Card.Group>
{ this.renderTableListElements() }
</Card.Group>
</div>
)
}
}
export default TableList