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

View File

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

View File

@ -5,6 +5,27 @@ class SchemaExplorer extends PureComponent {
public render() {
return (
<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 />
</div>
)

View File

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

View File

@ -9,7 +9,6 @@ interface Props {
interface State {
isOpen: boolean
filterText: string
}
@ErrorHandling
@ -17,102 +16,52 @@ class TagListItem extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
filterText: '',
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()
this.setState({isOpen: !this.state.isOpen})
}
public handleFilterText(e) {
e.stopPropagation()
this.setState({
filterText: e.target.value,
})
private get tagItemLabel(): string {
const {tagKey, tagValues} = this.props
return `${tagKey}${tagValues.length}`
}
public handleEscape(e) {
if (e.key !== 'Escape') {
return
}
e.stopPropagation()
this.setState({
filterText: '',
})
}
public handleInputClick(e: MouseEvent<HTMLInputElement>) {
e.stopPropagation()
}
public renderTagValues() {
private get renderTagValues(): JSX.Element[] | JSX.Element {
const {tagValues} = this.props
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()
const filtered = tagValues.filter(v => v.toLowerCase().includes(filterText))
return (
<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" />
return tagValues.map(v => {
return (
<div key={v} className="ifql-schema-item readonly">
{v}
</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() {
const {tagKey, tagValues} = this.props
private get className(): string {
const {isOpen} = this.state
const tagItemLabel = `${tagKey}${tagValues.length}`
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>
)
return classnames('ifql-schema-tree', {expanded: isOpen})
}
}

View File

@ -3,47 +3,92 @@
----------------------------------------------------------------------------
*/
$ifql-tree-indent: 30px;
$ifql-tree-indent: 28px;
.ifql-schema-explorer {
width: 100%;
height: 100%;
background-color: $g2-kevlar;
}
.ifql-schema-tree {
display: flex;
flex-direction: column;
align-items: stretch;
padding-left: 0;
& > .ifql-schema-tree {
> .ifql-schema-tree {
padding-left: $ifql-tree-indent;
}
.ifql-schema-item + & {
display: none;
}
.expanded .ifql-schema-item + & {
display: flex;
}
}
.ifql-schema-item {
position: relative;
height: 30px;
.ifql-schema-tree__empty {
height: $ifql-tree-indent;
display: flex;
align-items: center;
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;
top: 50%;
left: 14px;
left: $ifql-tree-indent / 2;
transform: translate(-50%, -50%);
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-builder';
// @import '../components/time-machine/ifql-explorer';
@import '../components/time-machine/ifql-explorer';
@import '../components/time-machine/visualization';
@import '../components/time-machine/add-func-button';