diff --git a/package.json b/package.json
index f009d34..350815b 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"scripts": {
"build": "tsc -b && vite build",
"build:wasm": "wasm-pack build colorlib -t bundler -d pkg --release",
+ "check": "tsc --noEmit -p tsconfig.app.json",
"clean": "rm -rf dist colorlib/pkg*",
"cypress:open": "1>/dev/null 2>/dev/null cypress open -d &",
"dev": "vite",
diff --git a/src/App.tsx b/src/App.tsx
index bb2d556..d350bb5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,12 +1,12 @@
-import { useState } from "react";
+import { useReducer, useState } from "react";
import clsx from "clsx";
import ColorPicker from "@components/ColorPicker/ColorPicker";
import ColorValues from "@components/ColorValues/ColorValues";
import { LeftMenu, RightMenu } from "@components/SideMenu";
-
import { useMediaQuery } from "@providers/hooks";
+import { useSelectedColor } from "@providers/hooks";
import styles from "./App.module.css";
@@ -90,6 +90,8 @@ function MobileRightNav({ onClick, isOpen }: MenuButtonProps) {
}
function MobileFirstZone() {
+ const { selectedColor, selectedColorActions } = useSelectedColor();
+
return (
@@ -190,6 +192,8 @@ function MobileContent({
// Desktop Layout Components
function FirstZone() {
+ const { selectedColor, selectedColorActions } = useSelectedColor();
+
return (
@@ -197,7 +201,7 @@ function FirstZone() {
-
+
);
diff --git a/src/components/ColorValues/ColorValues.module.css b/src/components/ColorValues/ColorValues.module.css
index a6a2a2b..5fbec1b 100644
--- a/src/components/ColorValues/ColorValues.module.css
+++ b/src/components/ColorValues/ColorValues.module.css
@@ -1,29 +1,6 @@
-.container {
+.colorValuesWrapper {
width: 100%;
height: 100%;
-}
-
-.valueItem {
-}
-
-/* Component Editor */
-
-.componentWrapper {
-}
-
-/* Large - Landscape Tablets / Desktops */
-/* Medium - Portrait Tablets */
-/* Horizontal layout, vertically scrolling picker and palette content */
-@media (min-width: 992px),
- (min-width: 568px) and (max-width: 991px) and (orientation: portrait) {
-}
-
-/* Medium - Landscape Phones */
-/* Horizontal layout, side menu, vertical tabbed picker */
-@media (min-width: 568px) and (max-width: 991px) and (orientation: landscape) {
-}
-
-/* Small - Portrait Phones*/
-/* Vertical layout, side menu, horizontal tabbed picker */
-@media (max-width: 567px) {
+ display: flex;
+ flex-direction: column;
}
diff --git a/src/components/ColorValues/ColorValues.tsx b/src/components/ColorValues/ColorValues.tsx
index 9145c56..ae4a46f 100644
--- a/src/components/ColorValues/ColorValues.tsx
+++ b/src/components/ColorValues/ColorValues.tsx
@@ -1,10 +1,75 @@
+import { useRef } from "react";
+import type { KeyboardEvent } from "react";
+
import * as colorlib from "colorlib";
+import type { ColorActions } from "@hooks/color";
+
import styles from "./ColorValues.module.css";
import SpaceEditor from "./SpaceEditor";
+import { HexEditor } from "./ValueEditor";
-function ColorValues({ selectedColor }: { selectedColor: colorlib.Color }) {
- return
;
+function ColorValues({
+ color,
+ actions,
+}: {
+ color: colorlib.Color;
+ actions: ColorActions;
+}) {
+ const wrapperRef = useRef(null);
+
+ const handleKeyDown = (e: KeyboardEvent) => {
+ // Cycle through inputs on Enter / Shift+Enter
+ if (e.key === "Enter") {
+ e.preventDefault();
+
+ const wrapper = wrapperRef.current;
+ if (!wrapper) return;
+
+ const inputs = Array.from(wrapper.querySelectorAll("input"));
+ const currentIndex = inputs.indexOf(e.target as HTMLInputElement);
+
+ if (currentIndex === -1) return;
+
+ if (e.shiftKey) {
+ // Go to previous input
+ const prevIndex = (currentIndex - 1 + inputs.length) % inputs.length;
+ inputs[prevIndex]?.focus();
+ } else {
+ // Go to next input
+ const nextIndex = (currentIndex + 1) % inputs.length;
+ inputs[nextIndex]?.focus();
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ );
}
export default ColorValues;
diff --git a/src/components/ColorValues/ColorValuesTest.cy.tsx b/src/components/ColorValues/ColorValuesTest.cy.tsx
index e69de29..a16214b 100644
--- a/src/components/ColorValues/ColorValuesTest.cy.tsx
+++ b/src/components/ColorValues/ColorValuesTest.cy.tsx
@@ -0,0 +1,86 @@
+import { useReducer } from "react";
+
+import { Color } from "colorlib";
+
+import { colorReducer, createColorActions } from "@hooks/color";
+
+import ColorValues from "./ColorValues";
+
+const initialState = {
+ color: Color.from_hex("2edd9d"),
+};
+
+function TestWrapper() {
+ const [state, dispatch] = useReducer(colorReducer, initialState);
+ const actions = createColorActions(dispatch);
+
+ return (
+
+ );
+}
+
+describe("color values component tests", () => {
+ beforeEach(() => {
+ cy.mount();
+ });
+
+ it("can cycle through inputs with Enter", () => {
+ cy.dataCy("HSV-editor").within(() => {
+ cy.dataCy("S-value-input").focus();
+ cy.dataCy("S-value-input").type("{enter}");
+ cy.dataCy("V-value-input").should("have.focus");
+ cy.dataCy("V-value-input").type("{enter}");
+ });
+
+ cy.dataCy("RGB-editor").within(() => {
+ cy.dataCy("R-value-input").should("have.focus");
+
+ cy.dataCy("B-value-input").focus();
+ cy.dataCy("B-value-input").type("{enter}");
+ });
+
+ cy.dataCy("hex-value-input").should("have.focus");
+ cy.dataCy("hex-value-input").type("{enter}");
+
+ cy.dataCy("HCL-editor").within(() => {
+ cy.dataCy("H-value-input").should("have.focus");
+ cy.dataCy("H-value-input").type("{shift}{enter}");
+ });
+
+ cy.dataCy("hex-value-input").should("have.focus");
+ cy.dataCy("hex-value-input").type("{shift}{enter}");
+
+ cy.dataCy("RGB-editor").within(() => {
+ cy.dataCy("B-value-input").should("have.focus");
+ cy.dataCy("B-value-input").type("{esc}");
+ cy.dataCy("B-value-input").should("not.have.focus");
+ });
+ });
+
+ it("can change color values", () => {
+ cy.dataCy("RGB-editor").within(() => {
+ cy.dataCy("R-value-input").type("120");
+ cy.dataCy("G-value-input").type("60");
+ cy.dataCy("B-value-input").type("220");
+ });
+
+ cy.dataCy("hex-value-input").should("have.value", "#783CDC");
+ });
+});
diff --git a/src/components/ColorValues/SpaceEditor.cy.tsx b/src/components/ColorValues/SpaceEditor.cy.tsx
deleted file mode 100644
index c66875e..0000000
--- a/src/components/ColorValues/SpaceEditor.cy.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import { useReducer } from "react";
-
-import { Color } from "colorlib";
-
-import { roundTo } from "@/util";
-import { colorReducer, createColorActions } from "@hooks/color";
-
-import SpaceEditor from "./SpaceEditor";
-
-const initialState = {
- color: Color.from_hex("2edd9d"),
-};
-
-function TestWrapper() {
- const [state, dispatch] = useReducer(colorReducer, initialState);
- const actions = createColorActions(dispatch);
-
- return (
- <>
-
-
-
-
-
-
-
-
- HCL ({roundTo(state.color.hcl.h, 0)}, {roundTo(state.color.hcl.c, 2)},{" "}
- {roundTo(state.color.hcl.l, 2)})
-
-
- HSV ({roundTo(state.color.hsv.h, 0)}, {roundTo(state.color.hsv.s, 2)},{" "}
- {roundTo(state.color.hsv.v, 2)})
-
-
- RGB ({roundTo(state.color.rgb.r, 0)}, {roundTo(state.color.rgb.g, 0)},{" "}
- {roundTo(state.color.rgb.b, 0)})
-
-
HEX: #{state.color.hex.to_code()}
-
- >
- );
-}
-
-describe("space editor tests", () => {
- it("can edit color values", () => {
- cy.mount();
-
- // Confirm initial values
- cy.dataCy("RGB-editor").within(() => {
- cy.dataCy("R-value-input").should("have.value", 46);
- cy.dataCy("G-value-input").should("have.value", 221);
- cy.dataCy("B-value-input").should("have.value", 157);
- });
-
- cy.dataCy("HSV-editor").within(() => {
- cy.dataCy("H-value-input").should("have.value", 158);
- cy.dataCy("S-value-input").should("have.value", 79);
- cy.dataCy("V-value-input").should("have.value", 87);
- });
-
- cy.dataCy("HCL-editor").within(() => {
- cy.dataCy("H-value-input").should("have.value", 158);
- cy.dataCy("C-value-input").should("have.value", 79);
- cy.dataCy("L-value-input").should("have.value", 70);
- });
-
- cy.dataCy("rgb-value").should("have.text", "RGB (46, 221, 157)");
- cy.dataCy("hsv-value").should("have.text", "HSV (158, 0.79, 0.87)");
- cy.dataCy("hcl-value").should("have.text", "HCL (158, 0.79, 0.7)");
- cy.dataCy("hex-value").should("have.text", "HEX: #2EDD9D");
-
- // Update the color values
- cy.wait(50); // ensure render
- cy.dataCy("HCL-editor").within(() => {
- cy.dataCy("H-slider").click();
- cy.dataCy("C-decrement-button").click();
- cy.dataCy("L-value-input").type("25");
- });
-
- cy.dataCy("rgb-value").should("have.text", "RGB (17, 76, 74)");
- cy.dataCy("hsv-value").should("have.text", "HSV (179, 0.78, 0.3)");
- cy.dataCy("hcl-value").should("have.text", "HCL (179, 0.78, 0.25)");
- cy.dataCy("hex-value").should("have.text", "HEX: #104B4A");
- });
-});
diff --git a/src/components/ColorValues/SpaceEditor.tsx b/src/components/ColorValues/SpaceEditor.tsx
index 500baaf..c4c4034 100644
--- a/src/components/ColorValues/SpaceEditor.tsx
+++ b/src/components/ColorValues/SpaceEditor.tsx
@@ -7,23 +7,33 @@ import type {
} from "@hooks/color";
import styles from "./SpaceEditor.module.css";
-import ValueEditor from "./ValueEditor";
+import { ValueEditor } from "./ValueEditor";
-type SpaceEditorProps =
+type ColorSpaceProps =
| { space: "RGB"; color: colorlib.RGB; actions: RGBColorActions }
| { space: "HSV"; color: colorlib.HSV; actions: HSVColorActions }
| { space: "HCL"; color: colorlib.HCL; actions: HCLColorActions };
-function SpaceEditor({ space, color, actions }: SpaceEditorProps) {
+type SpaceEditorProps = ColorSpaceProps & {
+ onKeyDown?: (e: React.KeyboardEvent) => void;
+};
+
+function SpaceEditor({ space, color, actions, onKeyDown }: SpaceEditorProps) {
switch (space) {
case "RGB":
- return ;
+ return (
+
+ );
case "HSV":
- return ;
+ return (
+
+ );
case "HCL":
- return ;
+ return (
+
+ );
default:
return <>>;
@@ -68,9 +78,11 @@ const COLOR_SPACES = {
function RGBSpaceEditor({
color,
actions,
+ onKeyDown,
}: {
color: colorlib.RGB;
actions: RGBColorActions;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
return (
@@ -79,18 +91,21 @@ function RGBSpaceEditor({
valueRange={COLOR_SPACES.RGB.ranges.r}
value={color.r}
setValue={actions.setR}
+ onKeyDown={onKeyDown}
/>
);
@@ -99,9 +114,11 @@ function RGBSpaceEditor({
function HSVSpaceEditor({
color,
actions,
+ onKeyDown,
}: {
color: colorlib.HSV;
actions: HSVColorActions;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
return (
@@ -110,12 +127,14 @@ function HSVSpaceEditor({
valueRange={COLOR_SPACES.HSV.ranges.h}
value={color.h}
setValue={actions.setH}
+ onKeyDown={onKeyDown}
/>
@@ -132,9 +152,11 @@ function HSVSpaceEditor({
function HCLSpaceEditor({
color,
actions,
+ onKeyDown,
}: {
color: colorlib.HCL;
actions: HCLColorActions;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
return (
@@ -143,12 +165,14 @@ function HCLSpaceEditor({
valueRange={COLOR_SPACES.HCL.ranges.h}
value={color.h}
setValue={actions.setH}
+ onKeyDown={onKeyDown}
/>
diff --git a/src/components/ColorValues/ValueEditor.module.css b/src/components/ColorValues/ValueEditor.module.css
index abcb415..78142b7 100644
--- a/src/components/ColorValues/ValueEditor.module.css
+++ b/src/components/ColorValues/ValueEditor.module.css
@@ -2,7 +2,7 @@
display: flex;
align-items: stretch;
width: 100%;
- min-height: 0;
+ height: 25px;
font-family: monospace;
border: 1px solid black;
border-top: none;
@@ -85,3 +85,34 @@
font-size: 14px;
text-align: right;
}
+
+.hexEditor {
+ display: flex;
+ align-items: stretch;
+ font-family: monospace;
+ border: 1px solid black;
+ height: 25px;
+ max-width: 150px;
+}
+
+.hexLabel {
+ font-family: monospace;
+ font-size: 14px;
+ padding: 0 10px;
+}
+
+.hexValueWrapper {
+ flex: 1;
+}
+
+.hexValueWrapper input {
+ height: 100%;
+ width: 100%;
+ background: none;
+ border: none;
+ padding: 0 5px;
+ font-family: monospace;
+ font-size: 14px;
+ letter-spacing: 0.1em;
+ text-align: center;
+}
diff --git a/src/components/ColorValues/ValueEditor.tsx b/src/components/ColorValues/ValueEditor.tsx
index 4ca07c5..cd88550 100644
--- a/src/components/ColorValues/ValueEditor.tsx
+++ b/src/components/ColorValues/ValueEditor.tsx
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from "react";
-import type { CSSProperties, ChangeEvent, RefObject } from "react";
+import type { ChangeEvent, KeyboardEvent, RefObject } from "react";
import {
faChevronLeft,
@@ -8,29 +8,37 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
+import * as colorlib from "colorlib";
import type { CartesianSpace, Range, Setter, Timeout } from "@/types";
import { Direction } from "@/types";
import { minmax, setMeasurements, valueToPosition } from "@/util";
+import type { HexColorActions } from "@hooks/color";
import { useScroll } from "@hooks/scroll";
import { useSlider } from "@hooks/slider";
import { useResize } from "@hooks/window";
import styles from "./ValueEditor.module.css";
+// ------------ //
+// Value Editor //
+// ------------ //
+
// Component
-function ValueEditor({
+export function ValueEditor({
componentSymbol,
valueRange,
value,
setValue,
scale = 1,
+ onKeyDown,
}: {
componentSymbol: string;
valueRange: Range;
value: number;
setValue: Setter;
scale?: number;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
// Set up component state
const direction = Direction.HORIZONTAL;
@@ -100,12 +108,14 @@ function ValueEditor({
position={position}
dimensions={dimensions}
componentSymbol={componentSymbol}
+ onKeyDown={onKeyDown}
/>
);
@@ -144,12 +156,22 @@ function Slider({
position,
dimensions,
componentSymbol,
+ onKeyDown,
}: {
sliderRef: RefObject;
position: number;
dimensions: CartesianSpace;
componentSymbol: string;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ (e.target as HTMLElement).blur();
+ } else if (onKeyDown) {
+ onKeyDown(e);
+ }
+ };
+
return (
void;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
const isIncrease = direction === "increase";
const label = isIncrease ? "Increase" : "Decrease";
@@ -191,6 +216,14 @@ function Button({
const onClick = () => handleValueStep(step);
const longPressProps = useLongPressRepeat(onClick);
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ (e.target as HTMLElement).blur();
+ } else if (onKeyDown) {
+ onKeyDown(e);
+ }
+ };
+
return (
@@ -213,6 +247,7 @@ function Value({
componentSymbol,
handleValueStep,
scale,
+ onKeyDown,
}: {
value: number;
onChange: (e: ChangeEvent) => void;
@@ -220,6 +255,7 @@ function Value({
componentSymbol: string;
handleValueStep: (step: number) => void;
scale: number;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
}) {
const valueRef = useRef(null);
const valueScroller = useScroll({
@@ -228,6 +264,14 @@ function Value({
onScrollDown: () => handleValueStep(-1),
});
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ (e.target as HTMLElement).blur();
+ } else if (onKeyDown) {
+ onKeyDown(e);
+ }
+ };
+
useEffect(() => {
if (valueRef.current) {
valueScroller.addScrollListener();
@@ -252,6 +296,7 @@ function Value({
aria-valuemax={valueRange.max}
aria-valuenow={value}
data-cy={`${componentSymbol}-value-input`}
+ onKeyDown={handleKeyDown}
/>
);
@@ -298,4 +343,88 @@ function useLongPressRepeat(
};
}
-export default ValueEditor;
+// ---------- //
+// Hex Editor //
+// ---------- //
+
+const extractHexValue = (value: string): string | null => {
+ const match = value.match(/^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$/);
+ return match ? match[1] : null;
+};
+
+const formatHexString = (
+ color: colorlib.Hex,
+ preserveShortFormat: boolean = false,
+): string => {
+ const hexValue = color.to_code();
+
+ if (preserveShortFormat) {
+ if (
+ hexValue[0] === hexValue[1] &&
+ hexValue[2] === hexValue[3] &&
+ hexValue[4] === hexValue[5]
+ ) {
+ return `#${hexValue[0]}${hexValue[2]}${hexValue[4]}`;
+ }
+ }
+
+ return `#${color.to_code()}`;
+};
+
+export function HexEditor({
+ color,
+ actions,
+ onKeyDown,
+}: {
+ color: colorlib.Hex;
+ actions: HexColorActions;
+ onKeyDown?: (e: React.KeyboardEvent) => void;
+}) {
+ const [inputValue, setInputValue] = useState(formatHexString(color));
+ const [isShortHex, setIsShortHex] = useState(false);
+
+ useEffect(() => {
+ setInputValue(formatHexString(color, isShortHex));
+ }, [color]);
+
+ const onChange = (e: ChangeEvent
) => {
+ const value = e.target.value;
+ setInputValue(value);
+
+ const hex = extractHexValue(value);
+ if (hex) {
+ setIsShortHex(hex.length === 3);
+ const newColor = colorlib.Hex.from_code(hex);
+ actions.setHex(newColor);
+ }
+ };
+
+ const onBlur = () => {
+ setInputValue(formatHexString(color));
+ };
+
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ (e.target as HTMLElement).blur();
+ } else if (onKeyDown) {
+ onKeyDown(e);
+ }
+ };
+
+ return (
+
+
HEX
+
+ e.target.select()}
+ onKeyDown={handleKeyDown}
+ />
+
+
+ );
+}
diff --git a/src/components/ColorValues/ValueEditorTest.cy.tsx b/src/components/ColorValues/ValueEditorTest.cy.tsx
index bfed38b..f5d886a 100644
--- a/src/components/ColorValues/ValueEditorTest.cy.tsx
+++ b/src/components/ColorValues/ValueEditorTest.cy.tsx
@@ -4,7 +4,7 @@ import { Color } from "colorlib";
import { colorReducer, createColorActions } from "@hooks/color";
-import ValueEditor from "./ValueEditor";
+import { ValueEditor } from "./ValueEditor";
const initialState = {
color: Color.from_hex("000"),
@@ -18,7 +18,6 @@ function TestWrapper() {
{
cy.clock().then((clock) => clock.restore());
});
- it.only("works with mouse events", () => {
+ it("works with mouse events", () => {
// Check initial state
cy.dataCy("R-slider-bar")
.should("have.css", "width", "0px")
@@ -159,4 +158,35 @@ describe("component editor tests", () => {
.dataCy("R-value-input")
.should("have.value", "127");
});
+
+ it("works with keyboard events", () => {
+ // Tab through components
+ cy.press(Cypress.Keyboard.Keys.TAB);
+ cy.dataCy("R-slider").should("have.focus");
+
+ cy.press(Cypress.Keyboard.Keys.TAB);
+ cy.dataCy("R-decrement-button").should("have.focus");
+
+ cy.press(Cypress.Keyboard.Keys.TAB);
+ cy.dataCy("R-value-input").should("have.focus");
+
+ cy.press(Cypress.Keyboard.Keys.TAB);
+ cy.dataCy("R-increment-button").should("have.focus");
+
+ // Pressing Escape should blur focused element
+ cy.dataCy("R-increment-button").type("{esc}");
+ cy.dataCy("R-increment-button").should("not.have.focus");
+
+ cy.dataCy("R-slider").focus();
+ cy.dataCy("R-slider").type("{esc}");
+ cy.dataCy("R-slider").should("not.have.focus");
+
+ cy.dataCy("R-decrement-button").focus();
+ cy.dataCy("R-decrement-button").type("{esc}");
+ cy.dataCy("R-decrement-button").should("not.have.focus");
+
+ cy.dataCy("R-value-input").focus();
+ cy.dataCy("R-value-input").type("{esc}");
+ cy.dataCy("R-value-input").should("not.have.focus");
+ });
});
diff --git a/src/hooks/scroll.ts b/src/hooks/scroll.ts
index 20ebad4..fbfe498 100644
--- a/src/hooks/scroll.ts
+++ b/src/hooks/scroll.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef, useState } from "react";
+import { useCallback, useEffect, useRef } from "react";
import type { RefObject } from "react";
export function handleScroll(
diff --git a/src/main.tsx b/src/main.tsx
index cf16e0a..c3c8241 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -4,12 +4,14 @@ import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
-import { MediaQueryProvider } from "./providers";
+import { MediaQueryProvider, SelectedColorProvider } from "./providers";
createRoot(document.getElementById("root")!).render(
-
+
+
+
,
);
diff --git a/src/providers/MediaQueryProvider.tsx b/src/providers/MediaQueryProvider.tsx
index f6a425d..bf73dd4 100644
--- a/src/providers/MediaQueryProvider.tsx
+++ b/src/providers/MediaQueryProvider.tsx
@@ -14,11 +14,11 @@ interface MediaQueryContextType {
isMobilePortrait: boolean;
}
-const MediaQueryContext = createContext
(
- undefined,
-);
+export const MediaQueryContext = createContext<
+ MediaQueryContextType | undefined
+>(undefined);
-function MediaQueryProvider({ children }: { children: ReactNode }) {
+export const MediaQueryProvider = ({ children }: { children: ReactNode }) => {
const [viewportMode, setViewportMode] = useState(
ViewportMode.DESKTOP,
);
@@ -71,7 +71,4 @@ function MediaQueryProvider({ children }: { children: ReactNode }) {
{children}
);
-}
-
-export default MediaQueryProvider;
-export { MediaQueryContext };
+};
diff --git a/src/providers/hooks.ts b/src/providers/hooks.ts
index ac06ce8..2bc43b4 100644
--- a/src/providers/hooks.ts
+++ b/src/providers/hooks.ts
@@ -1,8 +1,9 @@
import { useContext } from "react";
import { MediaQueryContext } from "./MediaQueryProvider";
+import { SelectedColorContext } from "./SelectedColorProvider";
-function useMediaQuery() {
+export function useMediaQuery() {
const context = useContext(MediaQueryContext);
if (context === undefined) {
throw new Error("useMediaQuery must be used within a MediaQueryProvider");
@@ -10,4 +11,10 @@ function useMediaQuery() {
return context;
}
-export { useMediaQuery };
+export function useSelectedColor() {
+ const context = useContext(SelectedColorContext);
+ if (!context) {
+ throw new Error("useColor must be used within a ColorProvider");
+ }
+ return context;
+}
diff --git a/src/providers/index.ts b/src/providers/index.ts
index 62a9952..dc20403 100644
--- a/src/providers/index.ts
+++ b/src/providers/index.ts
@@ -1,3 +1,4 @@
-import MediaQueryProvider from "./MediaQueryProvider";
+import { MediaQueryProvider } from "./MediaQueryProvider";
+import { SelectedColorProvider } from "./SelectedColorProvider";
-export { MediaQueryProvider };
+export { MediaQueryProvider, SelectedColorProvider };