dark mode v2
This commit is contained in:
@@ -4,7 +4,8 @@ import persian from 'react-date-object/calendars/persian';
|
|||||||
import persian_fa from 'react-date-object/locales/persian_fa';
|
import persian_fa from 'react-date-object/locales/persian_fa';
|
||||||
import DateObject from 'react-date-object';
|
import DateObject from 'react-date-object';
|
||||||
import { Calendar } from 'iconsax-react';
|
import { Calendar } from 'iconsax-react';
|
||||||
import { clx } from '../helpers/utils';
|
import { clx } from '@/helpers/utils';
|
||||||
|
import { getIconColor } from '@/utils/colorUtils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onChange: (date: string) => void;
|
onChange: (date: string) => void;
|
||||||
@@ -76,7 +77,7 @@ const DatePickerComponent: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
<Calendar
|
<Calendar
|
||||||
size={20}
|
size={20}
|
||||||
color='black'
|
color={getIconColor('muted')}
|
||||||
className='absolute top-0 bottom-0 my-auto left-2 pointer-events-none'
|
className='absolute top-0 bottom-0 my-auto left-2 pointer-events-none'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import EmojiPicker, { EmojiClickData } from 'emoji-picker-react'
|
import EmojiPicker, { EmojiClickData, Theme as EmojiTheme } from 'emoji-picker-react'
|
||||||
import { EmojiHappy } from 'iconsax-react'
|
import { EmojiHappy } from 'iconsax-react'
|
||||||
|
import { getIconColor } from '@/utils/colorUtils'
|
||||||
import { useOutsideClick } from '@/hooks/useOutSideClick'
|
import { useOutsideClick } from '@/hooks/useOutSideClick'
|
||||||
|
import { useTheme } from '@/contexts/ThemeContext'
|
||||||
|
|
||||||
interface EmojiReactionProps {
|
interface EmojiReactionProps {
|
||||||
onEmojiSelect: (emoji: string) => void
|
onEmojiSelect: (emoji: string) => void
|
||||||
@@ -10,6 +12,7 @@ interface EmojiReactionProps {
|
|||||||
|
|
||||||
const EmojiReaction: FC<EmojiReactionProps> = ({ onEmojiSelect, className = '' }) => {
|
const EmojiReaction: FC<EmojiReactionProps> = ({ onEmojiSelect, className = '' }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const containerRef = useOutsideClick(() => {
|
const containerRef = useOutsideClick(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@@ -30,14 +33,14 @@ const EmojiReaction: FC<EmojiReactionProps> = ({ onEmojiSelect, className = '' }
|
|||||||
<div ref={containerRef} className={`relative ${className}`}>
|
<div ref={containerRef} className={`relative ${className}`}>
|
||||||
<EmojiHappy
|
<EmojiHappy
|
||||||
size={24}
|
size={24}
|
||||||
color='black'
|
color={getIconColor('primary')}
|
||||||
className='cursor-pointer hover:opacity-70 transition-opacity'
|
className='cursor-pointer hover:opacity-70 transition-opacity'
|
||||||
onClick={togglePicker}
|
onClick={togglePicker}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className='absolute bottom-10 right-0 z-50'>
|
<div className='absolute bottom-10 right-0 z-50'>
|
||||||
<div className='bg-white rounded-lg shadow-lg border border-gray-200'>
|
<div className='bg-popover text-popover-foreground rounded-lg shadow-lg border border-border'>
|
||||||
<EmojiPicker
|
<EmojiPicker
|
||||||
onEmojiClick={handleEmojiClick}
|
onEmojiClick={handleEmojiClick}
|
||||||
width={300}
|
width={300}
|
||||||
@@ -45,6 +48,7 @@ const EmojiReaction: FC<EmojiReactionProps> = ({ onEmojiSelect, className = '' }
|
|||||||
|
|
||||||
searchDisabled
|
searchDisabled
|
||||||
skinTonesDisabled
|
skinTonesDisabled
|
||||||
|
theme={theme === 'dark' ? EmojiTheme.DARK : EmojiTheme.LIGHT}
|
||||||
previewConfig={{
|
previewConfig={{
|
||||||
defaultCaption: 'یک ایموجی انتخاب کنید',
|
defaultCaption: 'یک ایموجی انتخاب کنید',
|
||||||
defaultEmoji: '1f60a'
|
defaultEmoji: '1f60a'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { Star1 } from 'iconsax-react'
|
import { Star1 } from 'iconsax-react'
|
||||||
import { useMoveToFavorite, useUnFavoriteMessage } from '../hooks/useEmailData';
|
import { useMoveToFavorite, useUnFavoriteMessage } from '../hooks/useEmailData';
|
||||||
|
import { getIconColor } from '@/utils/colorUtils'
|
||||||
|
|
||||||
const Favorite: FC<{ flagged: boolean, id: string, mailbox: string }> = ({ flagged, id, mailbox }) => {
|
const Favorite: FC<{ flagged: boolean, id: string, mailbox: string }> = ({ flagged, id, mailbox }) => {
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ const Favorite: FC<{ flagged: boolean, id: string, mailbox: string }> = ({ flagg
|
|||||||
<Star1
|
<Star1
|
||||||
variant={isFavorite ? 'Bold' : 'Outline'}
|
variant={isFavorite ? 'Bold' : 'Outline'}
|
||||||
size={19}
|
size={19}
|
||||||
color={isFavorite ? '#FFC107' : '#8C90A3'}
|
color={isFavorite ? '#FFC107' : getIconColor('muted')}
|
||||||
className='cursor-pointer select-none'
|
className='cursor-pointer select-none'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ const Header: FC<{
|
|||||||
onClick={handleDownloadEmail}
|
onClick={handleDownloadEmail}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center xl:px-5'>
|
<div className='flex gap-2 items-center xl:px-5'>
|
||||||
<DocumentDownload size={20} color={getIconColor('primary')} />
|
<DocumentDownload size={20} color={'#000000'} />
|
||||||
<span className='pt-0.5 xl:block hidden'>{t('mail.download')}</span>
|
<span className='pt-0.5 xl:block hidden'>{t('mail.download')}</span>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -153,7 +153,7 @@ const Header: FC<{
|
|||||||
onClick={handlePrint}
|
onClick={handlePrint}
|
||||||
>
|
>
|
||||||
<div className='flex gap-2 items-center xl:px-5'>
|
<div className='flex gap-2 items-center xl:px-5'>
|
||||||
<Printer size={20} color={getIconColor('primary')} />
|
<Printer size={20} color={'#000000'} />
|
||||||
<span className='pt-0.5 xl:block hidden'>{t('mail.print')}</span>
|
<span className='pt-0.5 xl:block hidden'>{t('mail.print')}</span>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Star1 } from 'iconsax-react'
|
import { Star1 } from 'iconsax-react'
|
||||||
|
import { getIconColor } from '@/utils/colorUtils'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { useMoveToFavorite, useUnFavoriteMessage } from '../hooks/useEmailData'
|
import { useMoveToFavorite, useUnFavoriteMessage } from '../hooks/useEmailData'
|
||||||
@@ -25,7 +26,7 @@ const MessageFavorite: FC<{ flagged: boolean }> = ({ flagged }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
setIsFavorite(!isFavorite)
|
setIsFavorite(!isFavorite)
|
||||||
}} className='lg:size-[18px] size-[20px]' variant={isFavorite ? 'Bold' : 'Outline'} color={isFavorite ? '#FFC107' : 'black'} />
|
}} className='lg:size-[18px] size-[20px]' variant={isFavorite ? 'Bold' : 'Outline'} color={isFavorite ? '#FFC107' : getIconColor('muted')} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,60 +2,60 @@ import { FC } from 'react'
|
|||||||
|
|
||||||
const SkeletonDetail: FC = () => {
|
const SkeletonDetail: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-8'>
|
<div className='bg-card rounded-4xl p-8'>
|
||||||
<div className='animate-pulse'>
|
<div className='animate-pulse'>
|
||||||
{/* Header Skeleton */}
|
{/* Header Skeleton */}
|
||||||
<div className='h-6 bg-gray-200 rounded w-48 mb-6'></div>
|
<div className='h-6 bg-secondary rounded w-48 mb-6'></div>
|
||||||
|
|
||||||
{/* Subject and Tags Section */}
|
{/* Subject and Tags Section */}
|
||||||
<div className='mt-6 flex xl:flex-row flex-col justify-between xl:items-center'>
|
<div className='mt-6 flex xl:flex-row flex-col justify-between xl:items-center'>
|
||||||
<div className='flex xl:flex-row flex-col-reverse gap-4 xl:items-center'>
|
<div className='flex xl:flex-row flex-col-reverse gap-4 xl:items-center'>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
<div className='h-4 bg-gray-200 rounded w-12 hidden xl:block'></div>
|
<div className='h-4 bg-secondary rounded w-12 hidden xl:block'></div>
|
||||||
<div className='h-5 bg-gray-200 rounded w-40'></div>
|
<div className='h-5 bg-secondary rounded w-40'></div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='h-6 xl:h-10 bg-gray-200 rounded-full w-20'></div>
|
<div className='h-6 xl:h-10 bg-secondary rounded-full w-20'></div>
|
||||||
<div className='h-6 xl:h-10 bg-gray-200 rounded-full w-24'></div>
|
<div className='h-6 xl:h-10 bg-secondary rounded-full w-24'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-24'></div>
|
<div className='h-4 bg-secondary rounded w-24'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sender Info */}
|
{/* Sender Info */}
|
||||||
<div className='mt-6 flex gap-4 items-center'>
|
<div className='mt-6 flex gap-4 items-center'>
|
||||||
<div className='size-10 bg-gray-200 rounded-full'></div>
|
<div className='size-10 bg-secondary rounded-full'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-32'></div>
|
<div className='h-4 bg-secondary rounded w-32'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Email Content */}
|
{/* Email Content */}
|
||||||
<div className='xl:mt-8 mt-3 space-y-3'>
|
<div className='xl:mt-8 mt-3 space-y-3'>
|
||||||
<div className='h-4 bg-gray-200 rounded w-full'></div>
|
<div className='h-4 bg-secondary rounded w-full'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-full'></div>
|
<div className='h-4 bg-secondary rounded w-full'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-3/4'></div>
|
<div className='h-4 bg-secondary rounded w-3/4'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-full'></div>
|
<div className='h-4 bg-secondary rounded w-full'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-2/3'></div>
|
<div className='h-4 bg-secondary rounded w-2/3'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Attachments Section */}
|
{/* Attachments Section */}
|
||||||
<div className='mt-10'>
|
<div className='mt-10'>
|
||||||
<div className='h-4 bg-gray-200 rounded w-20 mb-4'></div>
|
<div className='h-4 bg-secondary rounded w-20 mb-4'></div>
|
||||||
<div className='flex border-b border-gray-200 pb-10 mt-4 flex-wrap gap-2.5 items-center'>
|
<div className='flex border-b border-border pb-10 mt-4 flex-wrap gap-2.5 items-center'>
|
||||||
<div className='h-10 bg-gray-200 rounded-full w-36'></div>
|
<div className='h-10 bg-secondary rounded-full w-36'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className='flex mt-9 justify-end'>
|
<div className='flex mt-9 justify-end'>
|
||||||
<div className='flex gap-5'>
|
<div className='flex gap-5'>
|
||||||
<div className='size-6 bg-gray-200 rounded'></div>
|
<div className='size-6 bg-secondary rounded'></div>
|
||||||
<div className='flex gap-2 items-center border-r border-gray-200 pr-5'>
|
<div className='flex gap-2 items-center border-r border-border pr-5'>
|
||||||
<div className='w-[17px] h-[17px] bg-gray-200 rounded'></div>
|
<div className='w-[17px] h-[17px] bg-secondary rounded'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-12'></div>
|
<div className='h-4 bg-secondary rounded w-12'></div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-2 items-center border-r border-gray-200 pr-5'>
|
<div className='flex gap-2 items-center border-r border-border pr-5'>
|
||||||
<div className='w-[17px] h-[17px] bg-gray-200 rounded'></div>
|
<div className='w-[17px] h-[17px] bg-secondary rounded'></div>
|
||||||
<div className='h-4 bg-gray-200 rounded w-16'></div>
|
<div className='h-4 bg-secondary rounded w-16'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import Forward from '@/components/newMessage/Forward'
|
|||||||
import EmojiReaction from '@/components/EmojiReaction'
|
import EmojiReaction from '@/components/EmojiReaction'
|
||||||
import { useAuthStore } from '@/pages/auth/store/AuthStore'
|
import { useAuthStore } from '@/pages/auth/store/AuthStore'
|
||||||
import { SendEmailDto } from './types/Types'
|
import { SendEmailDto } from './types/Types'
|
||||||
|
import { getIconColor } from '@/utils/colorUtils'
|
||||||
|
|
||||||
const DetailEmail: FC = () => {
|
const DetailEmail: FC = () => {
|
||||||
|
|
||||||
@@ -130,8 +131,8 @@ const DetailEmail: FC = () => {
|
|||||||
|
|
||||||
if (error || !messageDetail) {
|
if (error || !messageDetail) {
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-8'>
|
<div className='bg-card rounded-4xl p-8'>
|
||||||
<div className='text-center text-red-500'>
|
<div className='text-center text-destructive'>
|
||||||
خطا در بارگذاری پیام
|
خطا در بارگذاری پیام
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,7 +140,7 @@ const DetailEmail: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-8'>
|
<div className='bg-card rounded-4xl p-8'>
|
||||||
<Header
|
<Header
|
||||||
flagged={messageDetail?.flagged || false}
|
flagged={messageDetail?.flagged || false}
|
||||||
mailBoxName={messageDetail?.mailboxName as MailboxEnum}
|
mailBoxName={messageDetail?.mailboxName as MailboxEnum}
|
||||||
@@ -155,7 +156,7 @@ const DetailEmail: FC = () => {
|
|||||||
<div className='flex gap-2 justify-between items-center'>
|
<div className='flex gap-2 justify-between items-center'>
|
||||||
<Button
|
<Button
|
||||||
label={t(`mail.${messageDetail?.mailboxName}`)}
|
label={t(`mail.${messageDetail?.mailboxName}`)}
|
||||||
className='bg-[#ECEFF6] text-description w-fit px-6 rounded-full h-6 xl:h-10 text-[10px] xl:text-sm'
|
className='bg-secondary text-description w-fit px-6 rounded-full h-6 xl:h-10 text-[10px] xl:text-sm'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='lg:hidden flex justify-end text-description xl:text-sm text-[10px]'>
|
<div className='lg:hidden flex justify-end text-description xl:text-sm text-[10px]'>
|
||||||
@@ -195,7 +196,7 @@ const DetailEmail: FC = () => {
|
|||||||
<div className='xl:mt-8 mt-3 text-[13px] leading-7 font-light'>
|
<div className='xl:mt-8 mt-3 text-[13px] leading-7 font-light'>
|
||||||
{messageDetail.html && messageDetail.html.length > 0 ? (
|
{messageDetail.html && messageDetail.html.length > 0 ? (
|
||||||
<div
|
<div
|
||||||
className='email-content-container border border-gray-200 rounded-lg p-4 overflow-x-auto overflow-y-hidden'
|
className='email-content-container border border-border rounded-lg p-4 overflow-x-auto overflow-y-hidden'
|
||||||
style={{
|
style={{
|
||||||
direction: detectTextDirection(messageDetail.html.join(''))
|
direction: detectTextDirection(messageDetail.html.join(''))
|
||||||
}}
|
}}
|
||||||
@@ -215,11 +216,11 @@ const DetailEmail: FC = () => {
|
|||||||
<div className='flex border-b border-border pb-10 mt-4 flex-wrap gap-2.5 items-center'>
|
<div className='flex border-b border-border pb-10 mt-4 flex-wrap gap-2.5 items-center'>
|
||||||
{messageDetail.attachments && messageDetail.attachments.length > 0 ? (
|
{messageDetail.attachments && messageDetail.attachments.length > 0 ? (
|
||||||
messageDetail.attachments.map((attachment, index) => (
|
messageDetail.attachments.map((attachment, index) => (
|
||||||
<div key={index} className='bg-[#EBEDF5] text-sm flex gap-2 h-10 rounded-full items-center px-4'>
|
<div key={index} className='bg-secondary text-sm flex gap-2 h-10 rounded-full items-center px-4'>
|
||||||
<span>{attachment.filename || `attachment-${index + 1}`}</span>
|
<span>{attachment.filename || `attachment-${index + 1}`}</span>
|
||||||
<DocumentDownload
|
<DocumentDownload
|
||||||
size={20}
|
size={20}
|
||||||
color='#0038FF'
|
color={getIconColor('primary')}
|
||||||
className='cursor-pointer hover:opacity-70 transition-opacity'
|
className='cursor-pointer hover:opacity-70 transition-opacity'
|
||||||
onClick={() => handleDownloadAttachment(
|
onClick={() => handleDownloadAttachment(
|
||||||
attachment.id || `ATT${String(index + 1).padStart(5, '0')}`,
|
attachment.id || `ATT${String(index + 1).padStart(5, '0')}`,
|
||||||
@@ -229,7 +230,7 @@ const DetailEmail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className='bg-[#EBEDF5] text-sm flex gap-2 h-10 rounded-full items-center px-4'>
|
<div className='bg-secondary text-sm flex gap-2 h-10 rounded-full items-center px-4'>
|
||||||
<span>فایل ضمیمهای وجود ندارد</span>
|
<span>فایل ضمیمهای وجود ندارد</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+7
-11
@@ -1,24 +1,20 @@
|
|||||||
/**
|
/**
|
||||||
* تابع برای دریافت رنگ مناسب آیکونها بر اساس theme
|
* منبع رنگ آیکونها بر اساس متغیرهای CSS تم
|
||||||
* @param variant - نوع رنگ: 'primary' یا 'muted'
|
* - primary: از `--primary`
|
||||||
* @returns رنگ مناسب برای theme فعلی
|
* - muted: از `--muted-foreground`
|
||||||
|
* استفاده از متغیرهای CSS باعث میشود با تغییر تم، رنگها بدون نیاز به رفرش بروزرسانی شوند.
|
||||||
*/
|
*/
|
||||||
export const getIconColor = (
|
export const getIconColor = (
|
||||||
variant: "primary" | "muted" = "primary"
|
variant: "primary" | "muted" = "primary"
|
||||||
): string => {
|
): string => {
|
||||||
// تشخیص theme از document
|
|
||||||
const isDark = document.documentElement.classList.contains("dark");
|
|
||||||
|
|
||||||
if (variant === "muted") {
|
if (variant === "muted") {
|
||||||
return isDark ? "#9ca3af" : "#6b7280"; // خاکستری متوسط
|
return "var(--muted-foreground)";
|
||||||
}
|
}
|
||||||
|
return "var(--primary)";
|
||||||
// primary color
|
|
||||||
return isDark ? "#ffffff" : "#000000"; // سفید برای dark، سیاه برای light
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook برای استفاده در React components
|
* Hook ساده برای دسترسی به رنگها؛ به دلیل استفاده از CSS vars، نیازی به context نیست
|
||||||
*/
|
*/
|
||||||
export const useIconColor = () => {
|
export const useIconColor = () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user