Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
arcatdmz committed Jul 6, 2020
1 parent ba7ce41 commit 400ec81
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 137 deletions.
1 change: 1 addition & 0 deletions docs/components/Body.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FC, useState } from "react";

import { ColorPanel } from "./ColorPanel";

export const Body: FC = () => {
Expand Down
131 changes: 18 additions & 113 deletions docs/components/ColorPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import React, {
FC,
useState,
useCallback,
useRef,
useMemo,
useEffect,
} from "react";
import React, { FC, useState, useCallback, useRef, useEffect } from "react";
import styled from "styled-components";
import tinycolor from "tinycolor2";

import { HueSlider } from "./HueSlider";
import { BrightnessSaturationPanel } from "./BrightnessSaturationPanel";
import { Size } from "./utils";
import { SaturationBrightnessPanel } from "./SaturationBrightnessPanel";

const Panel = styled.div`
margin-bottom: 5px;
Expand All @@ -23,107 +15,22 @@ export interface ColorPanelProps {
onColorUpdate?(color: tinycolor.Instance): void;
}

interface CursorPosition {
x: number;
y: number;
}

export const ColorPanel: FC<ColorPanelProps> = ({ width, onColorUpdate }) => {
const ref = useRef<HTMLDivElement>(null);
const [hue, setHue] = useState<number>(0);
const [cursorPosition, setCursorPosition] = useState<CursorPosition>({
x: 0,
y: 0,
});
const [panelSize, setPanelSize] = useState<Size>(null);

const updateCursorPosition = useCallback(
({ x, y }: CursorPosition) => {
if (!ref.current) {
return;
}
const rect = ref.current.getBoundingClientRect();
setCursorPosition({
x: x - rect.left,
y: y - rect.top,
});
},
[ref.current]
);

const handleMouseMove = useCallback(
(ev: MouseEvent | React.MouseEvent) => {
const { buttons } = ev;
if (buttons !== 1) {
return;
}
updateCursorPosition({ x: ev.clientX, y: ev.clientY });
},
[updateCursorPosition]
);

const handleMouseDown = useCallback(
(ev: MouseEvent | React.MouseEvent) => {
handleMouseMove(ev);
const handleMouseUp = (ev: MouseEvent) => {
handleMouseMove(ev);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
},
[handleMouseMove]
);
const [saturation, setSaturation] = useState<number>(0);
const [brightness, setBrightness] = useState<number>(0);

const handleTouchMove = useCallback(
(ev: TouchEvent | React.TouchEvent) => {
const { targetTouches } = ev;
if (targetTouches.length <= 0) {
return;
}
ev.preventDefault();
updateCursorPosition({
x: targetTouches[0].clientX,
y: targetTouches[0].clientY,
});
},
[updateCursorPosition]
);

const handleTouchStart = useCallback(
(ev: TouchEvent | React.TouchEvent) => {
handleTouchMove(ev);
const handleTouchEnd = (ev: TouchEvent) => {
handleTouchMove(ev);
window.removeEventListener("touchmove", handleTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
};
window.addEventListener("touchmove", handleTouchMove, { passive: false });
window.addEventListener("touchend", handleTouchEnd);
},
[handleTouchMove]
);

const handleHueChange = useCallback((h) => {
const handleHueUpdate = useCallback((h: number) => {
setHue(h);
}, []);

const brightness = useMemo(
() =>
Math.max(
0,
Math.min(1, 1 - (panelSize ? cursorPosition.y / panelSize.height : 0))
),
[panelSize, cursorPosition]
);
const saturation = useMemo(
() =>
Math.max(
0,
Math.min(1, panelSize ? cursorPosition.x / panelSize.width : 0)
),
[panelSize, cursorPosition]
const handleSaturationBrightnessUpdate = useCallback(
(s: number, b: number) => {
setSaturation(s);
setBrightness(b);
},
[]
);

useEffect(() => {
Expand All @@ -133,7 +40,7 @@ export const ColorPanel: FC<ColorPanelProps> = ({ width, onColorUpdate }) => {
v: brightness,
});
onColorUpdate && onColorUpdate(color);
}, [brightness, saturation, hue, onColorUpdate]);
}, [hue, saturation, brightness, onColorUpdate]);

return (
<div
Expand All @@ -143,17 +50,15 @@ export const ColorPanel: FC<ColorPanelProps> = ({ width, onColorUpdate }) => {
}}
ref={ref}
>
<Panel
onMouseDown={handleMouseDown}
onTouchStart={handleTouchStart}>
<BrightnessSaturationPanel
brightness={brightness}
saturation={saturation}
<Panel>
<SaturationBrightnessPanel
hue={hue}
onSizeUpdate={setPanelSize}
saturation={saturation}
brightness={brightness}
onUpdate={handleSaturationBrightnessUpdate}
/>
</Panel>
<HueSlider hue={hue} onHueChange={handleHueChange} />
<HueSlider hue={hue} onHueChange={handleHueUpdate} />
</div>
);
};
120 changes: 120 additions & 0 deletions docs/components/SaturationBrightnessEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
useCallback,
useState,
useMemo,
MouseEventHandler,
TouchEventHandler,
} from "react";

import { Size } from "./utils";

interface EventHandlers<E extends HTMLElement> {
onMouseDown: MouseEventHandler<E>;
onTouchStart: TouchEventHandler<E>;
}

interface CursorPosition {
x: number;
y: number;
}

export function useSaturationBrightnessEventHandler<E extends HTMLElement>(
el: E,
elSize: Size
): [number, number, EventHandlers<E>] {
const [cursorPosition, setCursorPosition] = useState<CursorPosition>({
x: 0,
y: 0,
});

const updateCursorPosition = useCallback(
({ x, y }: CursorPosition) => {
if (!el) {
return;
}
const rect = el.getBoundingClientRect();
setCursorPosition({
x: x - rect.left,
y: y - rect.top,
});
},
[el]
);

const handleMouseMove = useCallback(
(ev: MouseEvent | React.MouseEvent) => {
const { buttons } = ev;
if (buttons !== 1) {
return;
}
updateCursorPosition({ x: ev.clientX, y: ev.clientY });
},
[updateCursorPosition]
);

const handleMouseDown = useCallback(
(ev: MouseEvent | React.MouseEvent) => {
handleMouseMove(ev);
const handleMouseUp = (ev: MouseEvent) => {
handleMouseMove(ev);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
},
[handleMouseMove]
);

const handleTouchMove = useCallback(
(ev: TouchEvent | React.TouchEvent) => {
const { targetTouches } = ev;
if (targetTouches.length <= 0) {
return;
}
ev.preventDefault();
updateCursorPosition({
x: targetTouches[0].clientX,
y: targetTouches[0].clientY,
});
},
[updateCursorPosition]
);

const handleTouchStart = useCallback(
(ev: TouchEvent | React.TouchEvent) => {
handleTouchMove(ev);
const handleTouchEnd = (ev: TouchEvent) => {
handleTouchMove(ev);
window.removeEventListener("touchmove", handleTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
};
window.addEventListener("touchmove", handleTouchMove, { passive: false });
window.addEventListener("touchend", handleTouchEnd);
},
[handleTouchMove]
);

const saturation = useMemo(
() =>
Math.max(0, Math.min(1, elSize ? cursorPosition.x / elSize.width : 0)),
[elSize, cursorPosition]
);
const brightness = useMemo(
() =>
Math.max(
0,
Math.min(1, 1 - (elSize ? cursorPosition.y / elSize.height : 0))
),
[elSize, cursorPosition]
);

return [
saturation,
brightness,
{
onMouseDown: handleMouseDown,
onTouchStart: handleTouchStart,
},
];
}
Original file line number Diff line number Diff line change
@@ -1,54 +1,62 @@
import { FC, MutableRefObject, useEffect, useState, useMemo } from "react";
import { FC, useEffect, useState, useMemo } from "react";
import styled from "styled-components";
import tinycolor from "tinycolor2";

import { useResize, Size } from "./utils";
import { Cursor } from "./Cursor";
import { useSaturationBrightnessEventHandler } from "./SaturationBrightnessEventHandler";

const Panel = styled.div`
position: relative;
width: 100%;
line-height: 0;
z-index: 1;
& > .brightness,
& > .saturation {
& > .saturation,
& > .brightness {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
& > .brightness {
z-index: 3;
background-image: linear-gradient(to bottom, transparent 0%, #000000 100%);
}
& > .saturation {
z-index: 2;
background-image: linear-gradient(to right, #ffffff 0%, transparent 100%);
}
& > .brightness {
z-index: 3;
background-image: linear-gradient(to bottom, transparent 0%, #000000 100%);
}
`;

interface BrightnessSaturationPanelProps {
brightness: number;
saturation: number;
interface SaturationBrightnessPanelProps {
hue: number;
onSizeUpdate?(size: Size): void;
saturation: number;
brightness: number;
onUpdate?(saturation: number, brightness: number): void;
}

export const BrightnessSaturationPanel: FC<BrightnessSaturationPanelProps> = ({
export const SaturationBrightnessPanel: FC<SaturationBrightnessPanelProps> = ({
brightness,
saturation,
hue,
onSizeUpdate,
onUpdate,
}) => {
const [size, ref] = useResize();
const [height, setHeight] = useState<string>("0px");
const [measuredSize, ref] = useResize<HTMLDivElement>();
const [size, setSize] = useState<Size>({
width: 0,
height: 0,
});
const [s, b, props] = useSaturationBrightnessEventHandler(ref.current, size);

useEffect(() => {
setHeight(`${size.width}px`);
onSizeUpdate && onSizeUpdate({ ...size, height: size.width });
}, [size, onSizeUpdate]);
setSize({ width: measuredSize.width, height: measuredSize.width });
}, [measuredSize, ref.current]);

useEffect(() => {
onUpdate && onUpdate(s, b);
}, [onUpdate, s, b]);

const hueColor = useMemo(
() =>
Expand All @@ -65,12 +73,13 @@ export const BrightnessSaturationPanel: FC<BrightnessSaturationPanelProps> = ({
return (
<Panel
className="panel"
style={{ height: height, backgroundColor: hueColor }}
ref={ref as MutableRefObject<HTMLDivElement>}
style={{ height: `${size.height}px`, backgroundColor: hueColor }}
ref={ref}
{...props}
>
<Cursor x={saturation} y={1 - brightness} />
<div className="brightness"></div>
<div className="saturation"></div>
<div className="brightness"></div>
</Panel>
);
};
Loading

0 comments on commit 400ec81

Please sign in to comment.