From 2d468f8a9531bfee2e413e01742c4df7318ba15e Mon Sep 17 00:00:00 2001 From: Aliaksandr Radzivanovich Date: Fri, 22 Nov 2024 12:11:34 +0100 Subject: [PATCH] feat: add a color palette tab to the color picker component --- .stylelintrc.js | 1 + .../lib/design/input/ImageInput.module.less | 2 +- .../lib/design/input/color/ColorInput.tsx | 2 +- .../input/color/ColorPaletteTab.module.less | 19 ++ .../design/input/color/ColorPaletteTab.tsx | 25 +++ .../input/color/ColorPicker.module.less | 26 +-- .../lib/design/input/color/ColorPicker.tsx | 102 +++------- .../input/color/ColorPickerTab.module.less | 21 ++ .../lib/design/input/color/ColorPickerTab.tsx | 85 ++++++++ .../lib/design/input/color/palette.ts | 186 ++++++++++++++++++ 10 files changed, 371 insertions(+), 98 deletions(-) create mode 100644 packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.module.less create mode 100644 packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.tsx create mode 100644 packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.module.less create mode 100644 packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.tsx create mode 100644 packages/keybr-theme-designer/lib/design/input/color/palette.ts diff --git a/.stylelintrc.js b/.stylelintrc.js index 08252a2e..91a4a5bf 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -152,6 +152,7 @@ export default { "block-size", "min-block-size", "max-block-size", + "aspect-ratio", "margin", "margin-left", "margin-right", diff --git a/packages/keybr-theme-designer/lib/design/input/ImageInput.module.less b/packages/keybr-theme-designer/lib/design/input/ImageInput.module.less index 001b868d..660a8de5 100644 --- a/packages/keybr-theme-designer/lib/design/input/ImageInput.module.less +++ b/packages/keybr-theme-designer/lib/design/input/ImageInput.module.less @@ -1,8 +1,8 @@ .root { position: relative; inline-size: 20rem; - border: var(--separator-border); aspect-ratio: 16/9; + border: var(--separator-border); } .preview { diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorInput.tsx b/packages/keybr-theme-designer/lib/design/input/color/ColorInput.tsx index 7bfffe88..252635b9 100644 --- a/packages/keybr-theme-designer/lib/design/input/color/ColorInput.tsx +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorInput.tsx @@ -1,7 +1,7 @@ import { tryParseColor } from "@keybr/color"; import { TextField, useHotkeysHandler } from "@keybr/widget"; import { useEffect, useRef, useState } from "react"; -import type { ColorEditorProps } from "./types.ts"; +import { type ColorEditorProps } from "./types.ts"; export function ColorInput({ color, onChange }: ColorEditorProps) { const focus = useRef(false); diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.module.less b/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.module.less new file mode 100644 index 00000000..541d2d0d --- /dev/null +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.module.less @@ -0,0 +1,19 @@ +.root { + padding-block: 1rem; +} + +.row { + display: flex; + justify-content: center; +} + +.cell { + inline-size: 1rem; + block-size: 1rem; + cursor: pointer; +} + +.cell:hover { + z-index: 1; + outline: 1px solid white; +} diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.tsx b/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.tsx new file mode 100644 index 00000000..95ef2f43 --- /dev/null +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPaletteTab.tsx @@ -0,0 +1,25 @@ +import { parseColor } from "@keybr/color"; +import * as styles from "./ColorPaletteTab.module.less"; +import { palette } from "./palette.ts"; +import { type ColorEditorProps } from "./types.ts"; + +export function ColorPaletteTab({ color, onChange }: ColorEditorProps) { + return ( +
+ {Object.entries(palette).map(([name, colors]) => ( +
+ {Object.entries(colors).map(([saturation, color]) => ( + { + onChange(parseColor(color)); + }} + /> + ))} +
+ ))} +
+ ); +} diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.module.less b/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.module.less index ef967afa..10a4db0f 100644 --- a/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.module.less +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.module.less @@ -1,27 +1,13 @@ .root { - background-color: #000; -} - -.saturation { - position: relative; - box-sizing: content-box; inline-size: 256px; - block-size: 256px; - outline: none; } -.hue { - position: relative; - box-sizing: content-box; - inline-size: 256px; - block-size: 2rem; - outline: none; +.tabs { + display: flex; } -.channel { - position: relative; - box-sizing: content-box; - inline-size: 256px; - block-size: 2rem; - outline: none; +.tab { + flex: 1; + text-align: center; + cursor: pointer; } diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.tsx b/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.tsx index 9735a8a7..f7896512 100644 --- a/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.tsx +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPicker.tsx @@ -1,85 +1,35 @@ -import { HsvColor, RgbColor } from "@keybr/color"; -import { Spacer } from "@keybr/widget"; -import { ColorInput } from "./ColorInput.tsx"; +import { Icon } from "@keybr/widget"; +import { mdiGrid, mdiPalette } from "@mdi/js"; +import { useState } from "react"; +import { ColorPaletteTab } from "./ColorPaletteTab.tsx"; import * as styles from "./ColorPicker.module.less"; -import { Slider } from "./Slider.tsx"; -import { Thumb } from "./Thumb.tsx"; +import { ColorPickerTab } from "./ColorPickerTab.tsx"; import { type ColorEditorProps } from "./types.ts"; export function ColorPicker({ color, onChange }: ColorEditorProps) { - const { h, s, v } = color.toHsv(); - const { r, g, b } = color.toRgb(); - const saturationValue = { x: s, y: v }; - const hueValue = { x: h, y: 0.5 }; - const hueColor = new HsvColor(h, 1, 1); - const rValue = { x: r, y: 0.5 }; - const gValue = { x: g, y: 0.5 }; - const bValue = { x: b, y: 0.5 }; + const [tab, setTab] = useState(0); return (
- { - onChange(new HsvColor(h, x, y)); - }} - > - - - - { - onChange(new HsvColor(x, s, v)); - }} - > - - - { - onChange(new RgbColor(x, g, b)); - }} - > - - - { - onChange(new RgbColor(r, x, b)); - }} - > - - - { - onChange(new RgbColor(r, g, x)); - }} - > - - - +
+ { + setTab(0); + }} + > + + + { + setTab(1); + }} + > + + +
+ {tab === 0 && } + {tab === 1 && }
); } diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.module.less b/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.module.less new file mode 100644 index 00000000..8fffcd96 --- /dev/null +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.module.less @@ -0,0 +1,21 @@ +.root { + background-color: #000; +} + +.saturation { + position: relative; + aspect-ratio: 1/1; + outline: none; +} + +.hue { + position: relative; + block-size: 2rem; + outline: none; +} + +.channel { + position: relative; + block-size: 2rem; + outline: none; +} diff --git a/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.tsx b/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.tsx new file mode 100644 index 00000000..f947ce8c --- /dev/null +++ b/packages/keybr-theme-designer/lib/design/input/color/ColorPickerTab.tsx @@ -0,0 +1,85 @@ +import { HsvColor, RgbColor } from "@keybr/color"; +import { Spacer } from "@keybr/widget"; +import { ColorInput } from "./ColorInput.tsx"; +import * as styles from "./ColorPickerTab.module.less"; +import { Slider } from "./Slider.tsx"; +import { Thumb } from "./Thumb.tsx"; +import { type ColorEditorProps } from "./types.ts"; + +export function ColorPickerTab({ color, onChange }: ColorEditorProps) { + const { h, s, v } = color.toHsv(); + const { r, g, b } = color.toRgb(); + const saturationValue = { x: s, y: v }; + const hueValue = { x: h, y: 0.5 }; + const hueColor = new HsvColor(h, 1, 1); + const rValue = { x: r, y: 0.5 }; + const gValue = { x: g, y: 0.5 }; + const bValue = { x: b, y: 0.5 }; + return ( +
+ { + onChange(new HsvColor(h, x, y)); + }} + > + + + + { + onChange(new HsvColor(x, s, v)); + }} + > + + + { + onChange(new RgbColor(x, g, b)); + }} + > + + + { + onChange(new RgbColor(r, x, b)); + }} + > + + + { + onChange(new RgbColor(r, g, x)); + }} + > + + + +
+ ); +} diff --git a/packages/keybr-theme-designer/lib/design/input/color/palette.ts b/packages/keybr-theme-designer/lib/design/input/color/palette.ts new file mode 100644 index 00000000..28653237 --- /dev/null +++ b/packages/keybr-theme-designer/lib/design/input/color/palette.ts @@ -0,0 +1,186 @@ +/* + * https://github.com/yeun/open-color/ + * + * MIT License + * + * Copyright (c) 2016 heeyeun + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export type Palette = Record>; + +export const palette = { + gray: { + 50: "#f8f9fa", + 100: "#f1f3f5", + 200: "#e9ecef", + 300: "#dee2e6", + 400: "#ced4da", + 500: "#adb5bd", + 600: "#868e96", + 700: "#495057", + 800: "#343a40", + 900: "#212529", + }, + red: { + 50: "#fff5f5", + 100: "#ffe3e3", + 200: "#ffc9c9", + 300: "#ffa8a8", + 400: "#ff8787", + 500: "#ff6b6b", + 600: "#fa5252", + 700: "#f03e3e", + 800: "#e03131", + 900: "#c92a2a", + }, + pink: { + 50: "#fff0f6", + 100: "#ffdeeb", + 200: "#fcc2d7", + 300: "#faa2c1", + 400: "#f783ac", + 500: "#f06595", + 600: "#e64980", + 700: "#d6336c", + 800: "#c2255c", + 900: "#a61e4d", + }, + grape: { + 50: "#f8f0fc", + 100: "#f3d9fa", + 200: "#eebefa", + 300: "#e599f7", + 400: "#da77f2", + 500: "#cc5de8", + 600: "#be4bdb", + 700: "#ae3ec9", + 800: "#9c36b5", + 900: "#862e9c", + }, + violet: { + 50: "#f3f0ff", + 100: "#e5dbff", + 200: "#d0bfff", + 300: "#b197fc", + 400: "#9775fa", + 500: "#845ef7", + 600: "#7950f2", + 700: "#7048e8", + 800: "#6741d9", + 900: "#5f3dc4", + }, + indigo: { + 50: "#edf2ff", + 100: "#dbe4ff", + 200: "#bac8ff", + 300: "#91a7ff", + 400: "#748ffc", + 500: "#5c7cfa", + 600: "#4c6ef5", + 700: "#4263eb", + 800: "#3b5bdb", + 900: "#364fc7", + }, + blue: { + 50: "#e7f5ff", + 100: "#d0ebff", + 200: "#a5d8ff", + 300: "#74c0fc", + 400: "#4dabf7", + 500: "#339af0", + 600: "#228be6", + 700: "#1c7ed6", + 800: "#1971c2", + 900: "#1864ab", + }, + cyan: { + 50: "#e3fafc", + 100: "#c5f6fa", + 200: "#99e9f2", + 300: "#66d9e8", + 400: "#3bc9db", + 500: "#22b8cf", + 600: "#15aabf", + 700: "#1098ad", + 800: "#0c8599", + 900: "#0b7285", + }, + teal: { + 50: "#e6fcf5", + 100: "#c3fae8", + 200: "#96f2d7", + 300: "#63e6be", + 400: "#38d9a9", + 500: "#20c997", + 600: "#12b886", + 700: "#0ca678", + 800: "#099268", + 900: "#087f5b", + }, + green: { + 50: "#ebfbee", + 100: "#d3f9d8", + 200: "#b2f2bb", + 300: "#8ce99a", + 400: "#69db7c", + 500: "#51cf66", + 600: "#40c057", + 700: "#37b24d", + 800: "#2f9e44", + 900: "#2b8a3e", + }, + lime: { + 50: "#f4fce3", + 100: "#e9fac8", + 200: "#d8f5a2", + 300: "#c0eb75", + 400: "#a9e34b", + 500: "#94d82d", + 600: "#82c91e", + 700: "#74b816", + 800: "#66a80f", + 900: "#5c940d", + }, + yellow: { + 50: "#fff9db", + 100: "#fff3bf", + 200: "#ffec99", + 300: "#ffe066", + 400: "#ffd43b", + 500: "#fcc419", + 600: "#fab005", + 700: "#f59f00", + 800: "#f08c00", + 900: "#e67700", + }, + orange: { + 50: "#fff4e6", + 100: "#ffe8cc", + 200: "#ffd8a8", + 300: "#ffc078", + 400: "#ffa94d", + 500: "#ff922b", + 600: "#fd7e14", + 700: "#f76707", + 800: "#e8590c", + 900: "#d9480f", + }, +} as const satisfies Palette;