Skip to content

Commit

Permalink
feat: add ColorPickerDropdown to the application (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastien-Ahkrin committed Nov 21, 2022
1 parent 6158c23 commit c9a29dd
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 46 deletions.
36 changes: 18 additions & 18 deletions src/app/panels/measurement-config/MeasurementConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
useAppDispatch,
useAppState,
} from '../../../app-data/index';
import { ColorPicker } from '../../../components/index';
import { ColorPickerDropdown } from '../../../components/index';

export function MeasurementConfigPanel() {
const dispatch = useAppDispatch();
Expand All @@ -20,25 +20,25 @@ export function MeasurementConfigPanel() {
return (
<div style={{ display: 'flex', padding: 8 }}>
<div style={{ flex: '1 1 0' }}>Stroke color</div>
<ColorPicker
color={{
hex: color.color,
}}
onChangeComplete={({ hex }) => {
dispatch({
type: 'CHANGE_MEASUREMENT_DISPLAY',
payload: {
measurement: measurement.kindAndId,
display: {
color: {
kind: 'fixed',
color: hex,
<div style={{ flex: '1 1 0' }}>
<ColorPickerDropdown
color={{ hex: color.color }}
onChangeComplete={({ hex }) => {
dispatch({
type: 'CHANGE_MEASUREMENT_DISPLAY',
payload: {
measurement: measurement.kindAndId,
display: {
color: {
kind: 'fixed',
color: hex,
},
},
},
},
});
}}
/>
});
}}
/>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import { useRef } from 'react';

import { useModifiedPopper } from '../../hooks/useModifiedPopper';
import { useOnClickOutside } from '../../hooks/useOnClickOutside';
import { useOnOff } from '../../hooks/useOnOff';
import { ColorPicker, ColorPickerProps } from '../react-color/ColorPicker';
import * as colorHelper from '../react-color/helpers/color';

type ColorPickerDopdpownProps = Pick<
ColorPickerProps,
'color' | 'presetColors' | 'disableAlpha' | 'onChange' | 'onChangeComplete'
>;

const colorPickerDropdownCss = {
root: css`
position: relative;
width: 100%;
border: 1px solid darkgray;
border-radius: 0.25rem;
height: 30px;
padding: 5px;
`,
preview: css`
height: 100%;
width: 100%;
border-radius: 0.125rem;
`,
};

export function ColorPickerDropdown(props: ColorPickerDopdpownProps) {
const { color, ...otherProps } = props;

const ref = useRef<HTMLDivElement>(null);
const [isOpened, , closeMenu, toggleMenu] = useOnOff(false);

const { hex } = colorHelper.toState(color || 'white');

const { setReferenceElement, setPopperElement, popperProps } =
useModifiedPopper({ placement: 'bottom-start', offset: 6 });

useOnClickOutside(ref, closeMenu);

return (
<div>
<div
ref={setReferenceElement}
css={colorPickerDropdownCss.root}
onClick={toggleMenu}
>
<div
css={colorPickerDropdownCss.preview}
style={{ backgroundColor: hex }}
/>
</div>
{isOpened && (
<div ref={setPopperElement} {...popperProps}>
<div ref={ref}>
<ColorPicker color={color} {...otherProps} />
</div>
</div>
)}
</div>
);
}
26 changes: 26 additions & 0 deletions src/components/color-picker/gradient-select/GradientPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from '@emotion/styled';
import { useMemo } from 'react';

interface GradientPreviewProps {
scale: (t: number) => string;
}

const GradientPreviewElement = styled.div`
height: 100%;
width: 100%;
border-radius: 0.125rem;
`;

export default function GradientPreview(props: GradientPreviewProps) {
const { scale } = props;

const gradient = useMemo(() => {
const stops: string[] = [];
for (let i = 0; i <= 100; i++) {
stops.push(scale(i / 100));
}
return `linear-gradient(to right, ${stops.join(', ')})`;
}, [scale]);

return <GradientPreviewElement style={{ background: gradient }} />;
}
26 changes: 3 additions & 23 deletions src/components/color-picker/gradient-select/GradientSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import styled from '@emotion/styled';
import { Listbox } from '@headlessui/react';
import * as scaleChromatic from 'd3-scale-chromatic';
import { Fragment, useMemo } from 'react';
import { Fragment } from 'react';
import { FaChevronDown } from 'react-icons/fa';

import GradientPreview from './GradientPreview';

const scales = {
turbo: scaleChromatic.interpolateTurbo,
viridis: scaleChromatic.interpolateViridis,
Expand Down Expand Up @@ -56,12 +58,6 @@ const GradientSelectOption = styled.li<{ active: boolean }>`
${(props) => props.active && 'font-weight: bold;'}
`;

const GradientPreviewElement = styled.div`
height: 100%;
width: 100%;
border-radius: 0.125rem;
`;

export interface GradientSelectProps {
value: GradientScaleName;
onChange: (value: GradientScaleName) => void;
Expand Down Expand Up @@ -94,19 +90,3 @@ export function GradientSelect(props: GradientSelectProps) {
</Listbox>
);
}

interface GradientPreviewProps {
scale: (t: number) => string;
}

function GradientPreview(props: GradientPreviewProps) {
const { scale } = props;
const gradient = useMemo(() => {
const stops: string[] = [];
for (let i = 0; i <= 100; i++) {
stops.push(scale(i / 100));
}
return `linear-gradient(to right, ${stops.join(', ')})`;
}, [scale]);
return <GradientPreviewElement style={{ background: gradient }} />;
}
1 change: 1 addition & 0 deletions src/components/color-picker/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './color-picker-dropdown/ColorPickerDropdown';
export * from './gradient-select/GradientSelect';
export * from './react-color/ColorPicker';
14 changes: 9 additions & 5 deletions src/components/hooks/useOnClickOutside.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { useEffect, RefObject } from 'react';

import { useRootLayoutContext } from '../root-layout/RootLayoutContext';

export function useOnClickOutside<T extends Node = Node>(
ref: RefObject<T>,
handler: (event: Event) => void,
) {
const shadowElement = useRootLayoutContext();

useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const listener = (event: any) => {
Expand All @@ -15,12 +19,12 @@ export function useOnClickOutside<T extends Node = Node>(
handler(event);
};

document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
shadowElement.addEventListener('mousedown', listener);
shadowElement.addEventListener('touchstart', listener);

return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
shadowElement.removeEventListener('mousedown', listener);
shadowElement.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}, [ref, handler, shadowElement]);
}
27 changes: 27 additions & 0 deletions stories/components/color-picker-dropdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useState } from 'react';

import { ColorPickerDropdown } from '../../src/components/index';

export default {
title: 'Components / Color Pickers Dropdown',
};

export function ColorPickerDropdownStory() {
const [color, setColor] = useState<string>('#BD6565');

return (
<div>
<p>Hex color: {color}</p>
<div style={{ width: 50 }}>
<ColorPickerDropdown
color={{ hex: color }}
onChange={(element) => {
setColor(element.hex);
}}
/>
</div>
</div>
);
}

ColorPickerDropdownStory.storyName = 'Color Picker Dropdown';

0 comments on commit c9a29dd

Please sign in to comment.