mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Start implementing the upgraded react-select in the tag filter box #13425
This commit is contained in:
parent
c0b7ca3902
commit
4d2be024f4
@ -5,17 +5,12 @@ export interface Props {
|
||||
label: string;
|
||||
removeIcon: boolean;
|
||||
count: number;
|
||||
onClick: any;
|
||||
onClick?: any;
|
||||
}
|
||||
|
||||
export class TagBadge extends React.Component<Props, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick(event) {
|
||||
this.props.onClick(event);
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -28,7 +23,7 @@ export class TagBadge extends React.Component<Props, any> {
|
||||
const countLabel = count !== 0 && <span className="tag-count-label">{`(${count})`}</span>;
|
||||
|
||||
return (
|
||||
<span className={`label label-tag`} onClick={this.onClick} style={tagStyle}>
|
||||
<span className={`label label-tag`} style={tagStyle}>
|
||||
{removeIcon && <i className="fa fa-remove" />}
|
||||
{label} {countLabel}
|
||||
</span>
|
||||
|
@ -1,8 +1,13 @@
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { Async } from 'react-select';
|
||||
import AsyncSelect from 'react-select/lib/Async';
|
||||
import { TagValue } from './TagValue';
|
||||
import { TagOption } from './TagOption';
|
||||
import { TagBadge } from './TagBadge';
|
||||
import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
|
||||
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
||||
import { components } from 'react-select';
|
||||
import ResetStyles from 'app/core/components/Picker/ResetStyles';
|
||||
|
||||
export interface Props {
|
||||
tags: string[];
|
||||
@ -23,10 +28,11 @@ export class TagFilter extends React.Component<Props, any> {
|
||||
|
||||
searchTags(query) {
|
||||
return this.props.tagOptions().then(options => {
|
||||
const tags = _.map(options, tagOption => {
|
||||
return { value: tagOption.term, label: tagOption.term, count: tagOption.count };
|
||||
});
|
||||
return { options: tags };
|
||||
return options.map(option => ({
|
||||
value: option.term,
|
||||
label: option.term,
|
||||
count: option.count,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@ -44,23 +50,65 @@ export class TagFilter extends React.Component<Props, any> {
|
||||
|
||||
render() {
|
||||
const selectOptions = {
|
||||
classNamePrefix: 'gf-form-select2',
|
||||
isMulti: true,
|
||||
defaultOptions: true,
|
||||
loadOptions: this.searchTags,
|
||||
onChange: this.onChange,
|
||||
value: this.props.tags,
|
||||
multi: true,
|
||||
className: 'gf-form-input gf-form-input--form-dropdown',
|
||||
placeholder: 'Tags',
|
||||
loadingPlaceholder: 'Loading...',
|
||||
noResultsText: 'No tags found',
|
||||
optionComponent: TagOption,
|
||||
loadingMessage: () => 'Loading...',
|
||||
noOptionsMessage: () => 'No tags found',
|
||||
getOptionValue: i => i.value,
|
||||
getOptionLabel: i => i.label,
|
||||
styles: ResetStyles,
|
||||
components: {
|
||||
Option: TagOption,
|
||||
IndicatorsContainer,
|
||||
NoOptionsMessage,
|
||||
MultiValueContainer: props => {
|
||||
const { data } = props;
|
||||
return (
|
||||
<components.MultiValueContainer {...props}>
|
||||
<TagBadge label={data.label} removeIcon={true} count={data.count} />
|
||||
</components.MultiValueContainer>
|
||||
);
|
||||
},
|
||||
MultiValueRemove: props => {
|
||||
return <components.MultiValueRemove {...props}>X</components.MultiValueRemove>;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// <AsyncSelect
|
||||
// classNamePrefix={`gf-form-select2`}
|
||||
// isMulti={false}
|
||||
// isLoading={isLoading}
|
||||
// defaultOptions={true}
|
||||
// loadOptions={this.debouncedSearch}
|
||||
// onChange={onSelected}
|
||||
// className={`gf-form-input gf-form-input--form-dropdown ${className || ''}`}
|
||||
// styles={ResetStyles}
|
||||
// components={{
|
||||
// Option: PickerOption,
|
||||
// IndicatorsContainer,
|
||||
// NoOptionsMessage,
|
||||
// }}
|
||||
// placeholder="Select user"
|
||||
// filterOption={(option: { label: string }, searchText?: string) => {
|
||||
// return option.label.includes(searchText);
|
||||
// }}
|
||||
// loadingMessage={() => 'Loading...'}
|
||||
// noOptionsMessage={() => 'No users found'}
|
||||
// getOptionValue={i => i.id}
|
||||
// getOptionLabel={i => i.label}
|
||||
|
||||
selectOptions['valueComponent'] = TagValue;
|
||||
|
||||
return (
|
||||
<div className="gf-form gf-form--has-input-icon gf-form--grow">
|
||||
<div className="tag-filter">
|
||||
<Async {...selectOptions} />
|
||||
<AsyncSelect {...selectOptions} />
|
||||
</div>
|
||||
<i className="gf-form-input-icon fa fa-tag" />
|
||||
</div>
|
||||
|
@ -1,52 +1,75 @@
|
||||
import React from 'react';
|
||||
import { components } from 'react-select';
|
||||
import { OptionProps } from 'react-select/lib/components/Option';
|
||||
import { TagBadge } from './TagBadge';
|
||||
|
||||
export interface Props {
|
||||
onSelect: any;
|
||||
onFocus: any;
|
||||
option: any;
|
||||
isFocused: any;
|
||||
className: any;
|
||||
// https://github.com/JedWatson/react-select/issues/3038
|
||||
interface ExtendedOptionProps extends OptionProps<any> {
|
||||
data: any;
|
||||
}
|
||||
|
||||
export class TagOption extends React.Component<Props, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleMouseDown = this.handleMouseDown.bind(this);
|
||||
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||
this.handleMouseMove = this.handleMouseMove.bind(this);
|
||||
}
|
||||
export const TagOption = (props: ExtendedOptionProps) => {
|
||||
const { data, className, label } = props;
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<div className={`tag-filter-option btn btn-link ${className || ''}`}>
|
||||
<TagBadge label={label} removeIcon={true} count={data.count} />
|
||||
</div>
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
|
||||
handleMouseDown(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.props.onSelect(this.props.option, event);
|
||||
}
|
||||
export default TagOption;
|
||||
|
||||
handleMouseEnter(event) {
|
||||
this.props.onFocus(this.props.option, event);
|
||||
}
|
||||
// import React from 'react';
|
||||
// import { TagBadge } from './TagBadge';
|
||||
|
||||
handleMouseMove(event) {
|
||||
if (this.props.isFocused) {
|
||||
return;
|
||||
}
|
||||
this.props.onFocus(this.props.option, event);
|
||||
}
|
||||
// export interface Props {
|
||||
// onSelect: any;
|
||||
// onFocus: any;
|
||||
// option: any;
|
||||
// isFocused: any;
|
||||
// className: any;
|
||||
// }
|
||||
|
||||
render() {
|
||||
const { option, className } = this.props;
|
||||
// export class TagOption extends React.Component<Props, any> {
|
||||
// constructor(props) {
|
||||
// super(props);
|
||||
// this.handleMouseDown = this.handleMouseDown.bind(this);
|
||||
// this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||
// this.handleMouseMove = this.handleMouseMove.bind(this);
|
||||
// }
|
||||
|
||||
return (
|
||||
<button
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseMove={this.handleMouseMove}
|
||||
title={option.title}
|
||||
className={`tag-filter-option btn btn-link ${className || ''}`}
|
||||
>
|
||||
<TagBadge label={option.label} removeIcon={false} count={option.count} onClick={this.handleMouseDown} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
// handleMouseDown(event) {
|
||||
// event.preventDefault();
|
||||
// event.stopPropagation();
|
||||
// this.props.onSelect(this.props.option, event);
|
||||
// }
|
||||
|
||||
// handleMouseEnter(event) {
|
||||
// this.props.onFocus(this.props.option, event);
|
||||
// }
|
||||
|
||||
// handleMouseMove(event) {
|
||||
// if (this.props.isFocused) {
|
||||
// return;
|
||||
// }
|
||||
// this.props.onFocus(this.props.option, event);
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// const { option, className } = this.props;
|
||||
|
||||
// return (
|
||||
// <button
|
||||
// onMouseDown={this.handleMouseDown}
|
||||
// onMouseEnter={this.handleMouseEnter}
|
||||
// onMouseMove={this.handleMouseMove}
|
||||
// title={option.title}
|
||||
// className={`tag-filter-option btn btn-link ${className || ''}`}
|
||||
// >
|
||||
// <TagBadge label={option.label} removeIcon={false} count={option.count} onClick={this.handleMouseDown} />
|
||||
// </button>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
@ -21,6 +21,6 @@ export class TagValue extends React.Component<Props, any> {
|
||||
render() {
|
||||
const { value } = this.props;
|
||||
|
||||
return <TagBadge label={value.label} removeIcon={true} count={0} onClick={this.onClick} />;
|
||||
return <TagBadge label={value.label} removeIcon={false} count={0} onClick={this.onClick} />;
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,15 @@ $select-input-bg-disabled: $input-bg-disabled;
|
||||
background: $select-input-bg-disabled;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tag-filter .gf-form-select2__menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gf-form-select2__multi-value {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.gf-form-select2__option {
|
||||
@ -106,3 +115,9 @@ $select-input-bg-disabled: $input-bg-disabled;
|
||||
border: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.gf-form--has-input-icon {
|
||||
.gf-form-select2__value-container {
|
||||
padding-left: 30px;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user