webcoreui 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -227
- package/astro.d.ts +6 -0
- package/astro.js +6 -0
- package/components/Avatar/Avatar.astro +9 -2
- package/components/Avatar/Avatar.svelte +3 -1
- package/components/Avatar/Avatar.tsx +4 -2
- package/components/Avatar/avatar.ts +1 -0
- package/components/Button/button.module.scss +6 -1
- package/components/Button/button.ts +2 -2
- package/components/Carousel/Carousel.astro +198 -0
- package/components/Carousel/Carousel.svelte +161 -0
- package/components/Carousel/Carousel.tsx +172 -0
- package/components/Carousel/carousel.module.scss +58 -0
- package/components/Carousel/carousel.ts +26 -0
- package/components/DataTable/DataTable.astro +332 -0
- package/components/DataTable/DataTable.svelte +272 -0
- package/components/DataTable/DataTable.tsx +287 -0
- package/components/DataTable/datatable.module.scss +102 -0
- package/components/DataTable/datatable.ts +41 -0
- package/components/Icon/map.ts +6 -0
- package/components/Input/input.module.scss +6 -0
- package/components/List/List.astro +1 -1
- package/components/List/List.svelte +1 -1
- package/components/List/List.tsx +1 -2
- package/components/Pagination/Pagination.astro +189 -0
- package/components/Pagination/Pagination.svelte +144 -0
- package/components/Pagination/Pagination.tsx +162 -0
- package/components/Pagination/pagination.module.scss +49 -0
- package/components/Pagination/pagination.ts +35 -0
- package/components/Select/Select.astro +8 -4
- package/components/Select/Select.svelte +15 -6
- package/components/Select/Select.tsx +15 -8
- package/components/Select/select.ts +7 -2
- package/components/Table/Table.svelte +1 -1
- package/components/Table/table.ts +1 -1
- package/icons/arrow-left.svg +3 -0
- package/icons/arrow-right.svg +3 -0
- package/icons/order.svg +3 -0
- package/icons.d.ts +3 -0
- package/icons.js +3 -0
- package/index.d.ts +6 -6
- package/package.json +11 -9
- package/react.d.ts +6 -0
- package/react.js +6 -0
- package/scss/resets.scss +27 -1
- package/svelte.d.ts +6 -0
- package/svelte.js +6 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { CarouselProps } from './carousel'
|
|
3
|
+
|
|
4
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.astro'
|
|
5
|
+
import Pagination from '../Pagination/Pagination.astro'
|
|
6
|
+
import Progress from '../Progress/Progress.astro'
|
|
7
|
+
|
|
8
|
+
import { classNames } from '../../utils/classNames'
|
|
9
|
+
|
|
10
|
+
import styles from './carousel.module.scss'
|
|
11
|
+
|
|
12
|
+
interface Props extends CarouselProps {}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
items,
|
|
16
|
+
visibleItems = 1,
|
|
17
|
+
subText,
|
|
18
|
+
scrollSnap = true,
|
|
19
|
+
progress,
|
|
20
|
+
pagination,
|
|
21
|
+
effect,
|
|
22
|
+
debounce = 20,
|
|
23
|
+
className,
|
|
24
|
+
wrapperClassName,
|
|
25
|
+
paginationClassName
|
|
26
|
+
} = Astro.props
|
|
27
|
+
|
|
28
|
+
const classes = [
|
|
29
|
+
styles.carousel,
|
|
30
|
+
className
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
const containerClasses = [
|
|
34
|
+
styles.container,
|
|
35
|
+
scrollSnap && styles.snap
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const wrapperClasses = [
|
|
39
|
+
styles.wrapper,
|
|
40
|
+
effect && styles[effect],
|
|
41
|
+
wrapperClassName
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
const paginationWrapperClasses = [
|
|
45
|
+
styles['pagination-wrapper'],
|
|
46
|
+
paginationClassName
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
const paginationClasses = classNames([
|
|
50
|
+
styles.pagination,
|
|
51
|
+
!subText && paginationClassName
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const totalPages = Math.ceil(items / visibleItems)
|
|
55
|
+
const subTextValue = subText?.match(/\{0\}|\{1\}/g) ? subText : undefined
|
|
56
|
+
const style = visibleItems > 1
|
|
57
|
+
? `--w-slide-width: ${100 / visibleItems}%;`
|
|
58
|
+
: null
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
<section class:list={classes}>
|
|
62
|
+
<div
|
|
63
|
+
class:list={containerClasses}
|
|
64
|
+
data-id="w-carousel"
|
|
65
|
+
data-debounce={debounce !== 20 && debounce}
|
|
66
|
+
>
|
|
67
|
+
<ul
|
|
68
|
+
class:list={wrapperClasses}
|
|
69
|
+
style={style}
|
|
70
|
+
data-visible-items={visibleItems > 1 ? visibleItems : null}
|
|
71
|
+
>
|
|
72
|
+
<slot />
|
|
73
|
+
</ul>
|
|
74
|
+
</div>
|
|
75
|
+
<ConditionalWrapper condition={!!(subText || progress)}>
|
|
76
|
+
<div slot="wrapper" class:list={paginationWrapperClasses}>children</div>
|
|
77
|
+
{progress && (
|
|
78
|
+
<Progress
|
|
79
|
+
className="w-carousel-progress"
|
|
80
|
+
value={0}
|
|
81
|
+
/>
|
|
82
|
+
)}
|
|
83
|
+
<Pagination
|
|
84
|
+
type="arrows"
|
|
85
|
+
{...pagination}
|
|
86
|
+
totalPages={totalPages}
|
|
87
|
+
className={paginationClasses}
|
|
88
|
+
/>
|
|
89
|
+
{subText && (
|
|
90
|
+
<span class={styles.subtext} data-text={subTextValue}>
|
|
91
|
+
{subText
|
|
92
|
+
.replace('{0}', '1')
|
|
93
|
+
.replace('{1}', String(totalPages))
|
|
94
|
+
}
|
|
95
|
+
</span>
|
|
96
|
+
)}
|
|
97
|
+
</ConditionalWrapper>
|
|
98
|
+
</section>
|
|
99
|
+
|
|
100
|
+
<script>
|
|
101
|
+
import { debounce } from '../../utils/debounce'
|
|
102
|
+
import { listen } from '../../utils/event'
|
|
103
|
+
|
|
104
|
+
const carousels = Array.from(document.querySelectorAll('[data-id="w-carousel"]'))
|
|
105
|
+
|
|
106
|
+
const scroll = (event: Event) => {
|
|
107
|
+
const target = event.target as HTMLDivElement
|
|
108
|
+
|
|
109
|
+
if (!target.dataset.paginated) {
|
|
110
|
+
const scrollLeft = target.scrollLeft
|
|
111
|
+
const itemWidth = target.children[0].clientWidth
|
|
112
|
+
const page = Math.round(scrollLeft / itemWidth) + 1
|
|
113
|
+
const carouselElement = target.children[0]
|
|
114
|
+
const paginationElement = target.parentElement
|
|
115
|
+
?.querySelector('[data-id="w-pagination"]') as HTMLUListElement
|
|
116
|
+
const currentPage = Number(paginationElement.dataset.currentPage)
|
|
117
|
+
const diff = Math.abs(currentPage - page)
|
|
118
|
+
|
|
119
|
+
let triggerButton = currentPage > page
|
|
120
|
+
? paginationElement.querySelector('[data-page="prev"]') as HTMLButtonElement
|
|
121
|
+
: paginationElement.querySelector('[data-page="next"]') as HTMLButtonElement
|
|
122
|
+
|
|
123
|
+
if (!triggerButton) {
|
|
124
|
+
triggerButton = paginationElement.querySelector(`[data-page="${page}"]`) as HTMLButtonElement
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (let i = 0; i < diff; i++) {
|
|
128
|
+
triggerButton.click()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Array.from(carouselElement.children).forEach(li => {
|
|
132
|
+
(li as HTMLLIElement).removeAttribute('data-active')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const activeElement = carouselElement.children[page - 1] as HTMLLIElement
|
|
136
|
+
|
|
137
|
+
activeElement.dataset.active = 'true'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
carousels.forEach(carousel => {
|
|
142
|
+
const carouselElement = carousel as HTMLDivElement
|
|
143
|
+
const debounceAmount = carouselElement.dataset.debounce
|
|
144
|
+
? Number(carouselElement.dataset.debounce)
|
|
145
|
+
: 20
|
|
146
|
+
|
|
147
|
+
carousel.addEventListener('scroll', debounce((event: Event) => {
|
|
148
|
+
scroll(event)
|
|
149
|
+
}, debounceAmount))
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
listen('paginate', event => {
|
|
153
|
+
const target = event.target
|
|
154
|
+
const carousel = target.closest('section').querySelector('[data-id="w-carousel"] ul')
|
|
155
|
+
|
|
156
|
+
if (!carousel) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const progress = target.closest('section').querySelector('.w-carousel-progress')
|
|
161
|
+
const progressValue = (100 / (Number(target.dataset.totalPages) - 1))
|
|
162
|
+
const visibleItems = Number(carousel.dataset.visibleItems) || 0
|
|
163
|
+
const pageIndex = (event.page + visibleItems) - 1
|
|
164
|
+
|
|
165
|
+
const liElement = carousel.children[pageIndex]
|
|
166
|
+
const subText = event.target.nextElementSibling
|
|
167
|
+
|
|
168
|
+
Array.from(carousel.children).forEach(li => {
|
|
169
|
+
(li as HTMLLIElement).removeAttribute('data-active')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
if (subText?.dataset.text) {
|
|
173
|
+
subText.innerText = subText.dataset.text
|
|
174
|
+
.replace('{0}', event.page)
|
|
175
|
+
.replace('{1}', target.dataset.totalPages)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (progress) {
|
|
179
|
+
const updatedProgress = progressValue * (Number(event.page) - 1)
|
|
180
|
+
|
|
181
|
+
progress.children[0]
|
|
182
|
+
.style.setProperty('--w-progress-width', `${updatedProgress}%`)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (event.trusted) {
|
|
186
|
+
const carouselContaienr = target.closest('section').querySelector('[data-id="w-carousel"]')
|
|
187
|
+
|
|
188
|
+
liElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
189
|
+
liElement.dataset.active = 'true'
|
|
190
|
+
|
|
191
|
+
carouselContaienr.dataset.paginated = 'true'
|
|
192
|
+
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
carouselContaienr.removeAttribute('data-paginated')
|
|
195
|
+
}, 300)
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
</script>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte'
|
|
3
|
+
import type { SvelteCarouselProps } from './carousel'
|
|
4
|
+
|
|
5
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.svelte'
|
|
6
|
+
import Pagination from '../Pagination/Pagination.svelte'
|
|
7
|
+
import Progress from '../Progress/Progress.svelte'
|
|
8
|
+
|
|
9
|
+
import { classNames } from '../../utils/classNames'
|
|
10
|
+
import { debounce as debounceScroll } from '../../utils/debounce'
|
|
11
|
+
|
|
12
|
+
import styles from './carousel.module.scss'
|
|
13
|
+
|
|
14
|
+
import type { PaginationEventType } from '../Pagination/pagination'
|
|
15
|
+
|
|
16
|
+
export let items: SvelteCarouselProps['items'] = 0
|
|
17
|
+
export let visibleItems: SvelteCarouselProps['visibleItems'] = 1
|
|
18
|
+
export let subText: SvelteCarouselProps['subText'] = ''
|
|
19
|
+
export let scrollSnap: SvelteCarouselProps['scrollSnap'] = true
|
|
20
|
+
export let progress: SvelteCarouselProps['progress'] = false
|
|
21
|
+
export let pagination: SvelteCarouselProps['pagination'] = {}
|
|
22
|
+
export let effect: SvelteCarouselProps['effect'] = null
|
|
23
|
+
export let debounce: SvelteCarouselProps['debounce'] = 20
|
|
24
|
+
export let className: SvelteCarouselProps['className'] = ''
|
|
25
|
+
export let wrapperClassName: SvelteCarouselProps['wrapperClassName'] = ''
|
|
26
|
+
export let paginationClassName: SvelteCarouselProps['paginationClassName'] = ''
|
|
27
|
+
export let onScroll: SvelteCarouselProps['onScroll'] = () => {}
|
|
28
|
+
|
|
29
|
+
let carouselContainer: HTMLDivElement
|
|
30
|
+
let carousel: HTMLUListElement
|
|
31
|
+
let carouselItems: HTMLCollection | NodeListOf<HTMLLIElement>
|
|
32
|
+
let progressValue = 0
|
|
33
|
+
let paginated = false
|
|
34
|
+
let currentPage = 1
|
|
35
|
+
|
|
36
|
+
const classes = classNames([
|
|
37
|
+
styles.carousel,
|
|
38
|
+
className
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
const containerClasses = classNames([
|
|
42
|
+
styles.container,
|
|
43
|
+
scrollSnap && styles.snap
|
|
44
|
+
])
|
|
45
|
+
|
|
46
|
+
const wrapperClasses = classNames([
|
|
47
|
+
styles.wrapper,
|
|
48
|
+
effect && styles[effect],
|
|
49
|
+
wrapperClassName
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
const paginationWrapperClasses = classNames([
|
|
53
|
+
styles['pagination-wrapper'],
|
|
54
|
+
paginationClassName
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
const paginationClasses = classNames([
|
|
58
|
+
styles.pagination,
|
|
59
|
+
!subText && paginationClassName
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
const totalPages = Math.ceil(items / visibleItems!)
|
|
63
|
+
const subTextValue = subText?.match(/\{0\}|\{1\}/g) ? subText : undefined
|
|
64
|
+
const style = visibleItems! > 1
|
|
65
|
+
? `--w-slide-width: ${100 / visibleItems!}%;`
|
|
66
|
+
: null
|
|
67
|
+
|
|
68
|
+
const updateValues = () => {
|
|
69
|
+
const activeElement = carouselItems[currentPage - 1] as HTMLLIElement
|
|
70
|
+
|
|
71
|
+
Array.from(carouselItems).forEach(li => li.removeAttribute('data-active'))
|
|
72
|
+
activeElement.dataset.active = 'true'
|
|
73
|
+
|
|
74
|
+
if (subTextValue) {
|
|
75
|
+
subText = subTextValue
|
|
76
|
+
.replace('{0}', String(currentPage))
|
|
77
|
+
.replace('{1}', String(totalPages))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (progress) {
|
|
81
|
+
const percentage = (100 / (totalPages - 1))
|
|
82
|
+
|
|
83
|
+
progressValue = percentage * (currentPage - 1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onScroll?.(currentPage)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const scroll = debounceScroll((event: Event) => {
|
|
90
|
+
if (!paginated) {
|
|
91
|
+
const target = event.target as HTMLDivElement
|
|
92
|
+
const scrollLeft = target.scrollLeft
|
|
93
|
+
const itemWidth = target.children[0].clientWidth
|
|
94
|
+
const page = Math.round(scrollLeft / itemWidth) + 1
|
|
95
|
+
|
|
96
|
+
currentPage = page
|
|
97
|
+
|
|
98
|
+
updateValues()
|
|
99
|
+
}
|
|
100
|
+
}, debounce)
|
|
101
|
+
|
|
102
|
+
const paginate = (event: PaginationEventType) => {
|
|
103
|
+
const liElement = carouselItems[event.page - 1] as HTMLLIElement
|
|
104
|
+
|
|
105
|
+
liElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
106
|
+
|
|
107
|
+
currentPage = event.page
|
|
108
|
+
paginated = true
|
|
109
|
+
|
|
110
|
+
updateValues()
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
paginated = false
|
|
113
|
+
}, 300)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
onMount(() => {
|
|
117
|
+
const usedInAstro = carousel.children[0].nodeName === 'ASTRO-SLOT'
|
|
118
|
+
|
|
119
|
+
carouselContainer.addEventListener('scroll', scroll)
|
|
120
|
+
|
|
121
|
+
carouselItems = usedInAstro
|
|
122
|
+
? carousel.querySelectorAll('li')
|
|
123
|
+
: carousel.children
|
|
124
|
+
|
|
125
|
+
return () => {
|
|
126
|
+
carouselContainer.removeEventListener('scroll', scroll)
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<section class={classes}>
|
|
132
|
+
<div class={containerClasses} bind:this={carouselContainer}>
|
|
133
|
+
<ul class={wrapperClasses} style={style} bind:this={carousel}>
|
|
134
|
+
<slot />
|
|
135
|
+
</ul>
|
|
136
|
+
</div>
|
|
137
|
+
<ConditionalWrapper
|
|
138
|
+
condition={!!(subText || progress)}
|
|
139
|
+
class={paginationWrapperClasses}
|
|
140
|
+
>
|
|
141
|
+
{#if progress}
|
|
142
|
+
<Progress value={progressValue} />
|
|
143
|
+
{/if}
|
|
144
|
+
<Pagination
|
|
145
|
+
type="arrows"
|
|
146
|
+
{...pagination}
|
|
147
|
+
currentPage={currentPage}
|
|
148
|
+
totalPages={totalPages}
|
|
149
|
+
className={paginationClasses}
|
|
150
|
+
onChange={paginate}
|
|
151
|
+
/>
|
|
152
|
+
{#if subText}
|
|
153
|
+
<span class={styles.subtext}>
|
|
154
|
+
{subText
|
|
155
|
+
.replace('{0}', '1')
|
|
156
|
+
.replace('{1}', String(totalPages))
|
|
157
|
+
}
|
|
158
|
+
</span>
|
|
159
|
+
{/if}
|
|
160
|
+
</ConditionalWrapper>
|
|
161
|
+
</section>
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react'
|
|
2
|
+
import type { ReactCarouselProps } from './carousel'
|
|
3
|
+
|
|
4
|
+
import ConditionalWrapper from '../ConditionalWrapper/ConditionalWrapper.tsx'
|
|
5
|
+
import Pagination from '../Pagination/Pagination.tsx'
|
|
6
|
+
import Progress from '../Progress/Progress.tsx'
|
|
7
|
+
|
|
8
|
+
import { classNames } from '../../utils/classNames'
|
|
9
|
+
import { debounce as debounceScroll } from '../../utils/debounce'
|
|
10
|
+
|
|
11
|
+
import styles from './carousel.module.scss'
|
|
12
|
+
|
|
13
|
+
import type { PaginationEventType } from '../Pagination/pagination'
|
|
14
|
+
|
|
15
|
+
const Carousel = ({
|
|
16
|
+
items,
|
|
17
|
+
visibleItems = 1,
|
|
18
|
+
subText,
|
|
19
|
+
scrollSnap = true,
|
|
20
|
+
progress,
|
|
21
|
+
pagination,
|
|
22
|
+
effect,
|
|
23
|
+
debounce = 20,
|
|
24
|
+
className,
|
|
25
|
+
wrapperClassName,
|
|
26
|
+
paginationClassName,
|
|
27
|
+
onScroll,
|
|
28
|
+
children
|
|
29
|
+
}: ReactCarouselProps) => {
|
|
30
|
+
const carouselContainer = useRef<HTMLDivElement>(null)
|
|
31
|
+
const carousel = useRef<HTMLUListElement>(null)
|
|
32
|
+
const carouselItems = useRef<any>(null)
|
|
33
|
+
const paginated = useRef(false)
|
|
34
|
+
const currentPage = useRef(1)
|
|
35
|
+
|
|
36
|
+
const [progressValue, setProgressValue] = useState(0)
|
|
37
|
+
const [updatedSubText, setUpdatedSubText] = useState(subText)
|
|
38
|
+
|
|
39
|
+
const classes = classNames([
|
|
40
|
+
styles.carousel,
|
|
41
|
+
className
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
const containerClasses = classNames([
|
|
45
|
+
styles.container,
|
|
46
|
+
scrollSnap && styles.snap
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
const wrapperClasses = classNames([
|
|
50
|
+
styles.wrapper,
|
|
51
|
+
effect && styles[effect],
|
|
52
|
+
wrapperClassName
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
const paginationWrapperClasses = classNames([
|
|
56
|
+
styles['pagination-wrapper'],
|
|
57
|
+
paginationClassName
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
const paginationClasses = classNames([
|
|
61
|
+
styles.pagination,
|
|
62
|
+
!subText && paginationClassName
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
const totalPages = Math.ceil(items / visibleItems!)
|
|
66
|
+
const subTextValue = subText?.match(/\{0\}|\{1\}/g) ? subText : undefined
|
|
67
|
+
const style = visibleItems > 1
|
|
68
|
+
? { '--w-slide-width': `${100 / visibleItems}%;` } as React.CSSProperties
|
|
69
|
+
: undefined
|
|
70
|
+
|
|
71
|
+
const updateValues = (page: number) => {
|
|
72
|
+
const activeElement = carouselItems.current[page - 1]
|
|
73
|
+
|
|
74
|
+
Array.from(carouselItems.current).forEach(li => (li as HTMLLIElement).removeAttribute('data-active'))
|
|
75
|
+
activeElement.dataset.active = 'true'
|
|
76
|
+
|
|
77
|
+
if (subTextValue) {
|
|
78
|
+
setUpdatedSubText(
|
|
79
|
+
subTextValue
|
|
80
|
+
.replace('{0}', String(page))
|
|
81
|
+
.replace('{1}', String(totalPages))
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (progress) {
|
|
86
|
+
const percentage = (100 / (totalPages - 1))
|
|
87
|
+
|
|
88
|
+
setProgressValue(percentage * (page - 1))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onScroll?.(page)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const scroll = debounceScroll((event: Event) => {
|
|
95
|
+
if (!paginated.current) {
|
|
96
|
+
const target = event.target as HTMLDivElement
|
|
97
|
+
const scrollLeft = target.scrollLeft
|
|
98
|
+
const itemWidth = target.children[0].clientWidth
|
|
99
|
+
const page = Math.round(scrollLeft / itemWidth) + 1
|
|
100
|
+
|
|
101
|
+
currentPage.current = page
|
|
102
|
+
|
|
103
|
+
updateValues(page)
|
|
104
|
+
}
|
|
105
|
+
}, debounce)
|
|
106
|
+
|
|
107
|
+
const paginate = (event: PaginationEventType) => {
|
|
108
|
+
const liElement = carouselItems.current[event.page - 1]
|
|
109
|
+
|
|
110
|
+
liElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
111
|
+
|
|
112
|
+
currentPage.current = event.page
|
|
113
|
+
paginated.current = true
|
|
114
|
+
|
|
115
|
+
updateValues(event.page)
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
paginated.current = false
|
|
118
|
+
}, 300)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
const usedInAstro = carousel.current?.children[0].nodeName === 'ASTRO-SLOT'
|
|
123
|
+
|
|
124
|
+
carouselItems.current = usedInAstro
|
|
125
|
+
? carousel.current.querySelectorAll('li')
|
|
126
|
+
: carousel.current?.children
|
|
127
|
+
|
|
128
|
+
carouselContainer.current?.addEventListener('scroll', scroll)
|
|
129
|
+
|
|
130
|
+
return () => {
|
|
131
|
+
carouselContainer.current?.removeEventListener('scroll', scroll)
|
|
132
|
+
}
|
|
133
|
+
}, [])
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<section className={classes}>
|
|
137
|
+
<div className={containerClasses} ref={carouselContainer}>
|
|
138
|
+
<ul className={wrapperClasses} style={style} ref={carousel}>
|
|
139
|
+
{children}
|
|
140
|
+
</ul>
|
|
141
|
+
</div>
|
|
142
|
+
<ConditionalWrapper
|
|
143
|
+
condition={!!(subText || progress)}
|
|
144
|
+
wrapper={children => (
|
|
145
|
+
<div className={paginationWrapperClasses}>{children}</div>
|
|
146
|
+
)}
|
|
147
|
+
>
|
|
148
|
+
{progress && (
|
|
149
|
+
<Progress value={progressValue} />
|
|
150
|
+
)}
|
|
151
|
+
<Pagination
|
|
152
|
+
type="arrows"
|
|
153
|
+
{...pagination}
|
|
154
|
+
currentPage={currentPage.current}
|
|
155
|
+
totalPages={totalPages}
|
|
156
|
+
className={paginationClasses}
|
|
157
|
+
onChange={paginate}
|
|
158
|
+
/>
|
|
159
|
+
{updatedSubText && (
|
|
160
|
+
<span className={styles.subtext}>
|
|
161
|
+
{updatedSubText
|
|
162
|
+
.replace('{0}', '1')
|
|
163
|
+
.replace('{1}', String(totalPages))
|
|
164
|
+
}
|
|
165
|
+
</span>
|
|
166
|
+
)}
|
|
167
|
+
</ConditionalWrapper>
|
|
168
|
+
</section>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default Carousel
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@import '../../scss/config.scss';
|
|
2
|
+
|
|
3
|
+
body {
|
|
4
|
+
--w-slide-width: 100%;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.carousel {
|
|
8
|
+
@include layout(flex, sm, column);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.container {
|
|
12
|
+
@include layout(flex, sm);
|
|
13
|
+
@include visibility(auto);
|
|
14
|
+
|
|
15
|
+
scrollbar-width: none;
|
|
16
|
+
|
|
17
|
+
&.snap {
|
|
18
|
+
scroll-snap-type: x mandatory;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.wrapper {
|
|
23
|
+
@include layout(flex, sm);
|
|
24
|
+
@include spacing(0);
|
|
25
|
+
@include size('w100%');
|
|
26
|
+
|
|
27
|
+
list-style-type: none;
|
|
28
|
+
|
|
29
|
+
&.opacity li:not([data-active]) {
|
|
30
|
+
opacity: .5;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.saturate li:not([data-active]) {
|
|
34
|
+
filter: saturate(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
li {
|
|
38
|
+
@include transition();
|
|
39
|
+
@include spacing(m0);
|
|
40
|
+
@include layout(flex, h-center);
|
|
41
|
+
|
|
42
|
+
scroll-snap-align: center;
|
|
43
|
+
min-width: var(--w-slide-width);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.pagination-wrapper {
|
|
48
|
+
@include layout(flex, sm, column);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.pagination {
|
|
52
|
+
@include layout(h-center);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.subtext {
|
|
56
|
+
@include layout(flex, h-center);
|
|
57
|
+
@include typography(md, primary-20);
|
|
58
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PaginationProps } from '../Pagination/pagination'
|
|
2
|
+
|
|
3
|
+
export type CarouselProps = {
|
|
4
|
+
items: number
|
|
5
|
+
visibleItems?: number
|
|
6
|
+
subText?: string
|
|
7
|
+
autoplay?: boolean
|
|
8
|
+
vertical?: boolean
|
|
9
|
+
scrollSnap?: boolean
|
|
10
|
+
progress?: boolean
|
|
11
|
+
pagination?: PaginationProps
|
|
12
|
+
effect?: 'opacity' | 'saturate' | null
|
|
13
|
+
debounce?: number
|
|
14
|
+
className?: string
|
|
15
|
+
wrapperClassName?: string
|
|
16
|
+
paginationClassName?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SvelteCarouselProps = {
|
|
20
|
+
onScroll?: (event: number) => void
|
|
21
|
+
} & CarouselProps
|
|
22
|
+
|
|
23
|
+
export type ReactCarouselProps = {
|
|
24
|
+
onScroll?: (event: number) => void
|
|
25
|
+
children?: React.ReactNode
|
|
26
|
+
} & CarouselProps
|