Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/gallery/gallery-picker-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function GalleryPickerModal({
>
<div className="flex flex-col w-full gap-4 p-1 h-[70vh]">
{categories.length > 0 && (
<div className="flex items-center gap-1.5 overflow-y-hidden h-12 overflow-x-auto no-scrollbar">
<div className="flex items-center gap-1.5 overflow-y-hidden h-12 overflow-x-auto scrollbar-none">
<Chip
selected={selectedCategory === 'ALL'}
onClick={() => setSelectedCategory('ALL')}
Expand Down
86 changes: 72 additions & 14 deletions src/components/ui/color-picker/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,66 @@
import type React from 'react'
import { useEffect, useRef, useState } from 'react'
import { RgbaStringColorPicker } from 'react-colorful'
import { HexColorPicker, RgbaStringColorPicker } from 'react-colorful'
import { Portal } from '../portal/portal'

export interface ColorPickerProps {
color: string
onChange: (color: string) => void
className?: string
mode?: 'hex' | 'rgba'
}

export const ColorPicker: React.FC<ColorPickerProps> = ({
color,
onChange,
className = '',
mode = 'hex',
}) => {
const [isOpen, setIsOpen] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)
const triggerRef = useRef<HTMLDivElement>(null)
const popupRef = useRef<HTMLDivElement>(null)
const [coords, setCoords] = useState<{ top: number; left: number }>({
top: 0,
left: 0,
})

const displayColor = color || '#000000'

const updatePosition = () => {
if (!triggerRef.current) return
const rect = triggerRef.current.getBoundingClientRect()
const popupWidth = 220
const popupHeight = 220

let left = rect.left
if (left + popupWidth > window.innerWidth - 16) {
left = window.innerWidth - popupWidth - 16
}
if (left < 16) {
left = 16
}

let top = rect.bottom + 8
if (top + popupHeight > window.innerHeight - 16) {
top = rect.top - popupHeight - 8
}

setCoords({ top, left })
}

useEffect(() => {
if (!isOpen) return

updatePosition()

const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Node
if (containerRef.current && !containerRef.current.contains(target)) {
if (
triggerRef.current &&
!triggerRef.current.contains(target) &&
popupRef.current &&
!popupRef.current.contains(target)
) {
setIsOpen(false)
}
}
Expand All @@ -34,36 +71,57 @@ export const ColorPicker: React.FC<ColorPickerProps> = ({
}
}

const handleScroll = () => {
updatePosition()
}

const timer = setTimeout(() => {
document.addEventListener('mousedown', handleClickOutside)
document.addEventListener('keydown', handleKeyDown)
window.addEventListener('scroll', handleScroll, true)
window.addEventListener('resize', handleScroll)
}, 0)

return () => {
clearTimeout(timer)
document.removeEventListener('mousedown', handleClickOutside)
document.removeEventListener('keydown', handleKeyDown)
window.removeEventListener('scroll', handleScroll, true)
window.removeEventListener('resize', handleScroll)
}
}, [isOpen])

return (
<div
ref={containerRef}
className={`relative inline-flex items-center ${isOpen ? 'z-50' : ''} ${className}`}
>
<div className={`relative inline-flex items-center ${className}`}>
<div
ref={triggerRef}
onClick={() => setIsOpen((prev) => !prev)}
className="w-8 h-8 p-1 transition-transform border-0 rounded-md shadow-xs cursor-pointer hover:scale-105 active:scale-95"
className="w-8 h-8 p-1 transition-transform border-0 shadow-xs cursor-pointer rounded-xl hover:scale-105 active:scale-95"
style={{ backgroundColor: displayColor }}
/>

{isOpen && (
<div
className="absolute right-0 top-full mt-2 p-2.5 shadow-2xl rounded-2xl bg-base-200 border border-base-content/15 backdrop-blur-xl z-50"
style={{ width: '200px' }}
>
<RgbaStringColorPicker color={displayColor} onChange={onChange} />
</div>
<Portal topLayer>
<div
ref={popupRef}
dir="ltr"
className="fixed p-2.5 shadow-2xl rounded-2xl bg-base-200 border border-base-content/15 backdrop-blur-xl z-[99999] pointer-events-auto"
style={{
top: `${coords.top}px`,
left: `${coords.left}px`,
width: '220px',
}}
>
{mode === 'hex' ? (
<HexColorPicker color={displayColor} onChange={onChange} />
) : (
<RgbaStringColorPicker
color={displayColor}
onChange={onChange}
/>
)}
</div>
</Portal>
)}
</div>
)
Expand Down
6 changes: 4 additions & 2 deletions src/components/ui/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ export function Modal({
style={
{
'--modal-duration': `${modalDurationMs}ms`,
zIndex: assignedZIndex ?? (customZIndex !== undefined ? customZIndex : BASE_MODAL_Z_INDEX),
zIndex:
assignedZIndex ??
(customZIndex !== undefined ? customZIndex : BASE_MODAL_Z_INDEX),
} as React.CSSProperties
}
>
Expand All @@ -123,7 +125,7 @@ export function Modal({
<button
type="button"
onClick={onClose}
className="flex items-center justify-center transition-all rounded-full cursor-pointer w-7 h-7 md:w-8 md:h-8 bg-base-300 text-muted hover:bg-base-content/10 hover:scale-105 active:scale-95 shrink-0 outline-0! border-0!"
className="flex items-center justify-center transition-all cursor-pointer w-7 h-7 md:w-8 md:h-8 bg-base-300 text-muted hover:bg-base-content/10 hover:scale-105 active:scale-95 shrink-0 outline-0! border-0! rounded-xl"
aria-label="Close modal"
>
<Icon name="close" size={16} className="md:hidden" />
Expand Down
2 changes: 2 additions & 0 deletions src/icons/packs/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
LuHistory,
LuLayers,
LuLayoutTemplate,
LuMinus,
LuPen,
LuPlus,
LuTicket,
Expand Down Expand Up @@ -286,4 +287,5 @@ export const defaultIcons = {
wifiOff: BiWifiOff,
infinity: TbInfinity,
ticket: LuTicket,
minus: LuMinus,
} satisfies IconMap
1 change: 1 addition & 0 deletions src/icons/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,5 @@ export type IconName =
| 'wifiOff'
| 'infinity'
| 'ticket'
| 'minus'
export type IconMap = Record<IconName, IconType>
6 changes: 3 additions & 3 deletions src/layouts/search/browser-bookmark/browser-bookmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ export function BrowserBookmark() {

return (
<div className="relative flex flex-row items-center justify-start w-full gap-2 px-2 py-0.5">
<div className="flex flex-row items-center w-full gap-1 py-0.5 overflow-x-auto no-scrollbar scroll-smooth">
<div className="flex flex-row items-center w-full gap-1 py-0.5 overflow-x-auto scrollbar-none scroll-smooth">
<div className="flex items-center shrink-0">
<button
type="button"
className="flex items-center cursor-pointer group border-none bg-transparent p-0"
className="flex items-center p-0 bg-transparent border-none cursor-pointer group"
onClick={() => onClickToExplorer()}
>
<div className="relative flex items-center justify-center w-fit px-1.5 gap-1 h-6 p-0.5 rounded-xl bg-base-300 group-hover:scale-95 transition-transform">
Expand All @@ -87,7 +87,7 @@ export function BrowserBookmark() {
<div ref={iconRef} className="flex items-center justify-center shrink-0">
<button
type="button"
className="flex items-center cursor-pointer group border-none bg-transparent p-0"
className="flex items-center p-0 bg-transparent border-none cursor-pointer group"
onClick={handleTogglePopover}
>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { safeAwait } from '@/services/api'
import { autoFormatErrorToast, showToast } from '@/common/toast'
import { HABIT_UNIT_STEP } from '@/common/constant/habit-options'
import { useQueryClient } from '@tanstack/react-query'
import { Icon } from '@/src/icons'
import { getHabitUnitLabel } from '../utils'
import { getHabitUnitLabel } from '../../utils'
import { cn } from '@/common/utils/cn'

interface HabitContributionChartProps {
Expand Down Expand Up @@ -224,67 +223,6 @@ export function HabitContributionChart({ habit, color }: HabitContributionChartP

return (
<div className="flex flex-col w-full gap-4 select-none">
<div className="grid grid-cols-2 gap-2 md:grid-cols-4">
<div className="flex flex-col p-2.5 rounded-2xl bg-base-200/80 border border-base-300">
<div className="flex items-center gap-1.5 text-xs text-muted mb-1">
<span className="text-warning">
<Icon name="strike" size={14} />
</span>
<span>استریک فعلی</span>
</div>
<div className="flex items-baseline gap-1">
<span className="text-lg font-bold text-content">
{stats.currentStreak}
</span>
<span className="text-[11px] text-muted">روز</span>
</div>
</div>

<div className="flex flex-col p-2.5 rounded-2xl bg-base-200/80 border border-base-300">
<div className="flex items-center gap-1.5 text-xs text-muted mb-1">
<span className="text-amber-400">
<Icon name="cup" size={14} />
</span>
<span>بهترین رکورد</span>
</div>
<div className="flex items-baseline gap-1">
<span className="text-lg font-bold text-content">
{stats.longestStreak}
</span>
<span className="text-[11px] text-muted">روز</span>
</div>
</div>

<div className="flex flex-col p-2.5 rounded-2xl bg-base-200/80 border border-base-300">
<div className="flex items-center gap-1.5 text-xs text-muted mb-1">
<span className="text-primary">
<Icon name="target" size={14} />
</span>
<span>روزهای موفق</span>
</div>
<div className="flex items-baseline gap-1">
<span className="text-lg font-bold text-content">
{stats.totalCompleted}
</span>
<span className="text-[11px] text-muted">روز</span>
</div>
</div>

<div className="flex flex-col p-2.5 rounded-2xl bg-base-200/80 border border-base-300">
<div className="flex items-center gap-1.5 text-xs text-muted mb-1">
<span className="text-success">
<Icon name="check" size={14} />
</span>
<span>نرخ موفقیت</span>
</div>
<div className="flex items-baseline gap-1">
<span className="text-lg font-bold text-content">
{stats.completionRate}٪
</span>
</div>
</div>
</div>

<div className="flex flex-col p-3 overflow-hidden border rounded-2xl bg-base-200/50 border-base-300/80">
<div className="pb-1 pl-1 overflow-x-auto scrollbar-thin">
<div className="inline-flex flex-col min-w-full gap-1">
Expand Down
Loading
Loading