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