Implement styles for IFQL schema explorer
parent
716f7e8b4d
commit
a70d5811c9
|
@ -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} />
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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} />}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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} />
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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';
|
Loading…
Reference in New Issue