mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
wip: react select css refactoring
This commit is contained in:
parent
d998527128
commit
1d7d72b49d
@ -8,15 +8,16 @@ interface ExtendedOptionProps extends OptionProps<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Option = (props: ExtendedOptionProps) => {
|
export const Option = (props: ExtendedOptionProps) => {
|
||||||
const { children, isSelected, data, className } = props;
|
const { children, isSelected, data } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.Option {...props}>
|
<components.Option {...props}>
|
||||||
<div className={`description-picker-option__button btn btn-link ${className}`}>
|
<div className="gf-form-select-box__desc-option">
|
||||||
{isSelected && <i className="fa fa-check pull-right" aria-hidden="true" />}
|
<div className="gf-form-select-box__desc-option__body">
|
||||||
<div className="gf-form">{children}</div>
|
<div>{children}</div>
|
||||||
<div className="gf-form">
|
{data.description && <div className="gf-form-select-box__desc-option__desc">{data.description}</div>}
|
||||||
<div className="muted width-17">{data.description}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{isSelected && <i className="fa fa-check" aria-hidden="true" />}
|
||||||
</div>
|
</div>
|
||||||
</components.Option>
|
</components.Option>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
// Libraries
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import KeyboardNavigation, { KeyboardNavigationProps } from './KeyboardNavigation';
|
|
||||||
|
// Components
|
||||||
|
import ResetStyles from 'app/core/components/Picker/ResetStyles';
|
||||||
|
import PickerOption from 'app/core/components/Picker/PickerOption';
|
||||||
|
import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
|
||||||
|
import Select from 'react-select';
|
||||||
|
|
||||||
|
// Types
|
||||||
import { DataSourceSelectItem } from 'app/types';
|
import { DataSourceSelectItem } from 'app/types';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -10,127 +17,50 @@ export interface Props {
|
|||||||
current: DataSourceSelectItem;
|
current: DataSourceSelectItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
export class DataSourcePicker extends PureComponent<Props> {
|
||||||
searchQuery: string;
|
|
||||||
isOpen: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DataSourcePicker extends PureComponent<Props, State> {
|
|
||||||
searchInput: HTMLElement;
|
searchInput: HTMLElement;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
|
||||||
searchQuery: '',
|
|
||||||
isOpen: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataSources() {
|
onChange = item => {
|
||||||
const { searchQuery } = this.state;
|
const ds = this.props.datasources.find(ds => ds.name === item.value);
|
||||||
const regex = new RegExp(searchQuery, 'i');
|
this.props.onChangeDataSource(ds);
|
||||||
const { datasources } = this.props;
|
|
||||||
|
|
||||||
const filtered = datasources.filter(item => {
|
|
||||||
return regex.test(item.name) || regex.test(item.meta.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
get maxSelectedIndex() {
|
|
||||||
const filtered = this.getDataSources();
|
|
||||||
return filtered.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDataSource = (ds: DataSourceSelectItem, index: number, keyNavProps: KeyboardNavigationProps) => {
|
|
||||||
const { onChangeDataSource } = this.props;
|
|
||||||
const { selected, onMouseEnter } = keyNavProps;
|
|
||||||
const onClick = () => onChangeDataSource(ds);
|
|
||||||
const isSelected = selected === index;
|
|
||||||
const cssClass = classNames({
|
|
||||||
'ds-picker-list__item': true,
|
|
||||||
'ds-picker-list__item--selected': isSelected,
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div key={index} className={cssClass} title={ds.name} onClick={onClick} onMouseEnter={() => onMouseEnter(index)}>
|
|
||||||
<img className="ds-picker-list__img" src={ds.meta.info.logos.small} />
|
|
||||||
<div className="ds-picker-list__name">{ds.name}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.searchInput.focus();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearchQueryChange = evt => {
|
|
||||||
const value = evt.target.value;
|
|
||||||
this.setState(prevState => ({
|
|
||||||
...prevState,
|
|
||||||
searchQuery: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
renderFilters({ onKeyDown, selected }: KeyboardNavigationProps) {
|
|
||||||
const { searchQuery } = this.state;
|
|
||||||
return (
|
|
||||||
<label className="gf-form--has-input-icon">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="gf-form-input width-13"
|
|
||||||
placeholder=""
|
|
||||||
ref={elem => (this.searchInput = elem)}
|
|
||||||
onChange={this.onSearchQueryChange}
|
|
||||||
value={searchQuery}
|
|
||||||
onKeyDown={evt => {
|
|
||||||
onKeyDown(evt, this.maxSelectedIndex, () => {
|
|
||||||
const { onChangeDataSource } = this.props;
|
|
||||||
const ds = this.getDataSources()[selected];
|
|
||||||
onChangeDataSource(ds);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<i className="gf-form-input-icon fa fa-search" />
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpen = () => {
|
|
||||||
this.setState({ isOpen: true });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { current } = this.props;
|
const { datasources, current, onChangeDatasource } = this.props;
|
||||||
const { isOpen } = this.state;
|
|
||||||
|
const options = datasources.map(ds => ({
|
||||||
|
value: ds.name,
|
||||||
|
label: ds.name,
|
||||||
|
avatarUrl: ds.meta.info.logos.small,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const value = { label: current.name, label: current.name };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ds-picker">
|
<div className="gf-form-inline">
|
||||||
{!isOpen && (
|
<Select
|
||||||
<div className="toolbar__main" onClick={this.onOpen}>
|
classNamePrefix={`gf-form-select-box`}
|
||||||
<img className="toolbar__main-image" src={current.meta.info.logos.small} />
|
isMulti={false}
|
||||||
<div className="toolbar__main-name">{current.name}</div>
|
menuShouldScrollIntoView={false}
|
||||||
<i className="fa fa-caret-down" />
|
isClearable={false}
|
||||||
</div>
|
className="gf-form-input gf-form-input--form-dropdown ds-picker"
|
||||||
)}
|
onChange={item => this.onChange(item)}
|
||||||
{isOpen && (
|
options={options}
|
||||||
<KeyboardNavigation
|
styles={ResetStyles}
|
||||||
render={(keyNavProps: KeyboardNavigationProps) => (
|
maxMenuHeight={500}
|
||||||
<div className="ds-picker-menu">
|
placeholder="Select datasource"
|
||||||
<div className="cta-form__bar">
|
loadingMessage={() => 'Loading datasources...'}
|
||||||
{this.renderFilters(keyNavProps)}
|
noOptionsMessage={() => 'No datasources found'}
|
||||||
<div className="gf-form--grow" />
|
value={value}
|
||||||
</div>
|
components={{
|
||||||
<div className="ds-picker-list">
|
Option: PickerOption,
|
||||||
{this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
|
IndicatorsContainer,
|
||||||
</div>
|
}}
|
||||||
</div>
|
/>
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,27 @@
|
|||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gf-form-select-box__desc-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
justify-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 7px 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-form-select-box__desc-option__body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding-right: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gf-form-select-box__desc-option__desc {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: $font-size-sm;
|
||||||
|
color: $text-muted;
|
||||||
|
}
|
||||||
|
@ -50,9 +50,10 @@ $select-input-bg-disabled: $input-bg-disabled;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gf-form-select-box__menu {
|
.gf-form-select-box__menu {
|
||||||
background: $dropdownBackground;
|
background: $input-bg;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gf-form-select-box__menu-list {
|
.gf-form-select-box__menu-list {
|
||||||
@ -64,12 +65,16 @@ $select-input-bg-disabled: $input-bg-disabled;
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .gf-form-select-box__single-value { */
|
||||||
|
/* } */
|
||||||
|
|
||||||
.gf-form-select-box__multi-value {
|
.gf-form-select-box__multi-value {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gf-form-select-box__option {
|
.gf-form-select-box__option {
|
||||||
border-left: 2px solid transparent;
|
border-left: 2px solid transparent;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
&.gf-form-select-box__option--is-focused {
|
&.gf-form-select-box__option--is-focused {
|
||||||
color: $dropdownLinkColorHover;
|
color: $dropdownLinkColorHover;
|
||||||
@ -88,6 +93,9 @@ $select-input-bg-disabled: $input-bg-disabled;
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gf-form-select-box__option {
|
||||||
|
}
|
||||||
|
|
||||||
.gf-form-select-box__value-container {
|
.gf-form-select-box__value-container {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
@ -119,10 +127,12 @@ $select-input-bg-disabled: $input-bg-disabled;
|
|||||||
border-width: 0 5px 5px;
|
border-width: 0 5px 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gf-form-input--form-dropdown {
|
.gf-form-input--form-dropdown {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gf-form--has-input-icon {
|
.gf-form--has-input-icon {
|
||||||
|
@ -275,6 +275,7 @@
|
|||||||
|
|
||||||
.ds-picker {
|
.ds-picker {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ds-picker-menu {
|
.ds-picker-menu {
|
||||||
|
Loading…
Reference in New Issue
Block a user