Skip to content

Commit

Permalink
feat: color picker (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuiiBz authored Mar 31, 2024
1 parent 0f1244b commit 4907506
Show file tree
Hide file tree
Showing 18 changed files with 173 additions and 194 deletions.
1 change: 1 addition & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"next-themes": "^0.3.0",
"oslo": "^1.1.3",
"react": "^18",
"react-colorful": "^5.6.1",
"react-dom": "^18",
"satori": "^0.10.11",
"sonner": "^1.3.1",
Expand Down
29 changes: 29 additions & 0 deletions apps/dashboard/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,32 @@ input[type=number]::-webkit-inner-spin-button {
height: 12px;
@apply bg-blue-500;
}

.react-colorful {
width: 100% !important;
}

.react-colorful__saturation {
border-radius: 3px 3px 0 0 !important;
}

.react-colorful__saturation-pointer {
width: 20px !important;
height: 20px !important;
border: 2px solid var(--gray-contrast) !important;
box-shadow: var(--shadow-4) !important;
}

.react-colorful__hue,
.react-colorful__alpha {
height: 20px !important;
border-radius: 0 0 3px 3px !important;
}

.react-colorful__hue-pointer,
.react-colorful__alpha-pointer {
width: 16px !important;
height: 16px !important;
border: 2px solid var(--gray-contrast) !important;
box-shadow: var(--shadow-4) !important;
}
42 changes: 42 additions & 0 deletions apps/dashboard/src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Button, Flex, Popover, TextField } from "@radix-ui/themes";
import { HexAlphaColorPicker } from "react-colorful";
import type { ReactNode } from "react";
import { ColorIcon } from "./icons/ColorIcon";

interface ColorPickerProps {
value: string;
onChange: (value: string) => void;
children?: ReactNode;
}

export function ColorPicker({ value, onChange, children }: ColorPickerProps) {
return (
<Popover.Root>
<Popover.Trigger>
<Button color="gray" style={{ color: value }} variant="soft">
{children ? children : <ColorIcon />}
{value}
</Button>
</Popover.Trigger>
<Popover.Content minWidth="268px">
<Flex direction="column" gap="2">
<HexAlphaColorPicker color={value} onChange={onChange} />
<TextField.Root
color="gray"
onChange={(event) => {
const newValue = event.target.value;

if (!newValue.startsWith("#")) {
return;
}

onChange(newValue);
}}
value={value}
variant="soft"
/>
</Flex>
</Popover.Content>
</Popover.Root>
);
}
5 changes: 0 additions & 5 deletions apps/dashboard/src/components/EditorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export function EditorToolbar() {
height: 50,
visible: true,
rotate: 0,
opacity: 100,
content: "Text",
color: "#000000",
fontFamily: "Inter",
Expand Down Expand Up @@ -57,7 +56,6 @@ export function EditorToolbar() {
height: 200,
visible: true,
rotate: 0,
opacity: 100,
backgroundColor: "#000000",
}),
);
Expand All @@ -78,7 +76,6 @@ export function EditorToolbar() {
height: 150,
visible: true,
rotate: 0,
opacity: 100,
backgroundColor: "#000000",
radius: 999,
}),
Expand All @@ -100,7 +97,6 @@ export function EditorToolbar() {
height: 150,
visible: true,
rotate: 0,
opacity: 100,
backgroundColor: "#000000",
backgroundImage: "https://source.unsplash.com/random",
backgroundSize: "cover",
Expand All @@ -123,7 +119,6 @@ export function EditorToolbar() {
height: 50,
visible: true,
rotate: 0,
opacity: 100,
content: "dynamic",
color: "#000000",
fontFamily: "Inter",
Expand Down
50 changes: 15 additions & 35 deletions apps/dashboard/src/components/RightPanel/BackgroundSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
IconButton,
} from "@radix-ui/themes";
import type { OGElement } from "../../lib/types";
import { ColorIcon } from "../icons/ColorIcon";
import { DeleteIcon } from "../icons/DeleteIcon";
import { StartIcon } from "../icons/StartIcon";
import { AddIcon } from "../icons/AddIcon";
import { EndIcon } from "../icons/EndIcon";
import { SquareIcon } from "../icons/SquareIcon";
import { LinkIcon } from "../icons/LinkIcon";
import { useElementsStore } from "../../stores/elementsStore";
import { ColorPicker } from "../ColorPicker";

interface BackgroundSectionProps {
selectedElement: OGElement;
Expand All @@ -34,23 +34,15 @@ export function BackgroundSection({ selectedElement }: BackgroundSectionProps) {
{!selectedElement.gradient ? (
<>
<Text size="1">Background color</Text>
<TextField.Root
onChange={(event) => {
<ColorPicker
onChange={(backgroundColor) => {
updateElement({
...selectedElement,
backgroundColor: event.target.value,
backgroundColor,
});
}}
value={selectedElement.backgroundColor}
variant="soft"
color="gray"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<ColorIcon />
</TextField.Slot>
</TextField.Root>
/>
</>
) : null}
<Flex justify="between">
Expand Down Expand Up @@ -92,48 +84,36 @@ export function BackgroundSection({ selectedElement }: BackgroundSectionProps) {
</Flex>
{selectedElement.gradient ? (
<Grid columns="2" gap="2">
<TextField.Root
onChange={(event) => {
<ColorPicker
onChange={(start) => {
updateElement({
...selectedElement,
// @ts-expect-error wtf?
gradient: {
...selectedElement.gradient,
start: event.target.value,
start,
},
});
}}
value={selectedElement.gradient.start}
variant="soft"
color="gray"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<StartIcon />
</TextField.Slot>
</TextField.Root>
<TextField.Root
color="gray"
onChange={(event) => {
<StartIcon />
</ColorPicker>
<ColorPicker
onChange={(end) => {
updateElement({
...selectedElement,
// @ts-expect-error wtf?
gradient: {
...selectedElement.gradient,
end: event.target.value,
end,
},
});
}}
value={selectedElement.gradient.end}
variant="soft"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<EndIcon />
</TextField.Slot>
</TextField.Root>
<EndIcon />
</ColorPicker>
<Select.Root
onValueChange={(value) => {
updateElement({
Expand Down
18 changes: 5 additions & 13 deletions apps/dashboard/src/components/RightPanel/BorderSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
import type { OGElement } from "../../lib/types";
import { DeleteIcon } from "../icons/DeleteIcon";
import { AddIcon } from "../icons/AddIcon";
import { ColorIcon } from "../icons/ColorIcon";
import { WidthIcon } from "../icons/WidthIcon";
import { useElementsStore } from "../../stores/elementsStore";
import { ColorPicker } from "../ColorPicker";

interface BorderSectionProps {
selectedElement: OGElement;
Expand Down Expand Up @@ -60,27 +60,19 @@ export function BorderSection({ selectedElement }: BorderSectionProps) {
</Flex>
{selectedElement.border ? (
<Grid columns="2" gap="2">
<TextField.Root
onChange={(event) => {
<ColorPicker
onChange={(color) => {
updateElement({
...selectedElement,
// @ts-expect-error wtf?
border: {
...selectedElement.border,
color: event.target.value,
color,
},
});
}}
value={selectedElement.border.color}
variant="soft"
color="gray"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<ColorIcon />
</TextField.Slot>
</TextField.Root>
/>
<TextField.Root
color="gray"
max={99}
Expand Down
18 changes: 5 additions & 13 deletions apps/dashboard/src/components/RightPanel/FontSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import type { OGElement } from "../../lib/types";
import type { Font } from "../../lib/fonts";
import { FONTS, FONT_WEIGHTS } from "../../lib/fonts";
import { FontSizeIcon } from "../icons/FontSizeIcon";
import { ColorIcon } from "../icons/ColorIcon";
import { LineHeightIcon } from "../icons/LineHeightIcon";
import { LetterSpacingIcon } from "../icons/LetterSpacingIcon";
import { useElementsStore } from "../../stores/elementsStore";
import { ColorPicker } from "../ColorPicker";

const SPACES_REGEX = /\s+/g;

Expand Down Expand Up @@ -85,23 +85,15 @@ export function FontSection({ selectedElement }: FontSectionProps) {
</TextField.Slot>
<TextField.Slot>px</TextField.Slot>
</TextField.Root>
<TextField.Root
color="gray"
onChange={(event) => {
<ColorPicker
onChange={(color) => {
updateElement({
...selectedElement,
color: event.target.value,
color,
});
}}
value={selectedElement.color}
variant="soft"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<ColorIcon />
</TextField.Slot>
</TextField.Root>
/>
<TextField.Root
color="gray"
max={5}
Expand Down
20 changes: 0 additions & 20 deletions apps/dashboard/src/components/RightPanel/MiscellanousSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Flex, Button, Grid, Text, TextField } from "@radix-ui/themes";
import type { OGElement } from "../../lib/types";
import { CornerIcon } from "../icons/CornerIcon";
import { OpacityIcon } from "../icons/OpacityIcon";
import { RotateIcon } from "../icons/RotateIcon";
import { useElementsStore } from "../../stores/elementsStore";
import { MagicWandIcon } from "../icons/MagicWandIcon";
Expand All @@ -20,25 +19,6 @@ export function MiscellanousSection({
<Flex direction="column" gap="2">
<Text size="1">Miscellanous</Text>
<Grid columns="2" gap="2">
<TextField.Root
color="gray"
max={100}
min={0}
onChange={(event) => {
updateElement({
...selectedElement,
opacity: event.target.valueAsNumber,
});
}}
type="number"
value={selectedElement.opacity}
variant="soft"
>
<TextField.Slot>
<OpacityIcon />
</TextField.Slot>
<TextField.Slot>%</TextField.Slot>
</TextField.Root>
<TextField.Root
color="gray"
max={360}
Expand Down
18 changes: 5 additions & 13 deletions apps/dashboard/src/components/RightPanel/ShadowSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Flex, Grid, IconButton, Text, TextField } from "@radix-ui/themes";
import type { OGElement } from "../../lib/types";
import { DeleteIcon } from "../icons/DeleteIcon";
import { AddIcon } from "../icons/AddIcon";
import { ColorIcon } from "../icons/ColorIcon";
import { WidthIcon } from "../icons/WidthIcon";
import { GradientIcon } from "../icons/GradientIcon";
import { useElementsStore } from "../../stores/elementsStore";
import { ColorPicker } from "../ColorPicker";

interface ShadowSectionProps {
selectedElement: OGElement;
Expand Down Expand Up @@ -143,27 +143,19 @@ export function ShadowSection({ selectedElement }: ShadowSectionProps) {
<TextField.Slot>Y</TextField.Slot>
<TextField.Slot>px</TextField.Slot>
</TextField.Root>
<TextField.Root
onChange={(event) => {
<ColorPicker
onChange={(color) => {
updateElement({
...selectedElement,
// @ts-expect-error wtf?
shadow: {
...selectedElement.shadow,
color: event.target.value,
color,
},
});
}}
value={selectedElement.shadow.color}
variant="soft"
color="gray"
// @ts-expect-error wtf?
type="color"
>
<TextField.Slot>
<ColorIcon />
</TextField.Slot>
</TextField.Root>
/>
</Grid>
) : null}
</Flex>
Expand Down
Loading

0 comments on commit 4907506

Please sign in to comment.