Skip to content

Commit

Permalink
EuiColorStops drag updates (#2557)
Browse files Browse the repository at this point in the history
* useMemo; fewer arrow function prop values

* hooks updates

* CL
  • Loading branch information
thompsongl authored Jan 27, 2020
1 parent ad1cd79 commit 60a3cbd
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- Fixed the display of `EuiButton` within `EuiControlBar` when `fill={true}` to be more consistent with other buttons ([#2781](https://github.com/elastic/eui/pull/2781))
- Fixed `EuiFormControlLayout` from overwriting className for `prepend` nodes. ([#2796](https://github.com/elastic/eui/pull/2796))
- Fixed `useRenderToText` and `EuiButtonToggle` from attempting state updates on unmounted components ([#2797](https://github.com/elastic/eui/pull/2797))
- Refactored function and hook instantiation to fix drag action sluggishness in `EuiColorStops` ([#2557](https://github.com/elastic/eui/pull/2557))

**Deprecations**

Expand Down
34 changes: 22 additions & 12 deletions src/components/color_picker/color_stops/color_stop_thumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,17 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
}
};

const setHasFocusTrue = () => setHasFocus(true);
const setHasFocusFalse = () => setHasFocus(false);

const handleColorChange = (value: ColorStop['color']) => {
setColorIsInvalid(isColorInvalid(value));
onChange({ stop, color: value });
};

const handleColorInputChange = (e: React.ChangeEvent<HTMLInputElement>) =>
handleColorChange(e.target.value);

const handleStopChange = (value: ColorStop['stop']) => {
const willBeInvalid = value > localMax || value < localMin;

Expand All @@ -140,7 +146,9 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
onChange({ stop: value, color });
};

const handleStopInputChange = (value: ColorStop['stop']) => {
const handleStopInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let value = parseFloat(e.target.value);

const willBeInvalid = value > globalMax || value < globalMin;

if (willBeInvalid) {
Expand Down Expand Up @@ -200,10 +208,14 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
openPopover();
};

const handleTouchStart = (e: React.TouchEvent<HTMLButtonElement>) => {
const handleTouchInteraction = (e: React.TouchEvent<HTMLButtonElement>) => {
if (!readOnly) {
handleInteraction(e);
}
};

const handleTouchStart = (e: React.TouchEvent<HTMLButtonElement>) => {
handleTouchInteraction(e);
openPopover();
};

Expand All @@ -215,6 +227,8 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
className
);

// console.log('render', stop);

return (
<EuiPopover
ref={popoverRef}
Expand Down Expand Up @@ -252,13 +266,13 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
max={localMax}
value={stop}
onFocus={handleFocus}
onBlur={() => setHasFocus(false)}
onMouseOver={() => setHasFocus(true)}
onMouseOut={() => setHasFocus(false)}
onBlur={setHasFocusFalse}
onMouseOver={setHasFocusTrue}
onMouseOut={setHasFocusFalse}
onKeyDown={handleKeyDown}
onMouseDown={handleOnMouseDown}
onTouchStart={handleTouchStart}
onTouchMove={readOnly ? undefined : handleInteraction}
onTouchMove={handleTouchInteraction}
aria-valuetext={ariaValueText}
aria-label={ariaLabel}
title={title}
Expand Down Expand Up @@ -307,9 +321,7 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
max={isRangeMax || max == null ? null : localMax}
value={isStopInvalid(stop) ? '' : stop}
isInvalid={stopIsInvalid}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleStopInputChange(parseFloat(e.target.value))
}
onChange={handleStopInputChange}
/>
</EuiFormRow>
)}
Expand Down Expand Up @@ -370,9 +382,7 @@ export const EuiColorStopThumb: FunctionComponent<EuiColorStopThumbProps> = ({
readOnly={readOnly}
value={color}
isInvalid={colorIsInvalid}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleColorChange(e.target.value)
}
onChange={handleColorInputChange}
/>
</EuiFormRow>
)}
Expand Down
166 changes: 103 additions & 63 deletions src/components/color_picker/color_stops/color_stops.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,12 @@ export const EuiColorStops: FunctionComponent<EuiColorStopsProps> = ({
);
};

const handleOnChange = (colorStops: ColorStop[]) => {
onChange(colorStops, isInvalid(colorStops));
};

const handleStopChange = (stop: ColorStop, id: number) => {
const newColorStops = [...colorStops];
newColorStops.splice(id, 1, stop);
handleOnChange(newColorStops);
};
const handleOnChange = useCallback(
(colorStops: ColorStop[]) => {
onChange(colorStops, isInvalid(colorStops));
},
[onChange]
);

const onFocusStop = useCallback(
(index: number) => {
Expand Down Expand Up @@ -207,11 +204,21 @@ export const EuiColorStops: FunctionComponent<EuiColorStopsProps> = ({
}
}, [sortedStops, onFocusStop, setFocusStopOnUpdate, focusStopOnUpdate]);

const onFocusWrapper = () => {
const onFocusWrapper = useCallback(() => {
setFocusedStopIndex(null);
if (wrapperRef) {
wrapperRef.focus();
}
}, [wrapperRef]);

const setWrapperHasFocus = (e: React.FocusEvent) => {
if (e.target === wrapperRef) {
setHasFocus(true);
}
};

const removeWrapperFocus = () => {
setHasFocus(false);
};

const onAdd = () => {
Expand All @@ -227,11 +234,24 @@ export const EuiColorStops: FunctionComponent<EuiColorStopsProps> = ({
handleOnChange(newColorStops);
};

const onRemove = (index: number) => {
const newColorStops = removeStop(colorStops, index);
const onRemove = useCallback(
(index: number) => {
const newColorStops = removeStop(colorStops, index);

onFocusWrapper();
handleOnChange(newColorStops);
onFocusWrapper();
handleOnChange(newColorStops);
},
[colorStops, handleOnChange, onFocusWrapper]
);

const disableHover = () => {
if (disabled) return;
setIsHoverDisabled(true);
};

const enableHover = () => {
if (disabled) return;
setIsHoverDisabled(false);
};

const handleAddHover = (e: React.MouseEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -307,46 +327,70 @@ export const EuiColorStops: FunctionComponent<EuiColorStopsProps> = ({
}
};

const thumbs = sortedStops.map((colorStop, index) => (
<EuiColorStopThumb
isRangeMin={min == null && colorStop.stop === rangeMin}
isRangeMax={max == null && colorStop.stop === rangeMax}
data-index={`${STOP_ATTR}${index}`}
key={colorStop.id}
globalMin={min || rangeMin}
globalMax={max || rangeMax}
min={min}
max={max}
localMin={index === 0 ? min || rangeMin : sortedStops[index - 1].stop + 1}
localMax={
index === sortedStops.length - 1
? max || rangeMax
: sortedStops[index + 1].stop - 1
}
stop={colorStop.stop}
color={colorStop.color}
onRemove={
sortedStops.length > 1 ? () => onRemove(colorStop.id) : undefined
}
onChange={stop => handleStopChange(stop, colorStop.id)}
onFocus={() => setFocusedStopIndex(index)}
parentRef={wrapperRef}
colorPickerMode={mode}
colorPickerSwatches={swatches}
disabled={disabled}
readOnly={readOnly}
aria-valuetext={`Stop: ${colorStop.stop}, Color: ${
colorStop.color
} (${index + 1} of ${colorStops.length})`}
isPopoverOpen={colorStop.id === openedStopId}
openPopover={() => {
setOpenedStopId(colorStop.id);
}}
closePopover={() => {
setOpenedStopId(null);
}}
/>
));
const thumbs = useMemo(() => {
const handleStopChange = (stop: ColorStop, id: number) => {
const newColorStops = [...colorStops];
newColorStops.splice(id, 1, stop);
handleOnChange(newColorStops);
};
return sortedStops.map((colorStop, index) => (
<EuiColorStopThumb
isRangeMin={min == null && colorStop.stop === rangeMin}
isRangeMax={max == null && colorStop.stop === rangeMax}
data-index={`${STOP_ATTR}${index}`}
key={colorStop.id}
globalMin={min || rangeMin}
globalMax={max || rangeMax}
min={min}
max={max}
localMin={
index === 0 ? min || rangeMin : sortedStops[index - 1].stop + 1
}
localMax={
index === sortedStops.length - 1
? max || rangeMax
: sortedStops[index + 1].stop - 1
}
stop={colorStop.stop}
color={colorStop.color}
onRemove={
sortedStops.length > 1 ? () => onRemove(colorStop.id) : undefined
}
onChange={stop => handleStopChange(stop, colorStop.id)}
onFocus={() => setFocusedStopIndex(index)}
parentRef={wrapperRef}
colorPickerMode={mode}
colorPickerSwatches={swatches}
disabled={disabled}
readOnly={readOnly}
aria-valuetext={`Stop: ${colorStop.stop}, Color: ${
colorStop.color
} (${index + 1} of ${colorStops.length})`}
isPopoverOpen={colorStop.id === openedStopId}
openPopover={() => {
setOpenedStopId(colorStop.id);
}}
closePopover={() => {
setOpenedStopId(null);
}}
/>
));
}, [
colorStops,
disabled,
handleOnChange,
max,
min,
mode,
onRemove,
openedStopId,
rangeMax,
rangeMin,
readOnly,
sortedStops,
swatches,
wrapperRef,
]);

const positions = wrapperRef
? sortedStops.map(({ stop }) => getPositionFromStopFn(stop))
Expand Down Expand Up @@ -381,16 +425,12 @@ export const EuiColorStops: FunctionComponent<EuiColorStopsProps> = ({
className={classes}
fullWidth={fullWidth}
tabIndex={disabled ? -1 : 0}
onMouseDown={() => !disabled && setIsHoverDisabled(true)}
onMouseUp={() => !disabled && setIsHoverDisabled(false)}
onMouseLeave={() => !disabled && setIsHoverDisabled(false)}
onMouseDown={disableHover}
onMouseUp={enableHover}
onMouseLeave={enableHover}
onKeyDown={handleKeyDown}
onFocus={e => {
if (e.target === wrapperRef) {
setHasFocus(true);
}
}}
onBlur={() => setHasFocus(false)}>
onFocus={setWrapperHasFocus}
onBlur={removeWrapperFocus}>
<EuiScreenReaderOnly>
<p aria-live="polite">
<EuiI18n
Expand Down

0 comments on commit 60a3cbd

Please sign in to comment.