Commit ae5bc366 by Johannes Schill

Start adding keyboard navigation to VizPicker

parent 74b3a509
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import classNames from 'classnames';
import _ from 'lodash'; import _ from 'lodash';
import config from 'app/core/config'; import config from 'app/core/config';
import { PanelPlugin } from 'app/types/plugins'; import { PanelPlugin } from 'app/types/plugins';
import VizTypePickerPlugin from './VizTypePickerPlugin';
interface Props { interface Props {
current: PanelPlugin; current: PanelPlugin;
...@@ -12,6 +12,7 @@ interface Props { ...@@ -12,6 +12,7 @@ interface Props {
interface State { interface State {
searchQuery: string; searchQuery: string;
selected: number;
} }
export class VizTypePicker extends PureComponent<Props, State> { export class VizTypePicker extends PureComponent<Props, State> {
...@@ -23,9 +24,56 @@ export class VizTypePicker extends PureComponent<Props, State> { ...@@ -23,9 +24,56 @@ export class VizTypePicker extends PureComponent<Props, State> {
this.state = { this.state = {
searchQuery: '', searchQuery: '',
selected: 0,
}; };
} }
get filteredPluginListCount() {
const filteredPluginList = this.getFilteredPluginList();
return filteredPluginList.length;
}
goRight = () => {
const maxArray = this.filteredPluginListCount - 1;
const nextIndex = this.state.selected >= maxArray ? 0 : this.state.selected + 1;
this.setState({
selected: nextIndex,
});
};
goLeft = () => {
const maxArray = this.filteredPluginListCount - 1;
const nextIndex = this.state.selected <= 0 ? maxArray : this.state.selected - 1;
this.setState({
selected: nextIndex,
});
};
onKeydown = (evt: KeyboardEvent) => {
if (evt.key === 'ArrowRight' || evt.key === 'ArrowDown') {
this.goRight();
}
if (evt.key === 'ArrowLeft' || evt.key === 'ArrowUp') {
this.goLeft();
}
if (evt.key === 'Enter') {
const filteredPluginList = this.getFilteredPluginList();
this.props.onTypeChanged(filteredPluginList[this.state.selected]);
}
};
componentDidMount() {
setTimeout(() => {
this.searchInput.focus();
}, 300);
document.addEventListener('keydown', this.onKeydown);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.onKeydown);
}
getPanelPlugins(filter): PanelPlugin[] { getPanelPlugins(filter): PanelPlugin[] {
const panels = _.chain(config.panels) const panels = _.chain(config.panels)
.filter({ hideFromList: false }) .filter({ hideFromList: false })
...@@ -37,25 +85,19 @@ export class VizTypePicker extends PureComponent<Props, State> { ...@@ -37,25 +85,19 @@ export class VizTypePicker extends PureComponent<Props, State> {
} }
renderVizPlugin = (plugin: PanelPlugin, index: number) => { renderVizPlugin = (plugin: PanelPlugin, index: number) => {
const cssClass = classNames({ const isSelected = this.state.selected === index;
'viz-picker__item': true, const isCurrent = plugin.id === this.props.current.id;
'viz-picker__item--selected': plugin.id === this.props.current.id,
});
return ( return (
<div key={index} className={cssClass} onClick={() => this.props.onTypeChanged(plugin)} title={plugin.name}> <VizTypePickerPlugin
<div className="viz-picker__item-name">{plugin.name}</div> key={plugin.id}
<img className="viz-picker__item-img" src={plugin.info.logos.small} /> isSelected={isSelected}
</div> isCurrent={isCurrent}
plugin={plugin}
onClick={() => this.props.onTypeChanged(plugin)}
/>
); );
}; };
componentDidMount() {
setTimeout(() => {
this.searchInput.focus();
}, 300);
}
getFilteredPluginList = (): PanelPlugin[] => { getFilteredPluginList = (): PanelPlugin[] => {
const { searchQuery } = this.state; const { searchQuery } = this.state;
const regex = new RegExp(searchQuery, 'i'); const regex = new RegExp(searchQuery, 'i');
...@@ -73,6 +115,7 @@ export class VizTypePicker extends PureComponent<Props, State> { ...@@ -73,6 +115,7 @@ export class VizTypePicker extends PureComponent<Props, State> {
this.setState(prevState => ({ this.setState(prevState => ({
...prevState, ...prevState,
searchQuery: value, searchQuery: value,
selected: 0,
})); }));
}; };
...@@ -102,7 +145,6 @@ export class VizTypePicker extends PureComponent<Props, State> { ...@@ -102,7 +145,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
{this.renderFilters()} {this.renderFilters()}
<div className="gf-form--grow" /> <div className="gf-form--grow" />
</div> </div>
<div className="viz-picker">{filteredPluginList.map(this.renderVizPlugin)}</div> <div className="viz-picker">{filteredPluginList.map(this.renderVizPlugin)}</div>
</> </>
); );
......
import React from 'react';
import classNames from 'classnames';
interface Props {
isSelected: boolean;
isCurrent: boolean;
plugin: any;
onClick: () => void;
}
const VizTypePickerPlugin = React.memo(
({ isSelected, isCurrent, plugin, onClick }: Props) => {
const cssClass = classNames({
'viz-picker__item': true,
'viz-picker__item--selected': isSelected,
'viz-picker__item--current': isCurrent,
});
return (
<div className={cssClass} onClick={onClick} title={plugin.name}>
<div className="viz-picker__item-name">{plugin.name}</div>
<img className="viz-picker__item-img" src={plugin.info.logos.small} />
</div>
);
},
(prevProps, nextProps) => {
if (prevProps.isSelected === nextProps.isSelected && prevProps.isCurrent === nextProps.isCurrent) {
return true;
}
return false;
}
);
export default VizTypePickerPlugin;
...@@ -163,7 +163,7 @@ ...@@ -163,7 +163,7 @@
border: $panel-editor-viz-item-border-hover; border: $panel-editor-viz-item-border-hover;
} }
&--selected { &--current {
box-shadow: 0 0 6px $orange; box-shadow: 0 0 6px $orange;
border: 1px solid $orange; border: 1px solid $orange;
...@@ -173,6 +173,17 @@ ...@@ -173,6 +173,17 @@
background: $panel-editor-viz-item-bg-hover-active; background: $panel-editor-viz-item-bg-hover-active;
} }
} }
&--selected {
box-shadow: 0 0 6px $purple;
border: 1px solid $purple;
&:hover {
box-shadow: 0 0 6px $purple;
border: 1px solid $purple;
background: $panel-editor-viz-item-bg-hover-active;
}
}
} }
.viz-picker__item-name { .viz-picker__item-name {
......
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