Skip to content
This repository has been archived by the owner on Jul 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #155 from turistikrota/feature/calendar-v2
Browse files Browse the repository at this point in the history
Feature/calendar v2
  • Loading branch information
9ssi7 authored Dec 31, 2023
2 parents d385a19 + b08d6f8 commit 13d691a
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 75 deletions.
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@turistikrota/ui",
"version": "1.0.9",
"version": "1.1.1",
"description": "the turistikrota ui library for React",
"main": "./index.js",
"module": "./index.js",
Expand Down
21 changes: 21 additions & 0 deletions packages/ui/src/carousel/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { useAgentMobile } from '../hooks/dom'
import { CarouselButtonProps } from './carousel.types'

const CarouselButton: React.FC<CarouselButtonProps> = ({ position, onClick }) => {
const isMobile = useAgentMobile()
const icon = position === 'left' ? 'bx-chevron-left' : 'bx-chevron-right'
const left = position === 'left' ? 'left-2' : 'right-2'
return (
<button
className={`${
!isMobile ? 'flex' : 'hidden'
} invisible absolute top-1/2 opacity-0 transition-all duration-200 group-hover:visible group-hover:opacity-90 group-hover:hover:opacity-100 ${left} shadow-xs bg-second h-8 w-8 -translate-y-1/2 transform items-center justify-center rounded-full border bg-opacity-95 text-gray-600 dark:bg-opacity-10 dark:text-white`}
onClick={onClick}
>
<i className={`bx bx-sm ${icon}`} />
</button>
)
}

export default CarouselButton
80 changes: 80 additions & 0 deletions packages/ui/src/carousel/carousel.design.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { PreviewComponent } from './carousel.types'
import CarouselSidePreview from './side-preview'
import CarouselSubPreview from './sub-preview'

export enum CarouselVariant {
Map = 'map',
List = 'list',
Detail = 'detail',
DetailHorizontal = 'detail-horizontal',
DetailVertical = 'detail-vertical',
}

export type PreviewStyles = {
provider: string
item: string
}

export type CarouselStyles = {
container: string
provider: string
item: string
itemLoading: string
preview: PreviewStyles
Preview?: PreviewComponent
}

export const CarouselVariants: Record<CarouselVariant, CarouselStyles> = {
[CarouselVariant.Map]: {
container: '',
provider: 'h-75 w-75',
item: 'rounded-b-none',
itemLoading: '',
preview: {
provider: '',
item: '',
},
},
[CarouselVariant.List]: {
container: '',
provider: 'h-72',
item: 'rounded-b-none',
itemLoading: 'rounded-t-md',
preview: {
provider: '',
item: '',
},
},
[CarouselVariant.DetailHorizontal]: {
container: 'grid grid-cols-12 gap-2',
provider: 'h-104 col-span-12 lg:col-span-9',
item: '',
itemLoading: '',
preview: {
provider: 'col-span-12 lg:col-span-3 grid-rows-1 lg:grid-rows-3',
item: 'col-span-3 lg:col-span-6 row-span-1 lg:row-span-1',
},
Preview: CarouselSidePreview,
},
[CarouselVariant.DetailVertical]: {
container: '',
provider: 'h-104',
item: '',
itemLoading: '',
preview: {
provider: '',
item: '',
},
Preview: CarouselSubPreview,
},
[CarouselVariant.Detail]: {
container: '',
provider: 'h-104',
item: '',
itemLoading: '',
preview: {
provider: '',
item: '',
},
},
}
34 changes: 34 additions & 0 deletions packages/ui/src/carousel/carousel.types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CarouselVariant, PreviewStyles } from './carousel.design'

export type CarouselProps = {
images: string[]
variant?: CarouselVariant
imageAltPrefix: string
imageTitlePrefix?: string
activeIndex?: number
onClick?: (image: string, idx: number) => void
autoPlay?: boolean
autoPlayInterval?: number
showDots?: boolean
showPreview?: boolean
}

export type CarouselButtonProps = {
position: 'left' | 'right'
onClick: () => void
}

export type PreviewProps = {
styles?: PreviewStyles
images: string[]
imageTitlePrefix?: string
currentIndex: number
setCurrentIndex: (idx: number) => void
onClick?: (image: string, idx: number) => void
}

export type PreviewComponent = React.FC<PreviewProps>

export type CarouselComponent = React.FC<CarouselProps> & {
Variants: typeof CarouselVariant
}
107 changes: 33 additions & 74 deletions packages/ui/src/carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,28 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { useInterval } from '../hooks/async'
import { useAgentMobile } from '../hooks/dom'
import PerfectImage from '../image/perfect'
import CarouselButton from './button'
import { CarouselStyles, CarouselVariant, CarouselVariants } from './carousel.design'
import { CarouselComponent } from './carousel.types'
import DotRenderer from './dot-renderer'

type Props = {
images: string[]
imageAltPrefix: string
imageTitlePrefix?: string
imageClassName?: string
imgLoadingClassName?: string
pictureClassName?: string
sizeClassName: string
activeIndex?: number
className?: string
onClick?: (image: string, idx: number) => void
autoPlay?: boolean
autoPlayInterval?: number
showDots?: boolean
showSubImages?: boolean
}

type ButtonProps = {
position: 'left' | 'right'
onClick: () => void
}

const CarouselButton: React.FC<ButtonProps> = ({ position, onClick }) => {
const isMobile = useAgentMobile()
const icon = position === 'left' ? 'bx-chevron-left' : 'bx-chevron-right'
const left = position === 'left' ? 'left-2' : 'right-2'
return (
<button
className={`${
!isMobile ? 'flex' : 'hidden'
} invisible absolute top-1/2 opacity-0 transition-all duration-200 group-hover:visible group-hover:opacity-90 group-hover:hover:opacity-100 ${left} shadow-xs bg-second h-8 w-8 -translate-y-1/2 transform items-center justify-center rounded-full border bg-opacity-95 text-gray-600 dark:bg-opacity-10 dark:text-white`}
onClick={onClick}
>
<i className={`bx bx-sm ${icon}`} />
</button>
)
}

const Carousel: React.FC<Props> = ({
const Carousel: CarouselComponent = ({
images,
imageTitlePrefix,
imageAltPrefix,
sizeClassName,
imageClassName,
pictureClassName,
imgLoadingClassName = 'rounded-md',
className,
variant = CarouselVariant.List,
onClick,
showDots = true,
showSubImages = false,
showPreview = false,
autoPlay = false,
autoPlayInterval = 5000,
activeIndex = 0,
}) => {
const [indexes, setIndexes] = useState([0, 0, 2])
const [currentIndex, setCurrentIndex] = useState(activeIndex)

const styles = useMemo<CarouselStyles>(() => CarouselVariants[variant], [variant])

useInterval(
() => {
goToNextSlide()
Expand Down Expand Up @@ -113,8 +75,8 @@ const Carousel: React.FC<Props> = ({
}

return (
<div className={`group w-full ${className ? className : ''}`} onClick={checkImageClick}>
<div className={`relative ${sizeClassName}`}>
<div className={`group w-full ${styles.container}`} onClick={checkImageClick}>
<div className={`relative col-span-10 ${styles.provider}`}>
{images.map((img, idx) => (
<PerfectImage
key={idx}
Expand All @@ -124,9 +86,9 @@ const Carousel: React.FC<Props> = ({
isActive={idx === currentIndex}
className={`absolute left-0 top-0 h-full w-full object-cover transition-opacity duration-200 ${
idx === currentIndex ? 'opacity-100' : 'cursor-pointer opacity-0'
} ${pictureClassName ? pictureClassName : ''}`}
imgClassName={`rounded-md ${imageClassName ? imageClassName : ''}`}
loadingClassName={imgLoadingClassName}
}`}
imgClassName={`rounded-md ${styles.item}`}
loadingClassName={styles.itemLoading}
onLeftSwipe={checkLeftSwipe}
onRightSwipe={checkRightSwipe}
/>
Expand All @@ -135,31 +97,28 @@ const Carousel: React.FC<Props> = ({
{currentIndex !== 0 && <CarouselButton position='left' onClick={goToPreviousSlide} />}
{currentIndex !== images.length - 1 && <CarouselButton position='right' onClick={goToNextSlide} />}
</div>
{showSubImages && (
<div className='mt-2 flex justify-start gap-1 overflow-x-auto pb-2'>
{images.map((img, idx) => (
<div key={idx} className='max-w-12 relative h-12 max-h-12 min-h-[3rem] w-12 min-w-[3rem]'>
<PerfectImage
src={img}
full={false}
alt={``}
title={imageTitlePrefix ? `${imageTitlePrefix}-${idx}` : undefined}
imgClassName='rounded-md w-full h-full'
loadingClassName='rounded-md w-full h-full'
className={`max-w-12 h-12 max-h-12 min-h-[3rem] w-12 min-w-[3rem] rounded-md object-cover transition-opacity duration-200 ${
idx === currentIndex ? 'opacity-100' : 'cursor-pointer opacity-50'
}`}
onClick={(e) => {
setCurrentIndex(idx)
e.stopPropagation()
}}
/>
</div>
))}
</div>
{showPreview && styles.Preview && (
<styles.Preview
currentIndex={currentIndex}
imageTitlePrefix={imageTitlePrefix}
images={images}
styles={styles.preview}
onClick={onClick}
setCurrentIndex={(idx) => {
setIndexes(([fromIndex, currentIndex, toIndex]) => {
if (idx === currentIndex) return [idx - 1, idx, idx + 1]
if (idx === fromIndex) return [idx - 1, idx, idx + 1]
if (idx === toIndex) return [idx - 1, idx, idx + 1]
return [idx - 1, idx, idx + 1]
})
setCurrentIndex(idx)
}}
/>
)}
</div>
)
}

Carousel.Variants = CarouselVariant

export default Carousel
67 changes: 67 additions & 0 deletions packages/ui/src/carousel/side-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { FC, useMemo } from 'react'
import { useIsDesktop } from '../hooks/dom'
import PerfectImage from '../image/perfect'
import { PreviewProps } from './carousel.types'

const CarouselSidePreview: FC<PreviewProps> = ({
styles,
images,
imageTitlePrefix,
currentIndex,
setCurrentIndex,
onClick,
}) => {
const isDesktop = useIsDesktop()
const max = useMemo<number>(() => (isDesktop ? 6 : 4), [isDesktop])
const [firstImages, totalCount] = useMemo(() => {
if (images.length > max) return [images.slice(0, max - 1), images.length]
return [images, images.length]
}, [images, max])

return (
<div className={`grid grid-cols-12 gap-2 ${styles ? styles.provider : ''}`}>
{firstImages.map((img, idx) => (
<div key={idx} className={`${styles ? styles.item : ''}`}>
<PerfectImage
src={img}
full={false}
alt={``}
title={imageTitlePrefix ? `${imageTitlePrefix}-${idx}` : undefined}
imgClassName='rounded-md w-full h-full'
loadingClassName='rounded-md w-full h-full'
className={`rounded-md object-cover transition-opacity duration-200 ${
idx === currentIndex ? 'opacity-100' : 'cursor-pointer opacity-50'
}`}
onClick={(e) => {
setCurrentIndex(idx)
e.stopPropagation()
}}
/>
</div>
))}
{totalCount > max && (
<div className={`relative ${styles ? styles.item : ''}`}>
<div className='absolute inset-0 flex items-center justify-center bg-white bg-opacity-100 dark:bg-black'>
<span className='text-2xl font-bold text-black dark:text-white lg:text-4xl'>{totalCount - max}+</span>
</div>
<PerfectImage
src={images[max - 1]}
full={false}
alt={``}
title={imageTitlePrefix ? `${imageTitlePrefix}-${5}` : undefined}
imgClassName='rounded-md w-full h-full'
loadingClassName='rounded-md w-full h-full'
className={`cursor-pointer rounded-md object-cover opacity-20 transition-opacity duration-200`}
onClick={(e) => {
if (onClick) onClick(images[max - 1], max - 1)
else setCurrentIndex(5)
e.stopPropagation()
}}
/>
</div>
)}
</div>
)
}

export default CarouselSidePreview
34 changes: 34 additions & 0 deletions packages/ui/src/carousel/sub-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { FC } from 'react'
import PerfectImage from '../image/perfect'
import { PreviewProps } from './carousel.types'

const CarouselSubPreview: FC<PreviewProps> = ({ styles, images, imageTitlePrefix, currentIndex, setCurrentIndex }) => {
return (
<div className={`mt-2 flex justify-start gap-1 overflow-x-auto pb-2 ${styles ? styles.provider : ''}`}>
{images.map((img, idx) => (
<div
key={idx}
className={`max-w-12 relative h-12 max-h-12 min-h-[3rem] w-12 min-w-[3rem] ${styles ? styles.item : ''}`}
>
<PerfectImage
src={img}
full={false}
alt={``}
title={imageTitlePrefix ? `${imageTitlePrefix}-${idx}` : undefined}
imgClassName='rounded-md w-full h-full'
loadingClassName='rounded-md w-full h-full'
className={`max-w-12 h-12 max-h-12 min-h-[3rem] w-12 min-w-[3rem] rounded-md object-cover transition-opacity duration-200 ${
idx === currentIndex ? 'opacity-100' : 'cursor-pointer opacity-50'
}`}
onClick={(e) => {
setCurrentIndex(idx)
e.stopPropagation()
}}
/>
</div>
))}
</div>
)
}

export default CarouselSubPreview

0 comments on commit 13d691a

Please sign in to comment.