diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3a18175d4..42e8cf85bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## [`master`](https://github.com/elastic/eui/tree/master) +- Added `stepNumber` prop and `stepped` as `stopType` option to `EuiColorStops` ([#4613](https://github.com/elastic/eui/pull/4613)) - Expanded `display` prop of `EuiCard` to inherit `color` values from `EuiPanel` ([#4649](https://github.com/elastic/eui/pull/4649)) - Added `element` prop to `EuiPanel` for forcing to `div` or `button` ([#4649](https://github.com/elastic/eui/pull/4649)) - Increased padding on `EuiCheckableCard` with refactor to use `EuiSplitPanel` ([#4649](https://github.com/elastic/eui/pull/4649)) diff --git a/src-docs/src/views/color_picker/color_picker_example.js b/src-docs/src/views/color_picker/color_picker_example.js index 36dc87dfe69..f78de25ab37 100644 --- a/src-docs/src/views/color_picker/color_picker_example.js +++ b/src-docs/src/views/color_picker/color_picker_example.js @@ -108,6 +108,17 @@ const colorStopsSnippetFixed = ` `; +const colorStopsSnippetStepped = ` +`; + import ColorStopsRange from './color_stops_range'; const colorStopsRangeSource = require('!!raw-loader!./color_stops_range'); const colorStopsRangeHtml = renderToHtml(ColorStopsRange); @@ -477,6 +488,7 @@ export const ColorPickerExample = { colorStopsSnippetStandard, colorStopsSnippetAdd, colorStopsSnippetFixed, + colorStopsSnippetStepped, ], demo: , }, diff --git a/src-docs/src/views/color_picker/color_stops.js b/src-docs/src/views/color_picker/color_stops.js index 8616285c23c..d502073d140 100644 --- a/src-docs/src/views/color_picker/color_stops.js +++ b/src-docs/src/views/color_picker/color_stops.js @@ -1,6 +1,14 @@ import React, { useState } from 'react'; -import { EuiColorStops, EuiFormRow } from '../../../../src/components'; +import { + EuiColorStops, + EuiFormRow, + EuiRange, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiPopover, +} from '../../../../src/components'; import { useColorStopsState } from '../../../../src/services'; @@ -12,6 +20,9 @@ export default () => { addRandomColor, ] = useColorStopsState(true); const [fixedColorStops, setFixedColorStops] = useColorStopsState(true); + const [steppedColorStops, setSteppedColorStops] = useColorStopsState(true); + const [value, setValue] = useState(10); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [extendedColorStops, setExtendedColorStops] = useState([ { @@ -38,6 +49,21 @@ export default () => { setEmptyColorStops(colorStops); }; + const onButtonClick = () => + setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + + const button = ( + + Steps + + ); + return ( @@ -87,6 +113,41 @@ export default () => { stopType="fixed" /> + + + + + + + + + + + setValue(parseInt(e.target.value))} + showInput + aria-label="Change the number of steps" + min={2} + max={20} + /> + + + + + ); }; diff --git a/src/components/color_picker/color_stops/__snapshots__/color_stops.test.tsx.snap b/src/components/color_picker/color_stops/__snapshots__/color_stops.test.tsx.snap index 3b60b39b4c7..683de1f390c 100644 --- a/src/components/color_picker/color_stops/__snapshots__/color_stops.test.tsx.snap +++ b/src/components/color_picker/color_stops/__snapshots__/color_stops.test.tsx.snap @@ -812,3 +812,11 @@ exports[`renders readOnly EuiColorStops 1`] = ` `; + +exports[`renders stepped stop EuiColorStops 1`] = ` +Object { + "background": "linear-gradient(to right, currentColor 0%, #ff0000 0% 2.94%, #d72800 2.94% 5.88%, #b04f00 5.880000000000001% 8.82%, #887700 8.82% 11.76%, #609f00 11.76% 14.7%, #39c600 14.700000000000001% 17.64%, #11ee00 17.64% 20.580000000000002%, #00c639 20.580000000000002% 23.520000000000003%, #00639c 23.520000000000003% 26.460000000000004%, #0000ff 26.460000000000004% 29.400000000000006%)", + "marginLeft": "0%", + "width": "100%", +} +`; diff --git a/src/components/color_picker/color_stops/color_stops.test.tsx b/src/components/color_picker/color_stops/color_stops.test.tsx index 9820b805779..24960173f62 100644 --- a/src/components/color_picker/color_stops/color_stops.test.tsx +++ b/src/components/color_picker/color_stops/color_stops.test.tsx @@ -173,6 +173,24 @@ test('renders fixed stop EuiColorStops', () => { expect(colorStops).toMatchSnapshot(); }); +test('renders stepped stop EuiColorStops', () => { + const colorStops = mount( + + ); + expect( + colorStops.find('.euiRangeHighlight__progress').prop('style') + ).toMatchSnapshot(); +}); + test('renders empty EuiColorStops', () => { const colorStops = render( = ({ className, label, stopType = 'gradient', + stepNumber = 10, swatches, showAlpha = false, valueInputProps, @@ -310,7 +325,6 @@ export const EuiColorStops: FunctionComponent = ({ if (isNotInteractive || isTargetAThumb(e.target) || !wrapperRef) return; const newStop = getStopFromMouseLocationFn({ x: e.pageX, y: e.pageY }); const newColorStops = addDefinedStop(colorStops, newStop, addColor); - setFocusStopOnUpdate(newStop); handleOnChange(newColorStops); }; @@ -461,10 +475,33 @@ export const EuiColorStops: FunctionComponent = ({ )}`; } }; - const linearGradient = sortedStops.map( - stopType === 'gradient' ? gradientStop : fixedStop - ); - const background = `linear-gradient(to right,${linearGradient})`; + + let gradient: string = ''; + + if (stopType === 'stepped' && positions.length > 0) { + const trailingPercentage = positions[0]; + const endingPercentage = positions[positions.length - 1]; + const steppedColors = getSteppedGradient(colorStops, stepNumber); + let steppedGradient = ''; + const percentage = + (endingPercentage - trailingPercentage) / steppedColors.length; + let percentageSteps = + (endingPercentage - trailingPercentage) / steppedColors.length + + trailingPercentage; + steppedColors.forEach((color) => { + steppedGradient = steppedGradient.concat( + `${color} ${percentageSteps - percentage}% ${percentageSteps}%, ` + ); + percentageSteps = percentageSteps + percentage; + }); + steppedGradient = steppedGradient.substring(0, steppedGradient.length - 2); + gradient = `linear-gradient(to right, currentColor ${trailingPercentage}%, ${steppedGradient})`; + } else { + const linearGradient = sortedStops.map( + stopType === 'gradient' ? gradientStop : fixedStop + ); + gradient = `linear-gradient(to right,${linearGradient})`; + } return ( = ({ max={max || rangeMax} lowerValue={min || rangeMin} upperValue={max || rangeMax} - background={background} + background={gradient} compressed={compressed} />
{ + if (a.stop < b.stop) { + return -1; + } + if (a.stop > b.stop) { + return 1; + } + return 0; + }); + return colorStops; }; export const addStop = ( @@ -125,7 +135,7 @@ export const getPositionFromStop = ( return parseFloat( ( ((stop - min) / (max - min)) * - calculateScale(ref ? ref.clientWidth : 100) + calculateScale(ref && ref.clientWidth > 0 ? ref.clientWidth : 100) ).toFixed(1) ); }; diff --git a/src/components/form/range/range_highlight.tsx b/src/components/form/range/range_highlight.tsx index ee67271f43f..adbc70e3512 100644 --- a/src/components/form/range/range_highlight.tsx +++ b/src/components/form/range/range_highlight.tsx @@ -49,6 +49,7 @@ export const EuiRangeHighlight: FunctionComponent = ({ // const rangeWidth = (value - min) / (max - min); const leftPosition = (lowerValue - min) / (max - min); const rangeWidth = (upperValue - lowerValue) / (max - min); + const rangeWidthStyle = { background, marginLeft: `${leftPosition * 100}%`, diff --git a/src/services/color/index.ts b/src/services/color/index.ts index 391d185bfab..f12247056fb 100644 --- a/src/services/color/index.ts +++ b/src/services/color/index.ts @@ -49,3 +49,4 @@ export { euiPaletteGray, } from './eui_palettes'; export { rgbDef, HSV, RGB } from './color_types'; +export { getSteppedGradient } from './stepped_gradient'; diff --git a/src/services/color/stepped_gradient.ts b/src/services/color/stepped_gradient.ts new file mode 100644 index 00000000000..0dbaa8ec996 --- /dev/null +++ b/src/services/color/stepped_gradient.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import chroma from 'chroma-js'; +import { ColorStop } from '../../components/color_picker/color_stops'; + +export const getSteppedGradient = function ( + colors: ColorStop[], + steps: number +) { + const range = colors[colors.length - 1].stop - colors[0].stop; + const offset = colors[0].stop; + const finalStops = [...colors.map((item) => (item.stop - offset) / range)]; + const color = [...colors.map((item) => item.color)]; + return chroma.scale(color).domain(finalStops).colors(steps); +}; diff --git a/src/services/index.ts b/src/services/index.ts index 51239389444..9b7e56f545d 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -70,6 +70,7 @@ export { euiPaletteWarm, euiPaletteGray, HSV, + getSteppedGradient, } from './color'; export { useColorPickerState, useColorStopsState } from './color_picker';