Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New design for color scale palette picking for maps #294

Merged
merged 21 commits into from
Jan 27, 2022
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d3ce6fb
refactor: Improve map color scale types
bprusinowski Jan 25, 2022
b86b22b
feat: Add radio + select elements for color scale types for maps
bprusinowski Jan 25, 2022
aaa0d09
feat: Add types for color palettes for maps
bprusinowski Jan 26, 2022
5078e34
refactor: Use proper color palette naming conventions
bprusinowski Jan 26, 2022
afbbc9a
feat: Add ColorRampField
bprusinowski Jan 26, 2022
8ddf7d1
chore: Remove redundant ColorRamp
bprusinowski Jan 26, 2022
fa066c8
feat: Use ColorRampField in map chart options
bprusinowski Jan 26, 2022
c15b844
feat: Add translations for divergent and sequential color palettes
bprusinowski Jan 26, 2022
1cdae16
feat: Add color types to tables
bprusinowski Jan 26, 2022
c338eee
fix: Set correct nbClass type to chart state
bprusinowski Jan 26, 2022
48d1724
feat: Do not cut color domain range on nbClass change
bprusinowski Jan 26, 2022
20d3349
fix: Retrieve number of geoShapes only if geoDimension if here
bprusinowski Jan 27, 2022
05e1824
feat: Add disabled prop to ColorRamp
bprusinowski Jan 27, 2022
36627fa
feat: Use translations for discrete color scale names
bprusinowski Jan 27, 2022
64809ca
refactor: Add more types for colors
bprusinowski Jan 27, 2022
8082a27
refactor: Move default palette in ColorRamp to useMemo
bprusinowski Jan 27, 2022
832090d
refactor: Small improvements to ColorRampField
bprusinowski Jan 27, 2022
108b56c
feat: Add ColorRamp docs
bprusinowski Jan 27, 2022
c419d6e
refactor: Move selectColorPicker style to theme object
bprusinowski Jan 27, 2022
b070e07
feat: Add types to field and path in ColorRampField
bprusinowski Jan 27, 2022
c60d46e
Merge branch 'main' of github.com:visualize-admin/visualization-tool …
bprusinowski Jan 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 193 additions & 18 deletions app/configurator/components/chart-controls/color-ramp.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,209 @@
import { useEffect, useRef } from "react";
import { interpolateOranges } from "d3";
import { Trans } from "@lingui/macro";
import { useSelect } from "downshift";
import { get } from "lodash";
import { useEffect, useMemo, useRef } from "react";
import { Box, Button, Text } from "theme-ui";
import {
DivergingPaletteType,
SequentialPaletteType,
useConfiguratorState,
} from "../..";
import { Label } from "../../../components/form";
import { Icon } from "../../../icons";
import { divergingPalettes, sequentialPalettes } from "../ui-helpers";
// Adapted from https://observablehq.com/@mbostock/color-ramp
export const ColorRamp = ({
colorInterpolator = interpolateOranges,
nbClass = 512,
width = 288,
height = 40,
}: {

type ColorRampProps = {
colorInterpolator: (t: number) => string;
nbClass: number;
nbClass?: number;
width?: number;
height?: number;
}) => {
};

export const ColorRamp = ({
colorInterpolator,
nbClass = 512,
width = 148,
height = 28,
}: ColorRampProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
// Clear by returning a function from usEEFFECT?
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
const canvas = canvasRef.current;
const context = canvas && canvas.getContext("2d");

if (canvas && context) {
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
context.clearRect(0, 0, width, height);
canvas.style.imageRendering = "-moz-crisp-edges";
canvas.style.imageRendering = "pixelated";
context.clearRect(0, 0, width, height);
for (let i = 0; i < nbClass; ++i) {
context.fillStyle = colorInterpolator(i / (nbClass - 1));
context.fillRect((width / nbClass) * i, 0, width / nbClass, height);
canvas.style.borderRadius = "2px";

const [widthPerClass, numberOfSteps] =
nbClass > width ? [1, width] : [width / nbClass, nbClass];

for (let i = 0; i < numberOfSteps; ++i) {
context.fillStyle = colorInterpolator(i / (numberOfSteps - 1));
context.fillRect(widthPerClass * i, 0, widthPerClass, height);
}
}
}, [colorInterpolator, height, width, nbClass]);

return <canvas ref={canvasRef} width={width} height={height} />;
};

type ColorRampFieldProps = Omit<ColorRampProps, "colorInterpolator"> & {
field: string;
path: string;
};

export const ColorRampField = ({
field,
path,
nbClass,
}: ColorRampFieldProps) => {
const [state, dispatch] = useConfiguratorState();

const palettes = useMemo(
() => [...divergingPalettes, ...sequentialPalettes],
[]
);
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved

const currentPaletteName = get(
state,
`chartConfig.fields.${field}.${path}`
) as DivergingPaletteType | SequentialPaletteType;
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved

const currentPalette =
palettes.find((d) => d.value === currentPaletteName) || palettes[0];

const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
highlightedIndex,
getItemProps,
} = useSelect({
items: palettes,
defaultSelectedItem: palettes.find((d) => d.value === "oranges"),
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
onSelectedItemChange: ({ selectedItem }) => {
if (selectedItem) {
dispatch({
type: "CHART_OPTION_CHANGED",
value: {
field,
path,
value: selectedItem.value,
},
});
}
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
},
});

return <canvas ref={canvasRef} />;
return (
<Box pb={2}>
<Label smaller {...getLabelProps()}>
<Trans id="controls.color.palette">Color palette</Trans>
</Label>
<Button
{...getToggleButtonProps()}
sx={{
color: "monochrome700",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
bg: "monochrome100",
p: 1,
height: "40px",
borderWidth: "1px",
borderStyle: "solid",
borderColor: "monochrome500",
":hover": {
bg: "monochrome100",
},
":active": {
bg: "monochrome100",
},
":disabled": {
cursor: "initial",
bg: "muted",
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
},
}}
>
{state.state === "CONFIGURING_CHART" && (
<>
<ColorRamp
colorInterpolator={currentPalette.interpolator}
nbClass={nbClass}
/>
<Icon name="unfold" />
</>
)}
</Button>
<Box {...getMenuProps()} sx={{ bg: "monochrome100" }}>
{isOpen && (
<>
<Text as="div" variant="meta" sx={{ p: 1 }}>
<Trans id="controls.color.palette.diverging">Diverging</Trans>
</Text>
{divergingPalettes.map((d, i) => (
<PaletteRamp
key={`diverging-${i}`}
palette={d}
backgroundColor={
i === highlightedIndex ? "monochrome200" : "monochrome100"
}
itemProps={getItemProps({ item: d, index: i })}
nbClass={nbClass}
/>
))}
<Text as="div" variant="meta" sx={{ p: 1 }}>
<Trans id="controls.color.palette.sequential">Sequential</Trans>
</Text>
{sequentialPalettes.map((d, i) => (
<PaletteRamp
key={`sequential-${i}`}
palette={d}
backgroundColor={
i + divergingPalettes.length === highlightedIndex
? "monochrome200"
: "monochrome100"
}
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
itemProps={getItemProps({
item: d,
index: i + divergingPalettes.length,
})}
nbClass={nbClass}
/>
))}
</>
)}
</Box>
</Box>
);
};

const PaletteRamp = (props: {
palette: {
label: string;
value: DivergingPaletteType | SequentialPaletteType;
interpolator: (t: number) => string;
};
backgroundColor: string;
itemProps: any;
nbClass?: number;
}) => {
const { palette, backgroundColor, nbClass, itemProps } = props;

return (
<Box sx={{ p: 1, cursor: "pointer", backgroundColor }}>
<Box sx={{ backgroundColor }} {...itemProps}>
<ColorRamp
key={`option-${palette.value}`}
colorInterpolator={palette.interpolator}
nbClass={nbClass}
/>
</Box>
</Box>
);
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
};