diff --git a/src/__tests__/_/utils/index.ts b/src/__tests__/_/utils/index.ts
index 599e2fa7..271a275a 100644
--- a/src/__tests__/_/utils/index.ts
+++ b/src/__tests__/_/utils/index.ts
@@ -14,7 +14,7 @@ export const waitForSkeletonsToVanish = async () => {
};
export const getToastMessage = async () => {
- return (await within(screen.getByRole("region")).findByRole("status"))
+ return (await within(await screen.findByRole("region")).findByRole("status"))
.textContent;
};
@@ -48,3 +48,9 @@ export const selectCombobox = async (label: string, value: string) => {
pointerEventsCheck: PointerEventsCheckLevel.Never,
});
};
+
+export const waitPointerEvents = async () => {
+ await waitFor(() => {
+ expect(document.body).toHaveStyle("pointer-events: auto");
+ });
+};
diff --git a/src/__tests__/integrations/variables__constants.spec.tsx b/src/__tests__/integrations/variables__constants.spec.tsx
index 52250fed..e5fae722 100644
--- a/src/__tests__/integrations/variables__constants.spec.tsx
+++ b/src/__tests__/integrations/variables__constants.spec.tsx
@@ -10,6 +10,7 @@ import {
confirmDelete,
getTableRows,
getToastMessage,
+ waitPointerEvents,
} from "@/tests/utils";
setupApiHandlers();
@@ -63,13 +64,17 @@ describe("pages/integrations/variables => constants", () => {
const dialog = await screen.findByRole("dialog");
+ await waitPointerEvents();
+
expect(within(dialog).getByText("Create Constant")).toBeInTheDocument();
await userEvent.type(within(dialog).getByLabelText("Key"), "NEW_KEY");
await userEvent.type(within(dialog).getByLabelText("Value"), "new value");
await userEvent.click(
- within(dialog).getByRole("button", { name: "Create Constant" })
+ within(dialog).getByRole("button", {
+ name: "Create Constant",
+ })
);
expect(await getToastMessage()).toBe("Constant Saved Successfully");
@@ -119,6 +124,8 @@ describe("pages/integrations/variables => constants", () => {
})
);
+ await waitPointerEvents();
+
const dialog = screen.getByRole("dialog");
expect(within(dialog).getByText("Update Constant")).toBeInTheDocument();
diff --git a/src/__tests__/integrations/variables__credentials.spec.tsx b/src/__tests__/integrations/variables__credentials.spec.tsx
index 4a6ed29e..efcef99f 100644
--- a/src/__tests__/integrations/variables__credentials.spec.tsx
+++ b/src/__tests__/integrations/variables__credentials.spec.tsx
@@ -301,13 +301,7 @@ describe("pages/integrations/variables => credentials", () => {
)
);
- const dialog = await screen.findByRole(
- "dialog",
- {},
- {
- timeout: 20000,
- }
- );
+ const dialog = await screen.findByRole("dialog");
expect(within(dialog).getByText("Create Secret")).toBeInTheDocument();
diff --git a/src/frontend/_layouts/useAppTheme.ts b/src/frontend/_layouts/useAppTheme.ts
index 74668a10..c6a2726b 100644
--- a/src/frontend/_layouts/useAppTheme.ts
+++ b/src/frontend/_layouts/useAppTheme.ts
@@ -1,78 +1,17 @@
-/* eslint-disable no-param-reassign */
-/* eslint-disable no-bitwise */
import { useAppConfiguration } from "@/frontend/hooks/configuration/configuration.store";
+import { hexToOklch } from "../lib/colors/conversion";
import { usePortalThemes } from "./portal";
-type ThreeNumbers = readonly [number, number, number];
-
-function hexToRgb(hex$1: string): ThreeNumbers {
- let hex = hex$1.replace("#", "");
- if (hex.length === 3) {
- hex = hex
- .split("")
- .map((c) => c + c)
- .join("");
- }
- const bigint = parseInt(hex, 16);
- const r = (bigint >> 16) & 255;
- const g = (bigint >> 8) & 255;
- const b = bigint & 255;
- return [r, g, b];
-}
-
-function rgbToXyz([r, g, b]: ThreeNumbers) {
- r /= 255;
- g /= 255;
- b /= 255;
-
- r = r > 0.04045 ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92;
- g = g > 0.04045 ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92;
- b = b > 0.04045 ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
-
- const x = (r * 0.4124564 + g * 0.3575761 + b * 0.1804375) / 0.95047;
- const y = (r * 0.2126729 + g * 0.7151522 + b * 0.072175) / 1.0;
- const z = (r * 0.0193339 + g * 0.119192 + b * 0.9503041) / 1.08883;
-
- return [x, y, z] as const;
-}
-
-function xyzToLab([x, y, z]: ThreeNumbers) {
- x = x > 0.008856 ? Math.cbrt(x) : 7.787 * x + 16 / 116;
- y = y > 0.008856 ? Math.cbrt(y) : 7.787 * y + 16 / 116;
- z = z > 0.008856 ? Math.cbrt(z) : 7.787 * z + 16 / 116;
-
- const l = 116 * y - 16;
- const a = 500 * (x - y);
- const b = 200 * (y - z);
-
- return [l, a, b] as const;
-}
-
-function labToOklch([l, a, b]: ThreeNumbers) {
- const c = Math.sqrt(a * a + b * b);
- const h = Math.atan2(b, a);
- const hDegrees = (h * 180) / Math.PI;
- const hPositive = hDegrees < 0 ? hDegrees + 360 : hDegrees;
-
- return [l / 100, c / 100, hPositive] as const;
-}
-
-function hexToOklch(hex: string) {
- const rgb = hexToRgb(hex);
- const xyz = rgbToXyz(rgb);
- const lab = xyzToLab(xyz);
- const [l, c, h] = labToOklch(lab);
- return `${l.toFixed(3)}% ${c.toFixed(3)} ${h.toFixed(3)}`;
-}
-
export const useAppTheme = () => {
const themeColor = useAppConfiguration("theme_color");
usePortalThemes();
+ const { l, c, h } = hexToOklch(themeColor.data.primary);
+
document.documentElement.style.setProperty(
"--app-primary",
- hexToOklch(themeColor.data.primary)
+ `${l}% ${c} ${h}`
);
};
diff --git a/src/frontend/components/app/app-blur.tsx b/src/frontend/components/app/app-blur.tsx
new file mode 100644
index 00000000..6f7cfab0
--- /dev/null
+++ b/src/frontend/components/app/app-blur.tsx
@@ -0,0 +1,28 @@
+import type { ReactNode } from "react";
+import { useEffect } from "react";
+
+interface IProps {
+ children: ReactNode;
+ isOn: boolean;
+}
+
+const gaussianPortals = ["gaussian-portal-0", "gaussian-portal-1"];
+
+export function AppBlur({ children, isOn }: IProps): JSX.Element {
+ useEffect(() => {
+ if (isOn) {
+ gaussianPortals.forEach((portal) => {
+ document.getElementById(portal)?.classList.add("gaussian-blur");
+ });
+ setTimeout(() => {
+ document.body.style.pointerEvents = "auto";
+ }, 500);
+ } else {
+ gaussianPortals.forEach((portal) => {
+ document.getElementById(portal)?.classList.remove("gaussian-blur");
+ });
+ }
+ }, [isOn]);
+
+ return children as JSX.Element;
+}
diff --git a/src/frontend/components/app/confirm-alert.tsx b/src/frontend/components/app/confirm-alert.tsx
index 4c9392b3..9619ec73 100644
--- a/src/frontend/components/app/confirm-alert.tsx
+++ b/src/frontend/components/app/confirm-alert.tsx
@@ -14,7 +14,7 @@ import {
} from "@/components/ui/alert-dialog";
import { createStore } from "@/frontend/lib/store";
-import { NextPortal } from "./next-portal";
+import { AppBlur } from "./app-blur";
interface IConfirmAlertDetails {
title: MessageDescriptor;
@@ -51,16 +51,12 @@ export function ConfirmAlert() {
store.onClose,
]);
- if (!title) {
- return null;
- }
-
return (
-
+
- {_(title)}
+ {title ? _(title) : null}
{t`Are you sure you want to do this?`}
@@ -76,6 +72,6 @@ export function ConfirmAlert() {
-
+
);
}
diff --git a/src/frontend/components/app/next-portal.tsx b/src/frontend/components/app/next-portal.tsx
deleted file mode 100644
index 51d3c82e..00000000
--- a/src/frontend/components/app/next-portal.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { ReactNode } from "react";
-import { useEffect, useRef } from "react";
-import { createPortal } from "react-dom";
-
-import { useToggle } from "@/frontend/hooks/state/useToggleState";
-
-interface IProps {
- children: ReactNode;
-}
-
-const gaussianPortals = ["gaussian-portal-0", "gaussian-portal-1"];
-
-export function NextPortal({ children }: IProps) {
- const ref = useRef(null);
- const isMounted = useToggle();
-
- useEffect(() => {
- ref.current = document.body;
- isMounted.on();
- gaussianPortals.forEach((portal) => {
- document.getElementById(portal)?.classList.add("gaussian-blur");
- });
-
- return () => {
- gaussianPortals.forEach((portal) => {
- document.getElementById(portal)?.classList.remove("gaussian-blur");
- });
-
- isMounted.off();
- };
- }, []);
-
- return isMounted.isOn && ref.current
- ? createPortal(children, document.body)
- : null;
-}
diff --git a/src/frontend/components/app/off-canvas.tsx b/src/frontend/components/app/off-canvas.tsx
index ef621add..12f63961 100644
--- a/src/frontend/components/app/off-canvas.tsx
+++ b/src/frontend/components/app/off-canvas.tsx
@@ -7,7 +7,7 @@ import { cn } from "@/components/utils";
import { ScrollArea } from "../ui/scroll-area";
import { Separator } from "../ui/separator";
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "../ui/sheet";
-import { NextPortal } from "./next-portal";
+import { AppBlur } from "./app-blur";
export interface IProps {
show: boolean;
@@ -20,12 +20,8 @@ export interface IProps {
export function OffCanvas({ show, onClose, title, children, size }: IProps) {
const { _ } = useLingui();
- if (!show) {
- return null;
- }
-
return (
-
+
-
+
);
}
diff --git a/src/frontend/lib/colors/conversion.ts b/src/frontend/lib/colors/conversion.ts
new file mode 100644
index 00000000..9da37587
--- /dev/null
+++ b/src/frontend/lib/colors/conversion.ts
@@ -0,0 +1,61 @@
+type TRGB = { r: number; g: number; b: number };
+type TLCH = { l: number; c: number; h: number };
+
+export const hexToOklch = (hex: string) => {
+ const hexToRGB = (h: string): TRGB => {
+ const r: number = parseInt(h.slice(1, 3), 16);
+ const g: number = parseInt(h.slice(3, 5), 16);
+ const b: number = parseInt(h.slice(5, 7), 16);
+ return { r, g, b };
+ };
+
+ const rgbToOklch = (rgb: TRGB): TLCH => {
+ const r = rgb.r / 255;
+ const g = rgb.g / 255;
+ const b = rgb.b / 255;
+
+ const [linearR, linearG, linearB] = [r, g, b].map((c: number) =>
+ c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4
+ );
+
+ // Convert Linear RGB to CIE XYZ
+ let x: number =
+ linearR * 0.4124564 + linearG * 0.3575761 + linearB * 0.1804375;
+ let y: number =
+ linearR * 0.2126729 + linearG * 0.7151522 + linearB * 0.072175;
+ let z: number =
+ linearR * 0.0193339 + linearG * 0.119192 + linearB * 0.9503041;
+
+ // Convert CIE XYZ to CIELAB
+ [x, y, z] = [x, y, z].map((c: number) =>
+ c > 0.008856 ? c ** (1 / 3) : (903.3 * c + 16) / 116
+ );
+ let l: number = 116 * y - 16;
+ const a: number = 500 * (x - y);
+ const bStar: number = 200 * (y - z);
+
+ // Convert CIELAB to Oklch
+ // let c: number = Math.sqrt(a * a + bStar * bStar);
+ let h: number = Math.atan2(bStar, a) * (180 / Math.PI);
+ if (h < 0) h += 360;
+
+ // Assume c_max is the maximum chroma value observed or expected in your conversions
+ const c_max = 100; /* your determined or observed maximum chroma value */
+ // Adjusted part of the rgbToOklch function for calculating 'c'
+ let c: number = Math.sqrt(a * a + bStar * bStar);
+ c = (c / c_max) * 0.37; // Scale c to be within 0 and 0.37
+
+ // Scale and round values to match the specified ranges
+ l = Math.round(((l + 16) / 116) * 1000) / 10; // Scale l to be between 0 and 1
+ c = Number(c.toFixed(2)); // Ensure c is correctly scaled, adjust if necessary based on your color space calculations
+ h = Number(h.toFixed(1)); // h is already within 0 to 360
+
+ return {
+ l,
+ c,
+ h,
+ };
+ };
+
+ return rgbToOklch(hexToRGB(hex));
+};