Skip to content

Commit

Permalink
Feat: Percentage field color customization (#692)
Browse files Browse the repository at this point in the history
* feat(percentage-c11n): convert to table cell

* feat(percentage-c11n): add logic to default configs

* feat(percentage-c11n): add color picker to settings

* feat(percentage-c11n): change default colors

* feat(percentage-c11n): fix button text color

* feat(percentage-c11n): add labels to settings

* feat(percentage-c11n): add preview section

* feat(percentage-c11n): fix cache issues with debouncing

* feat(percentage-c11n): add width responsiveness to color picker

* feat(percentage-c11n): fix responsiveness issues

* feat(percentage-c11n): add checkbox, refactor a little

* feat(percentage-c11n): convert data type to array

* feat(percentage-c11n): refactor config states

* feat(percentage-c11n): fix defaults

* feat(percentage-c11n): add basic cell without bg

* feat(percentage-c11n): remove collapse

* feat(percentage-c11n): refactor checkStates

* feat(percentage-c11n): add grid layout

* feat(percentage-c11n): chore conventions

* feat(percentage-c11n): add default theme color to sidedrawer

* remove redundant fragment

Co-authored-by: Sidney Alcantara <sidney@sidney.me>

* fix text color in preview

Co-authored-by: Sidney Alcantara <sidney@sidney.me>

* fix: change state to derived state

Co-authored-by: Sidney Alcantara <sidney@sidney.me>

* fix: review suggestions

* fix: remove redundant change call

Co-authored-by: Sidney Alcantara <sidney@sidney.me>

* fix(percentage-c11n): remove redundant dependencies

Co-authored-by: Shams <shams.mosowi@gmail.com>
Co-authored-by: Sidney Alcantara <sidney@sidney.me>
  • Loading branch information
3 people authored Jul 5, 2022
1 parent 6db708a commit db7b3eb
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 49 deletions.
76 changes: 76 additions & 0 deletions src/components/ColorPickerInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useState, useRef, MutableRefObject, useLayoutEffect } from "react";
import { Box, useTheme } from "@mui/material";

import { Color, ColorPicker } from "react-color-palette";

const useResponsiveWidth = (): [
width: number,
setRef: MutableRefObject<HTMLElement | null>
] => {
const ref = useRef(null);
const [width, setWidth] = useState(0);

useLayoutEffect(() => {
if (!ref || !ref.current) {
return;
}
const resizeObserver = new ResizeObserver((targets) => {
const { width: currentWidth } = targets[0].contentRect;
setWidth(currentWidth);
});

resizeObserver.observe(ref.current);

return () => {
resizeObserver.disconnect();
};
}, []);

return [width, ref];
};

export interface IColorPickerProps {
value: Color;
onChangeComplete: (color: Color) => void;
disabled?: boolean;
}

export default function ColorPickerInput({
value,
onChangeComplete,
disabled = false,
}: IColorPickerProps) {
const [localValue, setLocalValue] = useState(value);
const [width, setRef] = useResponsiveWidth();
const theme = useTheme();

return (
<Box
ref={setRef}
sx={[
{
padding: theme.spacing(1.5),
paddingTop: theme.spacing(1),
transitionDuration: 0,
"& .rcp": {
border: "none",
"& .rcp-saturation": {
borderRadius: theme.spacing(0.5),
},
"& .rcp-body": {
boxSizing: "unset",
},
},
},
]}
>
<ColorPicker
width={width}
height={150}
color={localValue}
onChange={(color) => setLocalValue(color)}
onChangeComplete={onChangeComplete}
/>
</Box>
);
}
46 changes: 13 additions & 33 deletions src/components/fields/Percentage/BasicCell.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,21 @@
import { IBasicCellProps } from "@src/components/fields/types";

import { useTheme } from "@mui/material";
import { resultColorsScale } from "@src/utils/color";

export default function Percentage({ value }: IBasicCellProps) {
const theme = useTheme();

if (typeof value === "number")
return (
<>
<div
style={{
backgroundColor: resultColorsScale(value).toHex(),

position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
opacity: 0.5,

zIndex: 0,
}}
/>
<div
style={{
textAlign: "right",
color: theme.palette.text.primary,

position: "relative",
zIndex: 1,
}}
>
{Math.round(value * 100)}%
</div>
</>
);

return null;
const percentage = typeof value === "number" ? value : 0;
return (
<div
style={{
textAlign: "right",
color: theme.palette.text.primary,
position: "relative",
zIndex: 1,
}}
>
{Math.round(percentage * 100)}%
</div>
);
}
171 changes: 171 additions & 0 deletions src/components/fields/Percentage/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { useState } from "react";

import {
Box,
Checkbox,
Grid,
InputLabel,
MenuItem,
TextField,
Typography,
useTheme,
} from "@mui/material";
import ColorPickerInput from "@src/components/ColorPickerInput";
import { ISettingsProps } from "@src/components/fields/types";

import { Color, toColor } from "react-color-palette";
import { fieldSx } from "@src/components/SideDrawer/utils";
import { resultColorsScale, defaultColors } from "@src/utils/color";

const colorLabels: { [key: string]: string } = {
0: "Start",
1: "Middle",
2: "End",
};

export default function Settings({ onChange, config }: ISettingsProps) {
const colors: string[] = config.colors ?? defaultColors;

const [checkStates, setCheckStates] = useState<boolean[]>(
colors.map(Boolean)
);

const onCheckboxChange = (index: number, checked: boolean) => {
onChange("colors")(
colors.map((value: any, idx: number) =>
index === idx ? (checked ? value || defaultColors[idx] : null) : value
)
);
setCheckStates(
checkStates.map((value, idx) => (index === idx ? checked : value))
);
};

const handleColorChange = (index: number, color: Color): void => {
onChange("colors")(
colors.map((value, idx) => (index === idx ? color.hex : value))
);
};

return (
<>
<Grid container>
{checkStates.map((checked: boolean, index: number) => {
const colorHex = colors[index];
return (
<Grid
xs={12}
md={4}
item
sx={{
display: "flex",
alignItems: "end",
justifyContent: "center",
}}
>
<Checkbox
checked={checked}
sx={[
fieldSx,
{
width: "auto",
boxShadow: "none",
backgroundColor: "inherit",
"&:hover": {
backgroundColor: "inherit",
},
},
]}
onChange={() => onCheckboxChange(index, !checked)}
/>
<TextField
select
label={colorLabels[index]}
value={1}
fullWidth
disabled={!checkStates[index]}
>
<MenuItem value={1} sx={{ display: "none" }}>
{checked && (
<Box sx={{ display: "flex", alignItems: "center" }}>
<Box
sx={{
backgroundColor: colorHex,
width: 15,
height: 15,
mr: 1.5,
boxShadow: (theme) =>
`0 0 0 1px ${theme.palette.divider} inset`,
borderRadius: 0.5,
opacity: 0.5,
}}
/>
<Box>{colorHex}</Box>
</Box>
)}
</MenuItem>
{colorHex && (
<div>
<ColorPickerInput
value={toColor("hex", colorHex)}
onChangeComplete={(color) =>
handleColorChange(index, color)
}
disabled={!checkStates[index]}
/>
</div>
)}
</TextField>
</Grid>
);
})}
</Grid>
<Preview colors={config.colors} />
</>
);
}

const Preview = ({ colors }: { colors: any }) => {
const theme = useTheme();
return (
<InputLabel>
Preview:
<Box
sx={{
display: "flex",
textAlign: "center",
}}
>
{[0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1].map((value) => {
return (
<Box
sx={{
position: "relative",
width: "100%",
padding: "0.5rem 0",
color: theme.palette.text.primary,
}}
>
<Box
key={value}
sx={{
position: "absolute",
inset: 0,
backgroundColor: resultColorsScale(
value,
colors,
theme.palette.background.paper
).toHex(),
opacity: 0.5,
}}
/>
<Typography style={{ position: "relative", zIndex: 1 }}>
{Math.floor(value * 100)}%
</Typography>
</Box>
);
})}
</Box>
</InputLabel>
);
};
14 changes: 9 additions & 5 deletions src/components/fields/Percentage/SideDrawerField.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ISideDrawerFieldProps } from "@src/components/fields/types";

import { TextField, InputAdornment, Box } from "@mui/material";
import { emphasize } from "@mui/material/styles";
import { TextField, InputAdornment, Box, useTheme } from "@mui/material";
import { resultColorsScale } from "@src/utils/color";
import { getFieldId } from "@src/components/SideDrawer/utils";

Expand All @@ -12,6 +11,8 @@ export default function Percentage({
onSubmit,
disabled,
}: ISideDrawerFieldProps) {
const { colors } = (column as any).config;
const theme = useTheme();
return (
<TextField
variant="filled"
Expand All @@ -34,11 +35,14 @@ export default function Percentage({
width: 20,
height: 20,
borderRadius: 0.5,
boxShadow: (theme) =>
`0 0 0 1px ${theme.palette.divider} inest`,
boxShadow: `0 0 0 1px ${theme.palette.divider} inset`,
backgroundColor:
typeof value === "number"
? resultColorsScale(value).toHex() + "!important"
? resultColorsScale(
value,
colors,
theme.palette.background.paper
).toHex() + "!important"
: undefined,
}}
/>
Expand Down
41 changes: 41 additions & 0 deletions src/components/fields/Percentage/TableCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { IHeavyCellProps } from "@src/components/fields/types";

import { useTheme } from "@mui/material";
import { resultColorsScale } from "@src/utils/color";

export default function Percentage({ column, value }: IHeavyCellProps) {
const theme = useTheme();
const { colors } = (column as any).config;

const percentage = typeof value === "number" ? value : 0;
return (
<>
<div
style={{
backgroundColor: resultColorsScale(
percentage,
colors,
theme.palette.background.paper
).toHex(),
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
opacity: 0.5,
zIndex: 0,
}}
/>
<div
style={{
textAlign: "right",
color: theme.palette.text.primary,
position: "relative",
zIndex: 1,
}}
>
{Math.round(percentage * 100)}%
</div>
</>
);
}
Loading

0 comments on commit db7b3eb

Please sign in to comment.