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) => {
|
||||
const { children, isSelected, data, className } = props;
|
||||
const { children, isSelected, data } = props;
|
||||
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<div className={`description-picker-option__button btn btn-link ${className}`}>
|
||||
{isSelected && <i className="fa fa-check pull-right" aria-hidden="true" />}
|
||||
<div className="gf-form">{children}</div>
|
||||
<div className="gf-form">
|
||||
<div className="muted width-17">{data.description}</div>
|
||||
<div className="gf-form-select-box__desc-option">
|
||||
<div className="gf-form-select-box__desc-option__body">
|
||||
<div>{children}</div>
|
||||
{data.description && <div className="gf-form-select-box__desc-option__desc">{data.description}</div>}
|
||||
</div>
|
||||
{isSelected && <i className="fa fa-check" aria-hidden="true" />}
|
||||
</div>
|
||||
</components.Option>
|
||||
);
|
||||
|
@ -1,7 +1,14 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
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';
|
||||
|
||||
export interface Props {
|
||||
@ -10,127 +17,50 @@ export interface Props {
|
||||
current: DataSourceSelectItem;
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchQuery: string;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
export class DataSourcePicker extends PureComponent<Props, State> {
|
||||
export class DataSourcePicker extends PureComponent<Props> {
|
||||
searchInput: HTMLElement;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
isOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
getDataSources() {
|
||||
const { searchQuery } = this.state;
|
||||
const regex = new RegExp(searchQuery, 'i');
|
||||
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 });
|
||||
onChange = item => {
|
||||
const ds = this.props.datasources.find(ds => ds.name === item.value);
|
||||
this.props.onChangeDataSource(ds);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { current } = this.props;
|
||||
const { isOpen } = this.state;
|
||||
const { datasources, current, onChangeDatasource } = this.props;
|
||||
|
||||
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 (
|
||||
<div className="ds-picker">
|
||||
{!isOpen && (
|
||||
<div className="toolbar__main" onClick={this.onOpen}>
|
||||
<img className="toolbar__main-image" src={current.meta.info.logos.small} />
|
||||
<div className="toolbar__main-name">{current.name}</div>
|
||||
<i className="fa fa-caret-down" />
|
||||
</div>
|
||||
)}
|
||||
{isOpen && (
|
||||
<KeyboardNavigation
|
||||
render={(keyNavProps: KeyboardNavigationProps) => (
|
||||
<div className="ds-picker-menu">
|
||||
<div className="cta-form__bar">
|
||||
{this.renderFilters(keyNavProps)}
|
||||
<div className="gf-form--grow" />
|
||||
</div>
|
||||
<div className="ds-picker-list">
|
||||
{this.getDataSources().map((ds, index) => this.renderDataSource(ds, index, keyNavProps))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<div className="gf-form-inline">
|
||||
<Select
|
||||
classNamePrefix={`gf-form-select-box`}
|
||||
isMulti={false}
|
||||
menuShouldScrollIntoView={false}
|
||||
isClearable={false}
|
||||
className="gf-form-input gf-form-input--form-dropdown ds-picker"
|
||||
onChange={item => this.onChange(item)}
|
||||
options={options}
|
||||
styles={ResetStyles}
|
||||
maxMenuHeight={500}
|
||||
placeholder="Select datasource"
|
||||
loadingMessage={() => 'Loading datasources...'}
|
||||
noOptionsMessage={() => 'No datasources found'}
|
||||
value={value}
|
||||
components={{
|
||||
Option: PickerOption,
|
||||
IndicatorsContainer,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -9,3 +9,27 @@
|
||||
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 {
|
||||
background: $dropdownBackground;
|
||||
background: $input-bg;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.gf-form-select-box__menu-list {
|
||||
@ -64,12 +65,16 @@ $select-input-bg-disabled: $input-bg-disabled;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* .gf-form-select-box__single-value { */
|
||||
/* } */
|
||||
|
||||
.gf-form-select-box__multi-value {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.gf-form-select-box__option {
|
||||
border-left: 2px solid transparent;
|
||||
white-space: nowrap;
|
||||
|
||||
&.gf-form-select-box__option--is-focused {
|
||||
color: $dropdownLinkColorHover;
|
||||
@ -88,6 +93,9 @@ $select-input-bg-disabled: $input-bg-disabled;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gf-form-select-box__option {
|
||||
}
|
||||
|
||||
.gf-form-select-box__value-container {
|
||||
display: table-cell;
|
||||
padding: 8px 10px;
|
||||
@ -119,10 +127,12 @@ $select-input-bg-disabled: $input-bg-disabled;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-input--form-dropdown {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gf-form--has-input-icon {
|
||||
|
@ -275,6 +275,7 @@
|
||||
|
||||
.ds-picker {
|
||||
position: relative;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.ds-picker-menu {
|
||||
|
Loading…
Reference in New Issue
Block a user