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