fix menu color
This commit is contained in:
@@ -1,39 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { hexToOklch, calculateBrightness } from '@/lib/helpers/colorUtils'
|
||||
|
||||
export function ThemeColorSetter() {
|
||||
useEffect(() => {
|
||||
try {
|
||||
const savedColor = localStorage.getItem('theme-primary-color')
|
||||
if (savedColor) {
|
||||
const hex = savedColor.replace('#', '')
|
||||
const r = parseInt(hex.substring(0, 2), 16) / 255
|
||||
const g = parseInt(hex.substring(2, 4), 16) / 255
|
||||
const b = parseInt(hex.substring(4, 6), 16) / 255
|
||||
|
||||
const lightness = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||
const max = Math.max(r, g, b)
|
||||
const min = Math.min(r, g, b)
|
||||
const chroma = (max - min) * 0.15
|
||||
|
||||
let hue = 0
|
||||
if (chroma !== 0) {
|
||||
if (max === r) {
|
||||
hue = ((g - b) / (max - min) + (g < b ? 6 : 0)) * 60
|
||||
} else if (max === g) {
|
||||
hue = ((b - r) / (max - min) + 2) * 60
|
||||
} else {
|
||||
hue = ((r - g) / (max - min) + 4) * 60
|
||||
}
|
||||
const oklchColor = hexToOklch(savedColor)
|
||||
|
||||
if (oklchColor) {
|
||||
document.documentElement.style.setProperty('--primary', oklchColor)
|
||||
|
||||
const brightness = calculateBrightness(savedColor)
|
||||
const foregroundOklch = brightness > 0.5 ? 'oklch(0 0 0)' : 'oklch(1 0 0)'
|
||||
document.documentElement.style.setProperty('--primary-foreground', foregroundOklch)
|
||||
}
|
||||
|
||||
const oklchColor = `oklch(${lightness.toFixed(3)} ${chroma.toFixed(3)} ${hue.toFixed(1)})`
|
||||
document.documentElement.style.setProperty('--primary', oklchColor)
|
||||
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000
|
||||
const foregroundOklch = brightness > 0.5 ? 'oklch(0 0 0)' : 'oklch(1 0 0)'
|
||||
document.documentElement.style.setProperty('--primary-foreground', foregroundOklch)
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
|
||||
@@ -4,6 +4,7 @@ import usePreference from '@/hooks/helpers/usePreference';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useGetAbout } from '@/app/[name]/(Main)/about/hooks/useAboutData';
|
||||
import SplashScreen from '@/components/overlays/SplashScreen';
|
||||
import { hexToOklch, calculateBrightness } from '@/lib/helpers/colorUtils';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
@@ -12,33 +13,15 @@ type Props = {
|
||||
// تابع برای اعمال رنگ primary
|
||||
function applyPrimaryColor(colorHex: string) {
|
||||
const root = document.documentElement;
|
||||
const hex = colorHex.replace('#', '');
|
||||
const r = parseInt(hex.substring(0, 2), 16) / 255;
|
||||
const g = parseInt(hex.substring(2, 4), 16) / 255;
|
||||
const b = parseInt(hex.substring(4, 6), 16) / 255;
|
||||
|
||||
const lightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
const chroma = (max - min) * 0.15;
|
||||
|
||||
let hue = 0;
|
||||
if (chroma !== 0) {
|
||||
if (max === r) {
|
||||
hue = ((g - b) / (max - min) + (g < b ? 6 : 0)) * 60;
|
||||
} else if (max === g) {
|
||||
hue = ((b - r) / (max - min) + 2) * 60;
|
||||
} else {
|
||||
hue = ((r - g) / (max - min) + 4) * 60;
|
||||
}
|
||||
const oklchColor = hexToOklch(colorHex);
|
||||
|
||||
if (oklchColor) {
|
||||
root.style.setProperty('--primary', oklchColor);
|
||||
|
||||
const brightness = calculateBrightness(colorHex);
|
||||
const foregroundOklch = brightness > 0.5 ? 'oklch(0 0 0)' : 'oklch(1 0 0)';
|
||||
root.style.setProperty('--primary-foreground', foregroundOklch);
|
||||
}
|
||||
|
||||
const oklchColor = `oklch(${lightness.toFixed(3)} ${chroma.toFixed(3)} ${hue.toFixed(1)})`;
|
||||
root.style.setProperty('--primary', oklchColor);
|
||||
|
||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||
const foregroundOklch = brightness > 0.5 ? 'oklch(0 0 0)' : 'oklch(1 0 0)';
|
||||
root.style.setProperty('--primary-foreground', foregroundOklch);
|
||||
}
|
||||
|
||||
function PreferenceWrapper({ children }: Props) {
|
||||
|
||||
@@ -1,56 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { hexToOklch, calculateBrightness } from "@/lib/helpers/colorUtils";
|
||||
|
||||
interface UseThemeColorProps {
|
||||
primaryColor?: string | null;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* تبدیل hex به RGB normalized (0-1)
|
||||
*/
|
||||
function hexToRgbNormalized(
|
||||
hex: string
|
||||
): { r: number; g: number; b: number } | null {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16) / 255,
|
||||
g: parseInt(result[2], 16) / 255,
|
||||
b: parseInt(result[3], 16) / 255,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* تبدیل RGB به OKLCH (تقریبی)
|
||||
*/
|
||||
function rgbToOklch(r: number, g: number, b: number): string {
|
||||
// محاسبه Lightness تقریبی
|
||||
const lightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
|
||||
// محاسبه Chroma و Hue (ساده شده)
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
const chroma = (max - min) * 0.15; // مقدار تقریبی
|
||||
|
||||
let hue = 0;
|
||||
if (chroma !== 0) {
|
||||
if (max === r) {
|
||||
hue = ((g - b) / (max - min) + (g < b ? 6 : 0)) * 60;
|
||||
} else if (max === g) {
|
||||
hue = ((b - r) / (max - min) + 2) * 60;
|
||||
} else {
|
||||
hue = ((r - g) / (max - min) + 4) * 60;
|
||||
}
|
||||
}
|
||||
|
||||
return `oklch(${lightness.toFixed(3)} ${chroma.toFixed(3)} ${hue.toFixed(
|
||||
1
|
||||
)})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* هوک برای تغییر دینامیک رنگ primary بر اساس رنگ دریافتی از about
|
||||
*/
|
||||
@@ -66,15 +23,14 @@ export function useThemeColor({
|
||||
|
||||
// اگر رنگ hex هست، آن را به oklch تبدیل میکنیم
|
||||
if (primaryColor.startsWith("#")) {
|
||||
const rgb = hexToRgbNormalized(primaryColor);
|
||||
if (rgb) {
|
||||
const oklchColor = rgbToOklch(rgb.r, rgb.g, rgb.b);
|
||||
|
||||
const oklchColor = hexToOklch(primaryColor);
|
||||
|
||||
if (oklchColor) {
|
||||
// اعمال رنگ primary
|
||||
root.style.setProperty("--primary", oklchColor);
|
||||
|
||||
// محاسبه رنگ foreground متناسب
|
||||
const brightness = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
||||
const brightness = calculateBrightness(primaryColor);
|
||||
const foregroundOklch =
|
||||
brightness > 0.5 ? "oklch(0 0 0)" : "oklch(1 0 0)";
|
||||
root.style.setProperty("--primary-foreground", foregroundOklch);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* تبدیل hex به RGB normalized (0-1)
|
||||
*/
|
||||
function hexToRgbNormalized(hex: string): { r: number; g: number; b: number } | null {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16) / 255,
|
||||
g: parseInt(result[2], 16) / 255,
|
||||
b: parseInt(result[3], 16) / 255,
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* تبدیل RGB normalized به oklch
|
||||
* استفاده از فرمول استاندارد OKLab/OKLCH
|
||||
*/
|
||||
function rgbToOklch(r: number, g: number, b: number): string {
|
||||
try {
|
||||
// تبدیل sRGB به linear RGB
|
||||
const toLinear = (c: number) => {
|
||||
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
};
|
||||
|
||||
const rLinear = toLinear(r);
|
||||
const gLinear = toLinear(g);
|
||||
const bLinear = toLinear(b);
|
||||
|
||||
// تبدیل linear RGB به XYZ (D65 white point)
|
||||
const x = rLinear * 0.4124564 + gLinear * 0.3575761 + bLinear * 0.1804375;
|
||||
const y = rLinear * 0.2126729 + gLinear * 0.7151522 + bLinear * 0.072175;
|
||||
const z = rLinear * 0.0193339 + gLinear * 0.119192 + bLinear * 0.9503041;
|
||||
|
||||
// تبدیل XYZ به OKLab
|
||||
const l = 0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z;
|
||||
const m = 0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z;
|
||||
const s = 0.0482003018 * x + 0.2643662691 * y + 0.6338517070 * z;
|
||||
|
||||
const lCbrt = Math.cbrt(l);
|
||||
const mCbrt = Math.cbrt(m);
|
||||
const sCbrt = Math.cbrt(s);
|
||||
|
||||
const L = 0.2104542553 * lCbrt + 0.7936177850 * mCbrt - 0.0040720468 * sCbrt;
|
||||
const a = 1.9779984951 * lCbrt - 2.4285922050 * mCbrt + 0.4505937099 * sCbrt;
|
||||
const bLab = 0.0259040371 * lCbrt + 0.7827717662 * mCbrt - 0.8086757660 * sCbrt;
|
||||
|
||||
// تبدیل OKLab به OKLCH
|
||||
const C = Math.sqrt(a * a + bLab * bLab);
|
||||
let h = Math.atan2(bLab, a) * (180 / Math.PI);
|
||||
if (h < 0) h += 360;
|
||||
|
||||
// تبدیل L از 0-1 به درصد
|
||||
const LPercent = L * 100;
|
||||
|
||||
return `oklch(${LPercent.toFixed(2)}% ${C.toFixed(4)} ${h.toFixed(1)})`;
|
||||
} catch (error) {
|
||||
// Fallback در صورت خطا
|
||||
return `oklch(60% 0.15 25)`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* تبدیل hex color به oklch
|
||||
*/
|
||||
export function hexToOklch(hex: string): string | null {
|
||||
const rgb = hexToRgbNormalized(hex);
|
||||
if (!rgb) return null;
|
||||
return rgbToOklch(rgb.r, rgb.g, rgb.b);
|
||||
}
|
||||
|
||||
/**
|
||||
* محاسبه brightness برای تعیین رنگ foreground
|
||||
*/
|
||||
export function calculateBrightness(hex: string): number {
|
||||
const rgb = hexToRgbNormalized(hex);
|
||||
if (!rgb) return 0.5;
|
||||
return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user