Commit 5975c4a7 by Torkel Ödegaard

ux: changed panel selection ux

parent b372b432
import React, { SFC } from 'react';
import Transition from 'react-transition-group/Transition';
interface Props {
duration: number;
children: JSX.Element;
in: boolean;
}
export const FadeIn: SFC<Props> = props => {
const defaultStyle = {
transition: `opacity ${props.duration}ms linear`,
opacity: 0,
};
const transitionStyles = {
exited: { opacity: 0, display: 'none' },
entering: { opacity: 0 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
};
return (
<Transition in={props.in} timeout={props.duration}>
{state => (
<div
style={{
...defaultStyle,
...transitionStyles[state],
}}
>
{props.children}
</div>
)}
</Transition>
);
};
......@@ -23,7 +23,7 @@ export default ({ children, in: inProp, maxHeight = defaultMaxHeight, style = de
const transitionStyles = {
exited: { maxHeight: 0 },
entering: { maxHeight: maxHeight },
entered: { maxHeight: maxHeight, overflow: 'visible' },
entered: { maxHeight: 'unset', overflow: 'visible' },
exiting: { maxHeight: 0 },
};
......
import React from 'react';
import config from 'app/core/config';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import { DashboardRow } from './DashboardRow';
import { AddPanelPanel } from './AddPanelPanel';
import { importPluginModule } from 'app/features/plugins/plugin_loader';
import { PluginExports, PanelPlugin } from 'app/types/plugins';
import { AddPanelPanel } from './AddPanelPanel';
import { DashboardRow } from './DashboardRow';
import { PanelPlugin } from 'app/types/plugins';
import { PanelChrome } from './PanelChrome';
import { PanelEditor } from './PanelEditor';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
export interface Props {
panelType: string;
panel: PanelModel;
......@@ -17,20 +20,19 @@ export interface Props {
}
export interface State {
pluginExports: PluginExports;
plugin: PanelPlugin;
}
export class DashboardPanel extends React.Component<Props, State> {
element: any;
angularPanel: AngularComponent;
pluginInfo: any;
specialPanels = {};
constructor(props) {
super(props);
this.state = {
pluginExports: null,
plugin: null,
};
this.specialPanels['row'] = this.renderRow.bind(this);
......@@ -63,20 +65,22 @@ export class DashboardPanel extends React.Component<Props, State> {
return;
}
const { panel } = this.props;
// handle plugin loading & changing of plugin type
if (!this.pluginInfo || this.pluginInfo.id !== this.props.panel.type) {
this.pluginInfo = config.panels[this.props.panel.type];
if (!this.state.plugin || this.state.plugin.id !== panel.type) {
const plugin = config.panels[panel.type];
if (this.pluginInfo.exports) {
if (plugin.exports) {
this.cleanUpAngularPanel();
this.setState({ pluginExports: this.pluginInfo.exports });
this.setState({ plugin: plugin });
} else {
importPluginModule(this.pluginInfo.module).then(pluginExports => {
importPluginModule(plugin.module).then(pluginExports => {
this.cleanUpAngularPanel();
// cache plugin exports (saves a promise async cycle next time)
this.pluginInfo.exports = pluginExports;
plugin.exports = pluginExports;
// update panel state
this.setState({ pluginExports: pluginExports });
this.setState({ plugin: plugin });
});
}
}
......@@ -112,44 +116,41 @@ export class DashboardPanel extends React.Component<Props, State> {
}
renderReactPanel() {
const { pluginExports } = this.state;
const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
const { dashboard, panel } = this.props;
const { plugin } = this.state;
const containerClass = panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
const panelWrapperClass = panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
// this might look strange with these classes that change when edit, but
// I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
return (
<div className={containerClass}>
<div className={panelWrapperClass}>
<PanelChrome
component={pluginExports.PanelComponent}
panel={this.props.panel}
dashboard={this.props.dashboard}
/>
<PanelChrome component={plugin.exports.PanelComponent} panel={panel} dashboard={dashboard} />
</div>
{this.props.panel.isEditing && (
<PanelEditor
panel={this.props.panel}
panelType={this.props.panel.type}
dashboard={this.props.dashboard}
onTypeChanged={this.onPluginTypeChanged}
pluginExports={pluginExports}
/>
{panel.isEditing && (
<PanelEditor panel={panel} plugin={plugin} dashboard={dashboard} onTypeChanged={this.onPluginTypeChanged} />
)}
</div>
);
}
render() {
const { panel } = this.props;
const { plugin } = this.state;
if (this.isSpecial()) {
return this.specialPanels[this.props.panel.type]();
return this.specialPanels[panel.type]();
}
if (!this.state.pluginExports) {
// if we have not loaded plugin exports yet, wait
if (!plugin || !plugin.exports) {
return null;
}
if (this.state.pluginExports.PanelComponent) {
// if exporting PanelComponent it must be a react panel
if (plugin.exports.PanelComponent) {
return this.renderReactPanel();
}
......
......@@ -10,13 +10,12 @@ import { updateLocation } from 'app/core/actions';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { PanelPlugin, PluginExports } from 'app/types/plugins';
import { PanelPlugin } from 'app/types/plugins';
interface PanelEditorProps {
panel: PanelModel;
dashboard: DashboardModel;
panelType: string;
pluginExports: PluginExports;
plugin: PanelPlugin;
onTypeChanged: (newType: PanelPlugin) => void;
}
......@@ -44,11 +43,11 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
}
renderPanelOptions() {
const { pluginExports, panel } = this.props;
const { plugin, panel } = this.props;
const { PanelOptionsComponent } = plugin.exports;
if (pluginExports.PanelOptionsComponent) {
const OptionsComponent = pluginExports.PanelOptionsComponent;
return <OptionsComponent options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
if (PanelOptionsComponent) {
return <PanelOptionsComponent options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
} else {
return <p>Visualization has no options</p>;
}
......@@ -62,7 +61,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
renderVizTab() {
return (
<div className="viz-editor">
<VizTypePicker currentType={this.props.panel.type} onTypeChanged={this.props.onTypeChanged} />
<VizTypePicker current={this.props.plugin} onTypeChanged={this.props.onTypeChanged} />
{this.renderPanelOptions()}
</div>
);
......
import React, { PureComponent } from 'react';
import classNames from 'classnames';
import _ from 'lodash';
import { FadeIn } from 'app/core/components/Animations/FadeIn';
import config from 'app/core/config';
import { PanelPlugin } from 'app/types/plugins';
import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar';
import _ from 'lodash';
interface Props {
currentType: string;
current: PanelPlugin;
onTypeChanged: (newType: PanelPlugin) => void;
}
interface State {
pluginList: PanelPlugin[];
isOpen: boolean;
}
export class VizTypePicker extends PureComponent<Props, State> {
......@@ -21,6 +22,7 @@ export class VizTypePicker extends PureComponent<Props, State> {
this.state = {
pluginList: this.getPanelPlugins(''),
isOpen: false,
};
}
......@@ -37,7 +39,7 @@ export class VizTypePicker extends PureComponent<Props, State> {
renderVizPlugin = (plugin, index) => {
const cssClass = classNames({
'viz-picker__item': true,
'viz-picker__item--selected': plugin.id === this.props.currentType,
'viz-picker__item--selected': plugin.id === this.props.current.id,
});
return (
......@@ -55,7 +57,7 @@ export class VizTypePicker extends PureComponent<Props, State> {
<input type="text" className="gf-form-input width-13" placeholder="" />
<i className="gf-form-input-icon fa fa-search" />
</label>
<div>
<div className="p-l-1">
<button className="btn toggle-btn gf-form-btn active">Basic Types</button>
<button className="btn toggle-btn gf-form-btn">Master Types</button>
</div>
......@@ -63,24 +65,52 @@ export class VizTypePicker extends PureComponent<Props, State> {
);
}
onToggleOpen = () => {
this.setState({ isOpen: !this.state.isOpen });
};
render() {
const { currentType } = this.props;
const { pluginList } = this.state;
const { current } = this.props;
const { pluginList, isOpen } = this.state;
return (
<div className="viz-picker">
<div className="viz-picker__bar">
<label className="gf-form-label">Visualization</label>
<label className="gf-form-input width-10">
<span>{currentType}</span>
</label>
<div className="gf-form--grow" />
{this.renderFilters()}
<div className="gf-form-inline">
<div className="gf-form">
<label className="gf-form-label">Visualization</label>
<label className="gf-form-input width-10" onClick={this.onToggleOpen}>
<span>{current.name}</span>
{isOpen && <i className="fa fa-caret-down pull-right" />}
{!isOpen && <i className="fa fa-caret-left pull-right" />}
</label>
</div>
<div className="gf-form gf-form--grow">
<label className="gf-form-label gf-form-label--grow" />
</div>
<div className="gf-form">
<label className="gf-form-label">
<i className="fa fa-caret-down" /> Help
</label>
</div>
</div>
</div>
<CustomScrollbar>
<div className="viz-picker__items">{pluginList.map(this.renderVizPlugin)}</div>
</CustomScrollbar>
<FadeIn in={isOpen} duration={300}>
<div className="cta-form">
<button className="cta-form__close" onClick={this.onToggleOpen}>
<i className="fa fa-remove" />
</button>
<div className="cta-form__bar">
<div className="cta-form__bar-header">Select visualization</div>
{this.renderFilters()}
<div className="gf-form--grow" />
</div>
<div className="viz-picker__items">{pluginList.map(this.renderVizPlugin)}</div>
</div>
</FadeIn>
</div>
);
}
......
......@@ -410,7 +410,27 @@ select.gf-form-input ~ .gf-form-help-icon {
}
.cta-form__close {
background: transparent;
padding: 4px 8px 4px 9px;
border: none;
position: absolute;
right: 0;
top: 0;
top: -2px;
font-size: $font-size-lg;
&:hover {
color: $text-color-strong;
}
}
.cta-form__bar {
display: flex;
align-items: center;
align-content: center;
margin-bottom: 20px;
}
.cta-form__bar-header {
font-size: $font-size-h4;
padding-right: 20px;
}
......@@ -59,6 +59,7 @@
.viz-picker__items {
display: flex;
flex-wrap: wrap;
// for scrollbar
margin-bottom: 13px;
}
......@@ -68,14 +69,15 @@
box-shadow: $card-shadow;
border-radius: 3px;
height: 70px;
width: 130px;
height: 90px;
width: 150px;
flex-shrink: 0;
flex-direction: column;
text-align: center;
cursor: pointer;
display: flex;
margin-right: 6px;
margin-right: 10px;
margin-bottom: 10px;
border: 1px solid transparent;
align-items: center;
......@@ -98,11 +100,11 @@
display: flex;
flex-direction: column;
align-self: center;
height: 20px;
height: 23px;
}
.viz-picker__item-img {
height: 40px;
height: 55px;
}
.panel-editor__aside {
......@@ -162,7 +164,6 @@
}
.viz-picker__bar {
display: flex;
margin-bottom: $spacer;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment