Migrate the color picker and color selector off Emotion (#83480)
* Migrate the color picker and color selector off Emotion * Move the color picker styles into its classes
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* react-color injects these gradients through <style> elements without a nonce, which our CSP blocks, so they are copied here.
|
||||
* https://github.com/casesandberg/react-color/blob/v2.18.1/src/components/common/Hue.js#L78
|
||||
* https://github.com/casesandberg/react-color/blob/v2.18.1/src/components/common/Saturation.js#L94
|
||||
*/
|
||||
.controls :global(.hue-horizontal) {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#f00 0%,
|
||||
#ff0 17%,
|
||||
#0f0 33%,
|
||||
#0ff 50%,
|
||||
#00f 67%,
|
||||
#f0f 83%,
|
||||
#f00 100%
|
||||
);
|
||||
}
|
||||
|
||||
.controls :global(.saturation-white) {
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.controls :global(.saturation-black) {
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.track {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
border: 0.125rem solid var(--mb-color-background_page-primary);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.saturationPointer {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.huePointer {
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
transform: translate(-50%, -0.0625rem);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import styled from "@emotion/styled";
|
||||
|
||||
export const ContentContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
width: 16.5rem;
|
||||
padding: 1rem;
|
||||
`;
|
||||
|
||||
// react-css inserts inline styles, but it's forbidden with our CSP headers
|
||||
// to fix this we copy relevant styles here
|
||||
// https://github.com/casesandberg/react-color/blob/v2.18.1/src/components/common/Hue.js#L78
|
||||
// https://github.com/casesandberg/react-color/blob/v2.18.1/src/components/common/Saturation.js#L94
|
||||
// eslint-disable-next-line metabase/no-color-literals
|
||||
export const ControlsRoot = styled.div`
|
||||
.hue-horizontal {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#f00 0%,
|
||||
#ff0 17%,
|
||||
#0f0 33%,
|
||||
#0ff 50%,
|
||||
#00f 67%,
|
||||
#f0f 83%,
|
||||
#f00 100%
|
||||
);
|
||||
}
|
||||
|
||||
.saturation-white {
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.saturation-black {
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
`;
|
||||
|
||||
export const SaturationContainer = styled.div`
|
||||
position: relative;
|
||||
height: 10rem;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const HueContainer = styled.div`
|
||||
position: relative;
|
||||
height: 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const ControlsPointer = styled.div`
|
||||
border: 2px solid var(--mb-color-background_page-primary);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
export const SaturationPointer = styled(ControlsPointer)`
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
transform: translate(-50%, -50%);
|
||||
`;
|
||||
|
||||
export const HuePointer = styled(ControlsPointer)`
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
transform: translate(-50%, -0.0625rem);
|
||||
`;
|
||||
@@ -5,9 +5,8 @@ import type { ColorState } from "react-color";
|
||||
import { t } from "ttag";
|
||||
|
||||
import { ColorInput } from "metabase/common/components/ColorInput";
|
||||
import { Group, NumberInput } from "metabase/ui";
|
||||
import { Group, NumberInput, Stack } from "metabase/ui";
|
||||
|
||||
import { ContentContainer } from "./ColorPicker.styled";
|
||||
import { ColorPickerControls } from "./ColorPickerControls";
|
||||
|
||||
export type ColorPickerContentAttributes = Omit<
|
||||
@@ -64,7 +63,7 @@ export const ColorPickerContent = forwardRef(function ColorPickerContent(
|
||||
);
|
||||
|
||||
return (
|
||||
<ContentContainer {...props} ref={ref}>
|
||||
<Stack {...props} ref={ref} gap="lg" w="16.5rem" p="lg">
|
||||
<ColorPickerControls color={value} onChange={handlePickerChange} />
|
||||
{showAlpha ? (
|
||||
<Group gap="sm" wrap="nowrap" align="flex-start">
|
||||
@@ -83,7 +82,7 @@ export const ColorPickerContent = forwardRef(function ColorPickerContent(
|
||||
) : (
|
||||
<ColorInput value={value} fullWidth onChange={onChange} />
|
||||
)}
|
||||
</ContentContainer>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import cx from "classnames";
|
||||
import type { CustomPickerInjectedProps } from "react-color";
|
||||
import { CustomPicker } from "react-color";
|
||||
import { Hue, Saturation } from "react-color/lib/components/common";
|
||||
|
||||
import {
|
||||
ControlsRoot,
|
||||
HueContainer,
|
||||
HuePointer,
|
||||
SaturationContainer,
|
||||
SaturationPointer,
|
||||
} from "./ColorPicker.styled";
|
||||
import { Box } from "metabase/ui";
|
||||
|
||||
import S from "./ColorPicker.module.css";
|
||||
|
||||
const saturationStyles = {
|
||||
color: {
|
||||
@@ -21,17 +18,25 @@ export const ColorPickerControls = CustomPicker(function ColorControls(
|
||||
props: CustomPickerInjectedProps,
|
||||
) {
|
||||
return (
|
||||
<ControlsRoot>
|
||||
<SaturationContainer>
|
||||
<Box className={S.controls}>
|
||||
<Box className={S.track} h="10rem" mb="lg">
|
||||
<Saturation
|
||||
{...props}
|
||||
pointer={SaturationPointer}
|
||||
style={saturationStyles}
|
||||
/>
|
||||
</SaturationContainer>
|
||||
<HueContainer>
|
||||
</Box>
|
||||
<Box className={S.track} h="0.5rem">
|
||||
<Hue {...props} pointer={HuePointer} />
|
||||
</HueContainer>
|
||||
</ControlsRoot>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
function SaturationPointer() {
|
||||
return <Box className={cx(S.pointer, S.saturationPointer)} />;
|
||||
}
|
||||
|
||||
function HuePointer() {
|
||||
return <Box className={cx(S.pointer, S.huePointer)} />;
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import styled from "@emotion/styled";
|
||||
|
||||
export const PopoverRoot = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
padding: 0.75rem;
|
||||
max-width: 22rem;
|
||||
`;
|
||||
@@ -2,8 +2,7 @@ import type { HTMLAttributes, Ref } from "react";
|
||||
import { forwardRef, useCallback } from "react";
|
||||
|
||||
import { ColorPill } from "metabase/common/components/ColorPill";
|
||||
|
||||
import { PopoverRoot } from "./ColorSelectorPopover.styled";
|
||||
import { Flex } from "metabase/ui";
|
||||
|
||||
/**
|
||||
* A picker given named palette colors reports which one was chosen, so the
|
||||
@@ -44,7 +43,7 @@ export const ColorSelectorPopover = forwardRef(function ColorSelector(
|
||||
);
|
||||
|
||||
return (
|
||||
<PopoverRoot {...props} ref={ref}>
|
||||
<Flex {...props} ref={ref} wrap="wrap" gap="xxs" p="md" maw="22rem">
|
||||
{colors.map(toNamedColor).map((option, index) => (
|
||||
<ColorPill
|
||||
key={index}
|
||||
@@ -53,6 +52,6 @@ export const ColorSelectorPopover = forwardRef(function ColorSelector(
|
||||
onSelect={(newValue) => handleSelect(newValue, option.name)}
|
||||
/>
|
||||
))}
|
||||
</PopoverRoot>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user