Implement styles for IFQL schema explorer

pull/10616/head
Alex P 2018-05-04 13:48:49 -07:00
parent 716f7e8b4d
commit a70d5811c9
7 changed files with 122 additions and 129 deletions

View File

@ -1,6 +1,5 @@
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import _ from 'lodash'
import DatabaseListItem from 'src/ifql/components/DatabaseListItem' import DatabaseListItem from 'src/ifql/components/DatabaseListItem'
@ -47,31 +46,15 @@ class DatabaseList extends PureComponent<{}, DatabaseListState> {
const sorted = databases.sort() const sorted = databases.sort()
this.setState({databases: sorted}) this.setState({databases: sorted})
const db = _.get(sorted, '0', '')
this.handleChooseDatabase(db)
} catch (err) { } catch (err) {
console.error(err) console.error(err)
} }
} }
public render() { public render() {
return ( return this.state.databases.map(db => {
<div className="ifql-schema-tree"> return <DatabaseListItem db={db} key={db} />
{this.state.databases.map(db => { })
return (
<DatabaseListItem
db={db}
key={db}
onChooseDatabase={this.handleChooseDatabase}
/>
)
})}
</div>
)
}
private handleChooseDatabase = (db: string): void => {
this.setState({db})
} }
} }

View File

@ -6,7 +6,6 @@ import TagList from 'src/ifql/components/TagList'
interface Props { interface Props {
db: string db: string
onChooseDatabase: (db: string) => void
} }
interface State { interface State {
@ -27,7 +26,7 @@ class DatabaseListItem extends PureComponent<Props, State> {
return ( return (
<div className={this.className} onClick={this.handleChooseDatabase}> <div className={this.className} onClick={this.handleChooseDatabase}>
<div className="ifql-schema-item"> <div className="ifql-schema-item">
<div className="icon caret-right" /> <span className="icon caret-right" />
{db} {db}
</div> </div>
{this.state.isOpen && <TagList db={db} />} {this.state.isOpen && <TagList db={db} />}

View File

@ -5,6 +5,27 @@ class SchemaExplorer extends PureComponent {
public render() { public render() {
return ( return (
<div className="ifql-schema-explorer"> <div className="ifql-schema-explorer">
<div className="ifql-schema--controls">
<div className="ifql-schema--filter">
<input
className="form-control input-sm"
placeholder="Filter YO schema dawg..."
type="text"
// onChange={this.handleFilterText}
// onKeyUp={this.handleEscape}
// onClick={this.handleInputClick}
spellCheck={false}
autoComplete="off"
/>
</div>
<button
className="btn btn-sm btn-default btn-square"
disabled={true}
title="Collapse YO tree"
>
<span className="icon zap" />
</button>
</div>
<DatabaseList /> <DatabaseList />
</div> </div>
) )

View File

@ -48,21 +48,17 @@ class TagList extends PureComponent<Props, State> {
const keys = await getTags() const keys = await getTags()
const values = await getTagValues() const values = await getTagValues()
const tags = keys.map(k => { const tags = keys.reduce((acc, k) => {
return (this.state.tags[k] = values) return {...acc, [k]: values}
}) }, {})
this.setState({tags}) this.setState({tags})
} }
public render() { public render() {
return ( return _.map(this.state.tags, (tagValues: string[], tagKey: string) => (
<div className="query-builder--sub-list"> <TagListItem key={tagKey} tagKey={tagKey} tagValues={tagValues} />
{_.map(this.state.tags, (tagValues: string[], tagKey: string) => ( ))
<TagListItem key={tagKey} tagKey={tagKey} tagValues={tagValues} />
))}
</div>
)
} }
} }

View File

@ -9,7 +9,6 @@ interface Props {
interface State { interface State {
isOpen: boolean isOpen: boolean
filterText: string
} }
@ErrorHandling @ErrorHandling
@ -17,102 +16,52 @@ class TagListItem extends PureComponent<Props, State> {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
filterText: '',
isOpen: false, isOpen: false,
} }
this.handleEscape = this.handleEscape.bind(this)
this.handleClickKey = this.handleClickKey.bind(this)
this.handleFilterText = this.handleFilterText.bind(this)
} }
public handleClickKey(e: MouseEvent<HTMLElement>) { public render() {
const {isOpen} = this.state
return (
<div className={this.className}>
<div className="ifql-schema-item" onClick={this.handleClick}>
<span className="icon caret-right" />
{this.tagItemLabel}
</div>
{isOpen && this.renderTagValues}
</div>
)
}
private handleClick = (e: MouseEvent<HTMLElement>): void => {
e.stopPropagation() e.stopPropagation()
this.setState({isOpen: !this.state.isOpen}) this.setState({isOpen: !this.state.isOpen})
} }
public handleFilterText(e) { private get tagItemLabel(): string {
e.stopPropagation() const {tagKey, tagValues} = this.props
this.setState({ return `${tagKey}${tagValues.length}`
filterText: e.target.value,
})
} }
public handleEscape(e) { private get renderTagValues(): JSX.Element[] | JSX.Element {
if (e.key !== 'Escape') {
return
}
e.stopPropagation()
this.setState({
filterText: '',
})
}
public handleInputClick(e: MouseEvent<HTMLInputElement>) {
e.stopPropagation()
}
public renderTagValues() {
const {tagValues} = this.props const {tagValues} = this.props
if (!tagValues || !tagValues.length) { if (!tagValues || !tagValues.length) {
return <div>no tag values</div> return <div className="ifql-schema-tree__empty">No tag values</div>
} }
const filterText = this.state.filterText.toLowerCase() return tagValues.map(v => {
const filtered = tagValues.filter(v => v.toLowerCase().includes(filterText)) return (
<div key={v} className="ifql-schema-item readonly">
return ( {v}
<div className="query-builder--sub-list">
<div className="query-builder--filter">
<input
className="form-control input-sm"
placeholder={`Filter within ${this.props.tagKey}`}
type="text"
value={this.state.filterText}
onChange={this.handleFilterText}
onKeyUp={this.handleEscape}
onClick={this.handleInputClick}
spellCheck={false}
autoComplete="false"
/>
<span className="icon search" />
</div> </div>
{filtered.map(v => { )
return ( })
<div
key={v}
className={'query-builder--list-item'}
data-test={`query-builder-list-item-tag-value-${v}`}
>
{v}
</div>
)
})}
</div>
)
} }
public render() { private get className(): string {
const {tagKey, tagValues} = this.props
const {isOpen} = this.state const {isOpen} = this.state
const tagItemLabel = `${tagKey}${tagValues.length}` return classnames('ifql-schema-tree', {expanded: isOpen})
return (
<div>
<div
className={classnames('query-builder--list-item', {active: isOpen})}
onClick={this.handleClickKey}
data-test={`query-builder-list-item-tag-${tagKey}`}
>
<span>
<div className="query-builder--caret icon caret-right" />
{tagItemLabel}
</span>
</div>
{isOpen ? this.renderTagValues() : null}
</div>
)
} }
} }

View File

@ -3,47 +3,92 @@
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
*/ */
$ifql-tree-indent: 30px; $ifql-tree-indent: 28px;
.ifql-schema-explorer { .ifql-schema-explorer {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: $g2-kevlar;
} }
.ifql-schema-tree { .ifql-schema-tree {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
padding-left: 0;
& > .ifql-schema-tree { > .ifql-schema-tree {
padding-left: $ifql-tree-indent; padding-left: $ifql-tree-indent;
} }
.ifql-schema-item + & {
display: none;
}
.expanded .ifql-schema-item + & {
display: flex;
}
} }
.ifql-schema-item { .ifql-schema-tree__empty {
position: relative; height: $ifql-tree-indent;
height: 30px;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 11px; padding: 0 11px;
padding-left: 32px; font-size: 12px;
font-weight: 600;
color: $g8-storm;
font-style: italic;
}
> span { .ifql-schema-item {
@include no-user-select();
position: relative;
height: $ifql-tree-indent;
display: flex;
align-items: center;
padding: 0 11px;
padding-left: $ifql-tree-indent;
font-size: 12px;
font-weight: 600;
color: $g11-sidewalk;
transition: color 0.25s ease, background-color 0.25s ease;
> span.icon {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 14px; left: $ifql-tree-indent / 2;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
transition: transform 0.25s ease; transition: transform 0.25s ease;
} }
&:hover {
color: $g15-platinum;
cursor: pointer;
background-color: $g4-onyx;
}
.expanded > & {
color: $c-pool;
background-color: $g3-castle;
> span.icon {
transform: translate(-50%, -50%) rotate(90deg);
}
}
&.readonly,
&.readonly:hover {
background-color: transparent;
color: $g11-sidewalk;
cursor: default;
}
} }
/*
Controls
----------------------------------------------------------------------------
*/
.ifql-schema--controls {
padding: 11px;
display: flex;
align-items: center;
justify-content: space-between;
}
.ifql-schema--filter {
flex: 1 0 0;
margin-right: 4px;
}

View File

@ -5,6 +5,6 @@
@import '../components/time-machine/ifql-editor'; @import '../components/time-machine/ifql-editor';
@import '../components/time-machine/ifql-builder'; @import '../components/time-machine/ifql-builder';
// @import '../components/time-machine/ifql-explorer'; @import '../components/time-machine/ifql-explorer';
@import '../components/time-machine/visualization'; @import '../components/time-machine/visualization';
@import '../components/time-machine/add-func-button'; @import '../components/time-machine/add-func-button';