Standardize list empty UI, searchable country fields, customer-products filter row, password/login polish, and one-time business dashboard handoff from the customer app. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import { ChevronLeft, ChevronRight, Images, Plus } from 'lucide-react'
|
|
import { useRef } from 'react'
|
|
import { useLocale } from '@meshkee/dashboard-ui'
|
|
import { useT } from '../i18n/useT'
|
|
import type { WebsiteSliderSlide } from '../types/websiteSlider'
|
|
import { EmptyState } from './EmptyState'
|
|
import { Tooltip } from './Tooltip'
|
|
import { WebsiteSliderSlideCard } from './WebsiteSliderSlideCard'
|
|
import styles from './WebsiteSliderGallery.module.css'
|
|
|
|
interface WebsiteSliderGalleryProps {
|
|
slides: WebsiteSliderSlide[]
|
|
onAddSlide: () => void
|
|
onRemoveSlide: (slide: WebsiteSliderSlide) => void
|
|
showAdd?: boolean
|
|
addLabel?: string
|
|
aspectRatio?: string | null
|
|
removeLabel?: string
|
|
}
|
|
|
|
export function WebsiteSliderGallery({
|
|
slides,
|
|
onAddSlide,
|
|
onRemoveSlide,
|
|
showAdd = true,
|
|
addLabel,
|
|
aspectRatio,
|
|
removeLabel,
|
|
}: WebsiteSliderGalleryProps) {
|
|
const t = useT()
|
|
const { locale } = useLocale()
|
|
const isFa = locale === 'fa'
|
|
const trackRef = useRef<HTMLDivElement>(null)
|
|
const showNav = slides.length > 0
|
|
const addText = addLabel ?? t('website.sliders.addSlide')
|
|
|
|
if (slides.length === 0) {
|
|
return (
|
|
<EmptyState
|
|
icon={Images}
|
|
message={t('website.sliders.empty')}
|
|
actionLabel={showAdd ? addText : undefined}
|
|
onAction={showAdd ? onAddSlide : undefined}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function scrollBy(direction: -1 | 1) {
|
|
const track = trackRef.current
|
|
if (!track) return
|
|
const amount = Math.max(track.clientWidth * 0.75, 280)
|
|
track.scrollBy({ left: direction * amount, behavior: 'smooth' })
|
|
}
|
|
|
|
const addCard = showAdd ? (
|
|
<div className={`${styles.slideSlot} ${styles.slideSlotAdd}`}>
|
|
<Tooltip label={addText}>
|
|
<button
|
|
type="button"
|
|
className={styles.addCard}
|
|
onClick={onAddSlide}
|
|
aria-label={addText}
|
|
>
|
|
<span className={styles.addIcon}>
|
|
<Plus size={28} />
|
|
</span>
|
|
</button>
|
|
</Tooltip>
|
|
</div>
|
|
) : null
|
|
|
|
const slideCards = slides.map((slide) => (
|
|
<div key={slide.id} className={styles.slideSlot}>
|
|
<WebsiteSliderSlideCard
|
|
slide={slide}
|
|
onRemove={() => onRemoveSlide(slide)}
|
|
aspectRatio={aspectRatio}
|
|
removeLabel={removeLabel}
|
|
/>
|
|
</div>
|
|
))
|
|
|
|
return (
|
|
<section className={styles.section}>
|
|
{showNav && (
|
|
<div className={styles.header}>
|
|
<div className={styles.navPair} dir="ltr">
|
|
<Tooltip label={t('website.scrollLeft')}>
|
|
<button
|
|
type="button"
|
|
className={styles.navBtn}
|
|
onClick={() => scrollBy(-1)}
|
|
aria-label={t('website.scrollLeft')}
|
|
>
|
|
<ChevronLeft size={18} />
|
|
</button>
|
|
</Tooltip>
|
|
<Tooltip label={t('website.scrollRight')}>
|
|
<button
|
|
type="button"
|
|
className={styles.navBtn}
|
|
onClick={() => scrollBy(1)}
|
|
aria-label={t('website.scrollRight')}
|
|
>
|
|
<ChevronRight size={18} />
|
|
</button>
|
|
</Tooltip>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className={styles.trackWrap}>
|
|
<div className={styles.track} ref={trackRef} dir={isFa ? 'rtl' : 'ltr'}>
|
|
{slideCards}
|
|
{addCard}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|