mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Migrating color pickers to Popper from drop.js pt1
This commit is contained in:
parent
c74b39696c
commit
33fa40a1f3
@ -1,69 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Drop from 'tether-drop';
|
||||
import React, { Component, createRef } from 'react';
|
||||
import PopperController from '../Tooltip/PopperController';
|
||||
import Popper from '../Tooltip/Popper';
|
||||
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||
import { Color } from '../../utils/colorsPalette';
|
||||
import { ColorDefinition } from '../../utils/colorsPalette';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Value to display, either empty (" ") or "X" / "O".
|
||||
*
|
||||
* @default " "
|
||||
**/
|
||||
name?: Color;
|
||||
color: string;
|
||||
onChange: (c: string) => void;
|
||||
}
|
||||
|
||||
export class ColorPicker extends Component<Props, any> {
|
||||
private pickerTriggerRef = createRef<HTMLDivElement>();
|
||||
pickerElem: HTMLElement | null;
|
||||
colorPickerDrop: any;
|
||||
|
||||
openColorPicker = () => {
|
||||
const dropContent = <ColorPickerPopover color={this.props.color} onColorSelect={this.onColorSelect} />;
|
||||
|
||||
const dropContentElem = document.createElement('div');
|
||||
ReactDOM.render(dropContent, dropContentElem);
|
||||
|
||||
const drop = new Drop({
|
||||
target: this.pickerElem as Element,
|
||||
content: dropContentElem,
|
||||
position: 'top center',
|
||||
classes: 'drop-popover',
|
||||
openOn: 'click',
|
||||
hoverCloseDelay: 200,
|
||||
tetherOptions: {
|
||||
constraints: [{ to: 'scrollParent', attachment: 'none both' }],
|
||||
attachment: 'bottom center',
|
||||
},
|
||||
});
|
||||
|
||||
drop.on('close', this.closeColorPicker);
|
||||
|
||||
this.colorPickerDrop = drop;
|
||||
this.colorPickerDrop.open();
|
||||
onColorSelect = (color: ColorDefinition) => {
|
||||
this.props.onChange(color.name);
|
||||
};
|
||||
|
||||
closeColorPicker = () => {
|
||||
setTimeout(() => {
|
||||
if (this.colorPickerDrop && this.colorPickerDrop.tether) {
|
||||
this.colorPickerDrop.destroy();
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
onColorSelect = (color: string) => {
|
||||
this.props.onChange(color);
|
||||
renderPickerTabs = () => {
|
||||
return <ColorPickerPopover color="" onColorSelect={() => {}} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className="sp-replacer sp-light" onClick={this.openColorPicker} ref={element => (this.pickerElem = element)}>
|
||||
<PopperController content={this.renderPickerTabs}>
|
||||
{(showPopper, hidePopper, popperProps) => {
|
||||
return (
|
||||
<>
|
||||
{this.pickerTriggerRef.current && (
|
||||
<Popper {...popperProps} referenceElement={this.pickerTriggerRef.current} className="ColorPicker" />
|
||||
)}
|
||||
<div ref={this.pickerTriggerRef} onClick={showPopper} className="sp-replacer sp-light">
|
||||
<div className="sp-preview">
|
||||
<div className="sp-preview-inner" style={{ backgroundColor: this.props.color }} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</PopperController>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,111 +1,76 @@
|
||||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { ColorPalette } from './ColorPalette';
|
||||
import React, { Children } from 'react';
|
||||
import NamedColorsPicker from './NamedColorsPicker';
|
||||
import { Color } from 'csstype';
|
||||
import { ColorDefinition, getColorName } from '../..//utils/colorsPalette';
|
||||
import { SpectrumPicker } from './SpectrumPicker';
|
||||
import { GrafanaTheme } from '../../types';
|
||||
|
||||
const DEFAULT_COLOR = '#000000';
|
||||
// const DEFAULT_COLOR = '#000000';
|
||||
|
||||
export interface Props {
|
||||
color: string;
|
||||
onColorSelect: (c: string) => void;
|
||||
color: Color | string;
|
||||
theme?: GrafanaTheme;
|
||||
onColorSelect: (color: string | ColorDefinition) => void;
|
||||
}
|
||||
|
||||
export class ColorPickerPopover extends React.Component<Props, any> {
|
||||
pickerNavElem: any;
|
||||
type PickerType = 'palette' | 'spectrum';
|
||||
|
||||
interface State {
|
||||
activePicker: PickerType;
|
||||
}
|
||||
|
||||
export class ColorPickerPopover extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
tab: 'palette',
|
||||
color: this.props.color || DEFAULT_COLOR,
|
||||
colorString: this.props.color || DEFAULT_COLOR,
|
||||
activePicker: 'spectrum',
|
||||
};
|
||||
}
|
||||
|
||||
setPickerNavElem(elem: any) {
|
||||
this.pickerNavElem = $(elem);
|
||||
}
|
||||
handleSpectrumColorSelect = (color: any) => {
|
||||
this.props.onColorSelect(color.toRgbString());
|
||||
};
|
||||
|
||||
setColor(color: string) {
|
||||
const newColor = tinycolor(color);
|
||||
if (newColor.isValid()) {
|
||||
this.setState({ color: newColor.toString(), colorString: newColor.toString() });
|
||||
this.props.onColorSelect(color);
|
||||
}
|
||||
}
|
||||
renderPicker = () => {
|
||||
const { activePicker } = this.state;
|
||||
const { color } = this.props;
|
||||
|
||||
sampleColorSelected(color: string) {
|
||||
this.setColor(color);
|
||||
}
|
||||
|
||||
spectrumColorSelected(color: any) {
|
||||
const rgbColor = color.toRgbString();
|
||||
this.setColor(rgbColor);
|
||||
}
|
||||
|
||||
onColorStringChange(e: any) {
|
||||
const colorString = e.target.value;
|
||||
this.setState({ colorString: colorString });
|
||||
|
||||
const newColor = tinycolor(colorString);
|
||||
if (newColor.isValid()) {
|
||||
// Update only color state
|
||||
const newColorString = newColor.toString();
|
||||
this.setState({ color: newColorString });
|
||||
this.props.onColorSelect(newColorString);
|
||||
}
|
||||
}
|
||||
|
||||
onColorStringBlur(e: any) {
|
||||
const colorString = e.target.value;
|
||||
this.setColor(colorString);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.pickerNavElem.find('li:first').addClass('active');
|
||||
this.pickerNavElem.on('show', (e: any) => {
|
||||
// use href attr (#name => name)
|
||||
const tab = e.target.hash.slice(1);
|
||||
this.setState({ tab: tab });
|
||||
});
|
||||
}
|
||||
return activePicker === 'spectrum' ? (
|
||||
<SpectrumPicker color={color} onColorSelect={this.handleSpectrumColorSelect} options={{}} />
|
||||
) : (
|
||||
<NamedColorsPicker selectedColor={getColorName(color)} onChange={this.props.onColorSelect} />
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const paletteTab = (
|
||||
<div id="palette">
|
||||
<ColorPalette color={this.state.color} onColorSelect={this.sampleColorSelected.bind(this)} />
|
||||
</div>
|
||||
);
|
||||
const spectrumTab = (
|
||||
<div id="spectrum">
|
||||
<SpectrumPicker color={this.state.color} onColorSelect={this.spectrumColorSelected.bind(this)} options={{}} />
|
||||
</div>
|
||||
);
|
||||
const currentTab = this.state.tab === 'palette' ? paletteTab : spectrumTab;
|
||||
const { activePicker } = this.state;
|
||||
const { theme, children } = this.props;
|
||||
const colorPickerTheme = theme || GrafanaTheme.Dark;
|
||||
|
||||
return (
|
||||
<div className="gf-color-picker">
|
||||
<ul className="nav nav-tabs" id="colorpickernav" ref={this.setPickerNavElem.bind(this)}>
|
||||
<li className="gf-tabs-item-colorpicker">
|
||||
<a href="#palette" data-toggle="tab">
|
||||
Colors
|
||||
</a>
|
||||
</li>
|
||||
<li className="gf-tabs-item-colorpicker">
|
||||
<a href="#spectrum" data-toggle="tab">
|
||||
<div className={`ColorPickerPopover ColorPickerPopover--${colorPickerTheme}`}>
|
||||
<div className="ColorPickerPopover__tabs">
|
||||
<div
|
||||
className={`ColorPickerPopover__tab ${activePicker === 'palette' && 'ColorPickerPopover__tab--active'}`}
|
||||
onClick={() => {
|
||||
this.setState({ activePicker: 'palette' });
|
||||
}}
|
||||
>
|
||||
Default
|
||||
</div>
|
||||
<div
|
||||
className={`ColorPickerPopover__tab ${activePicker === 'spectrum' && 'ColorPickerPopover__tab--active'}`}
|
||||
onClick={() => {
|
||||
this.setState({ activePicker: 'spectrum' });
|
||||
}}
|
||||
>
|
||||
Custom
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="gf-color-picker__body">{currentTab}</div>
|
||||
<div>
|
||||
<input
|
||||
className="gf-form-input gf-form-input--small"
|
||||
value={this.state.colorString}
|
||||
onChange={this.onColorStringChange.bind(this)}
|
||||
onBlur={this.onColorStringBlur.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="ColorPickerPopover__content">
|
||||
{this.renderPicker()}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -47,19 +47,22 @@ const ColorSwatch: FunctionComponent<ColorSwatchProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ColorsGroup = ({
|
||||
interface ColorsGroupProps {
|
||||
colors: ColorDefinition[];
|
||||
selectedColor?: Color;
|
||||
onColorSelect: ColorChangeHandler;
|
||||
key?: string;
|
||||
}
|
||||
const ColorsGroup: FunctionComponent<ColorsGroupProps> = ({
|
||||
colors,
|
||||
selectedColor,
|
||||
onColorSelect,
|
||||
}: {
|
||||
colors: ColorDefinition[];
|
||||
selectedColor?: Color;
|
||||
onColorSelect: ColorChangeHandler
|
||||
...otherProps
|
||||
}) => {
|
||||
const primaryColor = find(colors, color => !!color.isPrimary);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div {...otherProps} style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{primaryColor && (
|
||||
<ColorSwatch
|
||||
isSelected={primaryColor.name === selectedColor}
|
||||
@ -75,7 +78,7 @@ const ColorsGroup = ({
|
||||
}}
|
||||
>
|
||||
{colors.map(color => !color.isPrimary && (
|
||||
<div style={{ marginRight: '4px' }}>
|
||||
<div key={color.name} style={{ marginRight: '4px' }}>
|
||||
<ColorSwatch
|
||||
isSelected={color.name === selectedColor}
|
||||
color={color}
|
||||
@ -88,7 +91,6 @@ const ColorsGroup = ({
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
interface NamedColorsPickerProps {
|
||||
selectedColor?: Color;
|
||||
onChange: ColorChangeHandler;
|
||||
@ -98,9 +100,7 @@ const NamedColorsPicker = ({ selectedColor, onChange }: NamedColorsPickerProps)
|
||||
|
||||
ColorsPalete.forEach((colors, hue) => {
|
||||
swatches.push(
|
||||
<>
|
||||
<ColorsGroup selectedColor={selectedColor} colors={colors} onColorSelect={onChange} />
|
||||
</>
|
||||
<ColorsGroup key={hue} selectedColor={selectedColor} colors={colors} onColorSelect={onChange} />
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Drop from 'tether-drop';
|
||||
import React, { createRef } from 'react';
|
||||
import * as PopperJS from 'popper.js';
|
||||
import { SeriesColorPickerPopover } from './SeriesColorPickerPopover';
|
||||
import PopperController from '../Tooltip/PopperController';
|
||||
import Popper from '../Tooltip/Popper';
|
||||
import { GrafanaTheme } from '../../types';
|
||||
|
||||
export interface SeriesColorPickerProps {
|
||||
color: string;
|
||||
@ -9,10 +11,12 @@ export interface SeriesColorPickerProps {
|
||||
optionalClass?: string;
|
||||
onColorChange: (newColor: string) => void;
|
||||
onToggleAxis?: () => void;
|
||||
children: JSX.Element;
|
||||
theme?: GrafanaTheme;
|
||||
}
|
||||
|
||||
export class SeriesColorPicker extends React.Component<SeriesColorPickerProps> {
|
||||
pickerElem: any;
|
||||
private pickerTriggerRef = createRef<PopperJS.ReferenceObject>();
|
||||
colorPickerDrop: any;
|
||||
|
||||
static defaultProps = {
|
||||
@ -21,65 +25,46 @@ export class SeriesColorPicker extends React.Component<SeriesColorPickerProps> {
|
||||
onToggleAxis: () => {},
|
||||
};
|
||||
|
||||
constructor(props: SeriesColorPickerProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.destroyDrop();
|
||||
}
|
||||
|
||||
onClickToOpen = () => {
|
||||
if (this.colorPickerDrop) {
|
||||
this.destroyDrop();
|
||||
}
|
||||
|
||||
const { color, yaxis, onColorChange, onToggleAxis } = this.props;
|
||||
const dropContent = (
|
||||
<SeriesColorPickerPopover color={color} yaxis={yaxis} onColorChange={onColorChange} onToggleAxis={onToggleAxis} />
|
||||
renderPickerTabs = () => {
|
||||
const { color, yaxis, onColorChange, onToggleAxis, theme } = this.props;
|
||||
return (
|
||||
<SeriesColorPickerPopover
|
||||
theme={theme}
|
||||
color={color}
|
||||
yaxis={yaxis}
|
||||
onColorChange={onColorChange}
|
||||
onToggleAxis={onToggleAxis}
|
||||
/>
|
||||
);
|
||||
const dropContentElem = document.createElement('div');
|
||||
ReactDOM.render(dropContent, dropContentElem);
|
||||
|
||||
const drop = new Drop({
|
||||
target: this.pickerElem,
|
||||
content: dropContentElem,
|
||||
position: 'bottom center',
|
||||
classes: 'drop-popover',
|
||||
openOn: 'hover',
|
||||
hoverCloseDelay: 200,
|
||||
remove: true,
|
||||
tetherOptions: {
|
||||
constraints: [{ to: 'scrollParent', attachment: 'none both' }],
|
||||
attachment: 'bottom center',
|
||||
},
|
||||
});
|
||||
|
||||
drop.on('close', this.closeColorPicker.bind(this));
|
||||
|
||||
this.colorPickerDrop = drop;
|
||||
this.colorPickerDrop.open();
|
||||
};
|
||||
|
||||
closeColorPicker() {
|
||||
setTimeout(() => {
|
||||
this.destroyDrop();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
destroyDrop() {
|
||||
if (this.colorPickerDrop && this.colorPickerDrop.tether) {
|
||||
this.colorPickerDrop.destroy();
|
||||
this.colorPickerDrop = null;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { optionalClass, children } = this.props;
|
||||
const { children } = this.props;
|
||||
|
||||
return (
|
||||
<div className={optionalClass} ref={e => (this.pickerElem = e)} onClick={this.onClickToOpen}>
|
||||
{children}
|
||||
</div>
|
||||
<PopperController placement="bottom-start" content={this.renderPickerTabs}>
|
||||
{(showPopper, hidePopper, popperProps) => {
|
||||
return (
|
||||
<>
|
||||
{this.pickerTriggerRef.current && (
|
||||
<Popper
|
||||
{...popperProps}
|
||||
onMouseEnter={showPopper}
|
||||
onMouseLeave={hidePopper}
|
||||
referenceElement={this.pickerTriggerRef.current}
|
||||
className="ColorPicker"
|
||||
arrowClassName="popper__arrow"
|
||||
/>
|
||||
)}
|
||||
{React.cloneElement(children, {
|
||||
ref: this.pickerTriggerRef,
|
||||
onClick: showPopper,
|
||||
onMouseLeave: hidePopper,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</PopperController>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,24 @@
|
||||
import React from 'react';
|
||||
import { ColorPickerPopover } from './ColorPickerPopover';
|
||||
import { GrafanaTheme } from '../../types';
|
||||
|
||||
export interface SeriesColorPickerPopoverProps {
|
||||
color: string;
|
||||
yaxis?: number;
|
||||
onColorChange: (color: string) => void;
|
||||
onToggleAxis?: () => void;
|
||||
theme?: GrafanaTheme;
|
||||
}
|
||||
|
||||
export class SeriesColorPickerPopover extends React.PureComponent<SeriesColorPickerPopoverProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<div className="graph-legend-popover">
|
||||
<div>
|
||||
<ColorPickerPopover theme={this.props.theme} color={this.props.color} onColorSelect={this.props.onColorChange}>
|
||||
<div style={{ marginTop: '32px' }}>
|
||||
{this.props.yaxis && <AxisSelector yaxis={this.props.yaxis} onToggleAxis={this.props.onToggleAxis} />}
|
||||
<ColorPickerPopover color={this.props.color} onColorSelect={this.props.onColorChange} />
|
||||
</div>
|
||||
</ColorPickerPopover>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import '../../vendor/spectrum';
|
||||
export interface Props {
|
||||
color: string;
|
||||
options: object;
|
||||
onColorSelect: (c: string) => void;
|
||||
onColorSelect: (color: string) => void;
|
||||
}
|
||||
|
||||
export class SpectrumPicker extends React.Component<Props, any> {
|
||||
|
@ -1,3 +1,54 @@
|
||||
.ColorPicker {
|
||||
.popper__arrow {
|
||||
border-color: #f7f8fa;
|
||||
}
|
||||
}
|
||||
|
||||
.ColorPickerPopover {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ColorPickerPopover--light {
|
||||
color: black;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f7f8fa 104.25%);
|
||||
box-shadow: 0px 2px 4px #dde4ed, 0px 0px 2px #dde4ed;
|
||||
}
|
||||
|
||||
.ColorPickerPopover--dark {
|
||||
color: #d8d9da;
|
||||
background: linear-gradient(180deg, #1e2028 0%, #161719 104.25%);
|
||||
box-shadow: 0px 2px 4px #000000, 0px 0px 2px #000000;
|
||||
|
||||
.ColorPickerPopover__tab {
|
||||
background: #303133;
|
||||
color: white;
|
||||
}
|
||||
.ColorPickerPopover__tab--active {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ColorPickerPopover__content {
|
||||
width: 360px;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.ColorPickerPopover__tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ColorPickerPopover__tab {
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
background: #dde4ed;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ColorPickerPopover__tab--active {
|
||||
background: white;
|
||||
}
|
||||
.sp-replacer {
|
||||
background: inherit;
|
||||
border: none;
|
||||
|
@ -3,3 +3,8 @@ export * from './time';
|
||||
export * from './panel';
|
||||
export * from './plugin';
|
||||
export * from './datasource';
|
||||
|
||||
export enum GrafanaTheme {
|
||||
Light = 'light',
|
||||
Dark = 'dark',
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { getFlotTickDecimals } from 'app/core/utils/ticks';
|
||||
import _ from 'lodash';
|
||||
import { ColorDefinition } from '@grafana/ui/src/utils/colorsPalette';
|
||||
|
||||
function matchSeriesOverride(aliasOrRegex, seriesAlias) {
|
||||
if (!aliasOrRegex) {
|
||||
@ -356,8 +357,13 @@ export default class TimeSeries {
|
||||
return false;
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
setColor(color: string | ColorDefinition) {
|
||||
if (typeof color === 'string') {
|
||||
this.color = color;
|
||||
this.bars.fillColor = color;
|
||||
} else {
|
||||
this.color = color.variants.dark;
|
||||
this.bars.fillColor = color.variants.dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user