This commit is contained in:
Generated
+433
-342
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
import { type FC, useState, useEffect } from 'react'
|
import { type FC, type UIEvent, useState, useEffect, useMemo, useRef } from 'react'
|
||||||
import DefaulModal from '@/components/DefaulModal'
|
import DefaulModal from '@/components/DefaulModal'
|
||||||
import type { Icon, IconGroup } from '../types/Types'
|
import type { Icon, IconGroup } from '../types/Types'
|
||||||
import { TickCircle } from 'iconsax-react'
|
import { TickCircle } from 'iconsax-react'
|
||||||
@@ -18,14 +18,64 @@ const IconSelectModal: FC<Props> = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
selectedUrl
|
selectedUrl
|
||||||
}) => {
|
}) => {
|
||||||
|
const ICONS_PAGE_SIZE = 120
|
||||||
const [selectedIconUrl, setSelectedIconUrl] = useState<string | undefined>(
|
const [selectedIconUrl, setSelectedIconUrl] = useState<string | undefined>(
|
||||||
selectedUrl
|
selectedUrl
|
||||||
)
|
)
|
||||||
|
const [visibleIconsCount, setVisibleIconsCount] = useState(ICONS_PAGE_SIZE)
|
||||||
|
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedIconUrl(selectedUrl)
|
setSelectedIconUrl(selectedUrl)
|
||||||
}, [selectedUrl, open])
|
}, [selectedUrl, open])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setVisibleIconsCount(ICONS_PAGE_SIZE)
|
||||||
|
if (scrollContainerRef.current) {
|
||||||
|
scrollContainerRef.current.scrollTop = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [open, icons])
|
||||||
|
|
||||||
|
const totalIcons = useMemo(
|
||||||
|
() => icons.reduce((total, group) => total + group.icons.length, 0),
|
||||||
|
[icons]
|
||||||
|
)
|
||||||
|
|
||||||
|
const hasMoreIcons = visibleIconsCount < totalIcons
|
||||||
|
|
||||||
|
const paginatedGroups = useMemo(() => {
|
||||||
|
let remainingIcons = visibleIconsCount
|
||||||
|
|
||||||
|
return icons
|
||||||
|
.map((group) => {
|
||||||
|
const visibleIcons = group.icons.slice(
|
||||||
|
0,
|
||||||
|
Math.max(0, Math.min(group.icons.length, remainingIcons))
|
||||||
|
)
|
||||||
|
remainingIcons -= visibleIcons.length
|
||||||
|
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
icons: visibleIcons
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((group) => group.icons.length > 0)
|
||||||
|
}, [icons, visibleIconsCount])
|
||||||
|
|
||||||
|
const handleScroll = (event: UIEvent<HTMLDivElement>) => {
|
||||||
|
if (!hasMoreIcons) return
|
||||||
|
|
||||||
|
const target = event.currentTarget
|
||||||
|
const isNearBottom =
|
||||||
|
target.scrollTop + target.clientHeight >= target.scrollHeight - 200
|
||||||
|
|
||||||
|
if (isNearBottom) {
|
||||||
|
setVisibleIconsCount((prev) => Math.min(prev + ICONS_PAGE_SIZE, totalIcons))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleSelect = (url: string) => {
|
const handleSelect = (url: string) => {
|
||||||
setSelectedIconUrl(url)
|
setSelectedIconUrl(url)
|
||||||
onSelect(url)
|
onSelect(url)
|
||||||
@@ -40,7 +90,11 @@ const IconSelectModal: FC<Props> = ({
|
|||||||
title_header="انتخاب آیکون"
|
title_header="انتخاب آیکون"
|
||||||
width={700}
|
width={700}
|
||||||
>
|
>
|
||||||
<div className="max-h-[65vh] overflow-y-auto py-2">
|
<div
|
||||||
|
ref={scrollContainerRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
className="max-h-[65vh] overflow-y-auto py-2"
|
||||||
|
>
|
||||||
{icons.length === 0 ? (
|
{icons.length === 0 ? (
|
||||||
<div className="text-center py-12 text-gray-500">
|
<div className="text-center py-12 text-gray-500">
|
||||||
<div className="text-base mb-2">آیکونی یافت نشد</div>
|
<div className="text-base mb-2">آیکونی یافت نشد</div>
|
||||||
@@ -50,7 +104,7 @@ const IconSelectModal: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{icons.map((group) => (
|
{paginatedGroups.map((group) => (
|
||||||
<div key={group.id} className="border-b border-gray-100 last:border-0 pb-6 last:pb-0">
|
<div key={group.id} className="border-b border-gray-100 last:border-0 pb-6 last:pb-0">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<div className="flex items-center gap-2 mb-4">
|
||||||
<h3 className="text-base font-semibold text-gray-800">
|
<h3 className="text-base font-semibold text-gray-800">
|
||||||
@@ -78,6 +132,8 @@ const IconSelectModal: FC<Props> = ({
|
|||||||
<img
|
<img
|
||||||
src={icon.url}
|
src={icon.url}
|
||||||
alt="icon"
|
alt="icon"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
className="w-full h-full object-contain transition-opacity duration-200 max-w-full max-h-full"
|
className="w-full h-full object-contain transition-opacity duration-200 max-w-full max-h-full"
|
||||||
style={{
|
style={{
|
||||||
imageRendering: 'auto',
|
imageRendering: 'auto',
|
||||||
@@ -106,6 +162,11 @@ const IconSelectModal: FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{hasMoreIcons && (
|
||||||
|
<div className="text-center text-sm text-gray-500 py-2">
|
||||||
|
در حال بارگذاری آیکون های بیشتر...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user