From 2317ac62a819d11b049f8bfab3813574b0cf932a Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Wed, 24 Jan 2018 23:57:10 +0200 Subject: [PATCH] Recursive keylist --- src/components/KeyList/KeyList.module.d.ts | 8 +- src/components/KeyList/KeyList.tsx | 75 +++++++++++----- src/components/ResponseRepr/ResponseRepr.tsx | 93 ++++++++++++-------- 3 files changed, 116 insertions(+), 60 deletions(-) diff --git a/src/components/KeyList/KeyList.module.d.ts b/src/components/KeyList/KeyList.module.d.ts index 8b626ab..f3660ce 100644 --- a/src/components/KeyList/KeyList.module.d.ts +++ b/src/components/KeyList/KeyList.module.d.ts @@ -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[] +} diff --git a/src/components/KeyList/KeyList.tsx b/src/components/KeyList/KeyList.tsx index afd2410..73d71b8 100644 --- a/src/components/KeyList/KeyList.tsx +++ b/src/components/KeyList/KeyList.tsx @@ -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 { private listeners: string[] @@ -16,6 +17,15 @@ class KeyList extends React.Component { 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 { 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 ( -
this.selectItem(key)}> - {key === '' ? '[Response]' : key} +
+
this.selectItem(key)}> + {key.label} +
+ {key.children && key.children.length ? ( + this.keyListElements(key.children, depth + 1) + ) : ''}
) }) } render() { - const classNames = [ - css.KeyList, - this.props.className || '' - ].join(' ') + const className = classNames(css.KeyList, this.props.className) + return ( -
- {this.keyListElements} +
+ {this.keyListElements(this.state.keyList)}
) } diff --git a/src/components/ResponseRepr/ResponseRepr.tsx b/src/components/ResponseRepr/ResponseRepr.tsx index 2e2184e..5ce3135 100644 --- a/src/components/ResponseRepr/ResponseRepr.tsx +++ b/src/components/ResponseRepr/ResponseRepr.tsx @@ -24,16 +24,16 @@ class ResponseRepr extends React.Component { 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 { 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 { // 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 { return response } + private filterInput() { + return ( + =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 ( +
+ {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 ( + + ) + })} +
+ ) + } + private getRObjectList() { const { response } = this.state let colAmt if (response && response.constructor === Array) { - const keys = Object.keys(response[0] || {}) - colAmt = keys.length - return ( -
- {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 ( - - ) - })} -
- ) + return this.table() } colAmt = Object.keys(response || {}) return (
+ style={{gridTemplateRows: `repeat(${colAmt}, min-content)`}}>
) @@ -181,16 +206,10 @@ class ResponseRepr extends React.Component { css.ResponseRepr, this.props.className ].join(' ') - - const repr = this.getRObjectList() return (
- =32)`} - onChange={(e) => this.setState({ filter: e.target.value })} /> - {repr} + {this.getRObjectList()}
) }