Recursive keylist

This commit is contained in:
Chen Asraf
2018-01-24 23:57:10 +02:00
parent da395f46da
commit 2317ac62a8
3 changed files with 116 additions and 60 deletions

View File

@@ -4,6 +4,12 @@ export interface IProps {
}
export interface IState {
keyList: string[]
keyList: KeyItem[]
viewKey: string
}
export interface KeyItem {
label: string,
path: string
children?: KeyItem[]
}

View File

@@ -6,6 +6,7 @@ import SelectBox, { Option, styles as selectBoxStyle } from 'components/SelectBo
import Button from 'components/Button/Button'
import axios, { AxiosResponse } from 'axios'
import Dispatcher, { register, dispatch, ActionTypes, StoreKeys } from 'common/Dispatcher'
import * as classNames from 'classnames'
class KeyList extends React.Component<I.IProps, I.IState> {
private listeners: string[]
@@ -16,6 +17,15 @@ class KeyList extends React.Component<I.IProps, I.IState> {
keyList: this.keyListFromObject(props.store.get(StoreKeys.Response, {})),
viewKey: props.store.get(StoreKeys.ViewKey, '')
}
console.debug(this.keyListFromObject({
one: {
two: 2,
three: {
four: 4,
five: 5
}
}
}))
}
public componentWillMount() {
@@ -39,45 +49,66 @@ class KeyList extends React.Component<I.IProps, I.IState> {
this.listeners.forEach(l => Dispatcher.unregister(l))
}
private keyListFromObject(data: any) {
return [''].concat(Object.keys(data))
private keyListFromObject(data: any, relativePath: string = ''): I.KeyItem[] {
const list: I.KeyItem[] = []
if (relativePath === '') {
list.push({
label: '[Response]',
path: '',
})
}
Object.keys(data).forEach((key) => {
const newPath = relativePath ? [relativePath, key].join('.') : key
let children
if (typeof data[key] !== 'string' && typeof data[key] !== 'number' &&
data[key].constructor !== Array && Object.keys(data[key]).length) {
children = this.keyListFromObject(data[key], newPath)
}
list.push({ label: key, path: newPath, children })
})
return list
}
private selectItem(key: string) {
this.setState({ viewKey: key }, () => {
dispatch(ActionTypes.UPDATE_VIEWKEY, key)
private selectItem(key: I.KeyItem) {
this.setState({ viewKey: key.path }, () => {
dispatch(ActionTypes.UPDATE_VIEWKEY, key.path)
})
}
private get keyListElements() {
private keyListElements(keyList: I.KeyItem[], depth: number = 0) {
const fullData = this.props.store.get(StoreKeys.Response, {})
const viewKey = this.state.viewKey
return this.state.keyList.map((key: string) => {
const className = [
css.item,
key === '' || (fullData[key] && fullData[key].constructor === Array) ? css.valid : '',
viewKey === key ? css.selected : ''
].join(' ')
return keyList.map((key: I.KeyItem) => {
const className = classNames(css.item, {
[css.selected]: viewKey === key.path
})
return (
<div className={className}
key={key}
onClick={(e) => this.selectItem(key)}>
{key === '' ? '[Response]' : key}
<div key={key.path}>
<div className={className}
style={{marginLeft: depth * 20 + 'px'}}
onClick={(e) => this.selectItem(key)}>
{key.label}
</div>
{key.children && key.children.length ? (
this.keyListElements(key.children, depth + 1)
) : ''}
</div>
)
})
}
render() {
const classNames = [
css.KeyList,
this.props.className || ''
].join(' ')
const className = classNames(css.KeyList, this.props.className)
return (
<div className={classNames}>
{this.keyListElements}
<div className={className}>
{this.keyListElements(this.state.keyList)}
</div>
)
}

View File

@@ -24,16 +24,16 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
this.listeners = [
D.register(D.ActionTypes.UPDATE_RESPONSE, (response) => {
const viewKey = this.props.store.get(D.StoreKeys.ViewKey)
if (viewKey && Object.keys(response).indexOf(viewKey) > -1) {
response = response[viewKey]
if (viewKey) {
response = this.objectByPath(response, viewKey)
}
this.setState({ response })
}),
D.register(D.ActionTypes.UPDATE_VIEWKEY, (viewKey) => {
let resp = this.props.store.get(D.StoreKeys.Response, {})
resp = resp && viewKey && resp.hasOwnProperty(viewKey) ? resp[viewKey] : resp
this.setState({ response: resp })
let response = this.props.store.get(D.StoreKeys.Response, {})
response = response && viewKey ? this.objectByPath(response, viewKey) : response
this.setState({ response: response })
}),
]
}
@@ -42,6 +42,16 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
this.listeners.forEach(listener => D.AppDispatcher.unregister(listener))
}
private objectByPath(obj: any, key: string) {
return key.split('.').reduce((acc, cur, i) => {
if (acc.hasOwnProperty(cur)) {
return acc[cur]
} else {
throw new Error('invalid key!')
}
}, obj)
}
private sortBy(key: string) {
this.setState((cur) => ({
sortKey: key,
@@ -81,8 +91,8 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
// numbers are matching
if (typeof a[key] === 'number' && typeof b === 'number' ||
isFinite(a[key]) && isFinite(b[key])) {
const out = parseFloat(a[key]) - parseFloat(b[key])
return desc ? -out : out
const result = parseFloat(a[key]) - parseFloat(b[key])
return desc ? -result : result
}
if (a[key] > b[key]) {
@@ -137,40 +147,55 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
return response
}
private filterInput() {
return (
<input className={css.filterInput}
type="text" value={this.state.filter}
placeholder={`Filter data (e.g.: first_name="John" age>=32)`}
onChange={(e) => this.setState({ filter: e.target.value })} />
)
}
private table() {
const keys = Object.keys(this.state.response && this.state.response[0] || {})
const colAmt = keys.length
return (
<div className={css.table}
style={{gridTemplateColumns: `repeat(${colAmt}, auto)`}}>
{this.filterInput()}
{this.columns()}
{this.processedResponse().map((row, i) => {
const cls = (j) => {
return classNames(css.cell, {
[css.rowStart]: j % colAmt === 0,
[css.rowEnd]: j % colAmt === colAmt - 1,
})
}
return (
<RObject className={cls}
key={`row-${i}`}
data={row} />
)
})}
</div>
)
}
private getRObjectList() {
const { response } = this.state
let colAmt
if (response && response.constructor === Array) {
const keys = Object.keys(response[0] || {})
colAmt = keys.length
return (
<div className={css.table}
style={{gridTemplateColumns: `repeat(${colAmt}, auto)`}}>
{this.columns()}
{this.processedResponse().map((row, i) => {
const cls = (j) => {
return classNames(css.cell, {
[css.rowStart]: j % colAmt === 0,
[css.rowEnd]: j % colAmt === colAmt - 1,
})
}
return (
<RObject className={cls}
key={`row-${i}`}
data={row} />
)
})}
</div>
)
return this.table()
}
colAmt = Object.keys(response || {})
return (
<div className={css.table}
style={{gridTemplateRows: `repeat(${colAmt}, auto)`}}>
style={{gridTemplateRows: `repeat(${colAmt}, min-content)`}}>
<RObject data={response} />
</div>
)
@@ -181,16 +206,10 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
css.ResponseRepr,
this.props.className
].join(' ')
const repr = this.getRObjectList()
return (
<div className={className}>
<input className={css.filterInput}
type="text" value={this.state.filter}
placeholder={`Filter data (e.g.: first_name="John" age>=32)`}
onChange={(e) => this.setState({ filter: e.target.value })} />
{repr}
{this.getRObjectList()}
</div>
)
}