ajo-ui 0.1.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/LICENSE +15 -0
- package/README.md +126 -0
- package/dist/accordion.js +130 -0
- package/dist/avatar.js +54 -0
- package/dist/calendar.js +2 -0
- package/dist/carousel.js +239 -0
- package/dist/chart.js +790 -0
- package/dist/checkbox-group.js +70 -0
- package/dist/checkbox.js +77 -0
- package/dist/chunks/bar-CVafh6C1.js +99 -0
- package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
- package/dist/chunks/collection-DtRB63U4.js +111 -0
- package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
- package/dist/chunks/menu-B45IyHHC.js +704 -0
- package/dist/chunks/native-BJdhd9XJ.js +20 -0
- package/dist/chunks/popup-C8Bb3l_g.js +459 -0
- package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
- package/dist/chunks/position-D6_i_SRn.js +434 -0
- package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
- package/dist/collapsible.js +106 -0
- package/dist/command.js +263 -0
- package/dist/context-menu.js +112 -0
- package/dist/data-table.js +5 -0
- package/dist/dialog.js +207 -0
- package/dist/direction.js +22 -0
- package/dist/drawer.js +139 -0
- package/dist/field.js +26 -0
- package/dist/index.js +38 -0
- package/dist/input-date.js +994 -0
- package/dist/input-group.js +52 -0
- package/dist/input-otp.js +179 -0
- package/dist/menu.js +2 -0
- package/dist/menubar.js +236 -0
- package/dist/message-scroller.js +446 -0
- package/dist/navigation-menu.js +330 -0
- package/dist/popover.js +307 -0
- package/dist/progress.js +39 -0
- package/dist/radio-group.js +107 -0
- package/dist/resizable.js +172 -0
- package/dist/select.js +961 -0
- package/dist/sidebar.js +343 -0
- package/dist/slider.js +259 -0
- package/dist/switch.js +53 -0
- package/dist/tabs.js +182 -0
- package/dist/toast.js +492 -0
- package/dist/toggle-group.js +111 -0
- package/dist/toggle.js +52 -0
- package/dist/toolbar.js +127 -0
- package/dist/tooltip.js +196 -0
- package/dist/utils.js +104 -0
- package/dist/virtual-list.js +2 -0
- package/package.json +250 -0
- package/src/accordion.tsx +261 -0
- package/src/availability.ts +261 -0
- package/src/avatar.tsx +99 -0
- package/src/bar.ts +156 -0
- package/src/calendar.tsx +1441 -0
- package/src/carousel.tsx +424 -0
- package/src/chart.tsx +1194 -0
- package/src/checkbox-group.tsx +132 -0
- package/src/checkbox.tsx +130 -0
- package/src/collapsible.tsx +188 -0
- package/src/collection.ts +154 -0
- package/src/command.tsx +511 -0
- package/src/context-menu.tsx +233 -0
- package/src/data-table-contract.ts +143 -0
- package/src/data-table-model.ts +760 -0
- package/src/data-table.tsx +475 -0
- package/src/dialog.tsx +393 -0
- package/src/direction.tsx +45 -0
- package/src/drawer.tsx +251 -0
- package/src/field.tsx +61 -0
- package/src/index.ts +37 -0
- package/src/input-date.tsx +1539 -0
- package/src/input-group.tsx +142 -0
- package/src/input-otp.tsx +324 -0
- package/src/menu-cluster.ts +124 -0
- package/src/menu.tsx +1095 -0
- package/src/menubar.tsx +459 -0
- package/src/message-scroller.tsx +732 -0
- package/src/native.ts +26 -0
- package/src/navigation-menu.tsx +578 -0
- package/src/popover.tsx +519 -0
- package/src/popup-surface.tsx +31 -0
- package/src/popup.ts +569 -0
- package/src/position.ts +523 -0
- package/src/progress.tsx +70 -0
- package/src/radio-group.tsx +186 -0
- package/src/resizable.tsx +310 -0
- package/src/segments.ts +922 -0
- package/src/select.tsx +1501 -0
- package/src/sidebar.tsx +683 -0
- package/src/slider.tsx +424 -0
- package/src/switch.tsx +104 -0
- package/src/tabs.tsx +314 -0
- package/src/toast.tsx +923 -0
- package/src/toggle-group.tsx +249 -0
- package/src/toggle.tsx +91 -0
- package/src/toolbar.tsx +212 -0
- package/src/tooltip.tsx +359 -0
- package/src/utils.ts +204 -0
- package/src/virtual-list.tsx +205 -0
- package/src/virtual.ts +385 -0
package/src/carousel.tsx
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import type { IntrinsicElements, Stateful, Stateless, WithChildren } from 'ajo'
|
|
2
|
+
import { callHandler, clamp, listen, resize, scrolling, statefulRootAttrs as rootAttrs } from 'ajo-cloves'
|
|
3
|
+
import { context } from 'ajo/context'
|
|
4
|
+
|
|
5
|
+
/** Layout axis used by a Carousel. */
|
|
6
|
+
export type CarouselOrientation =
|
|
7
|
+
| 'horizontal'
|
|
8
|
+
| 'vertical'
|
|
9
|
+
|
|
10
|
+
/** Inline direction used by horizontal Carousel navigation. */
|
|
11
|
+
export type CarouselDirection =
|
|
12
|
+
| 'ltr'
|
|
13
|
+
| 'rtl'
|
|
14
|
+
|
|
15
|
+
/** Behavioral options for the native Carousel controller. */
|
|
16
|
+
export type CarouselOptions = {
|
|
17
|
+
/** Axis hint for carousel options. */
|
|
18
|
+
axis?: 'x' | 'y'
|
|
19
|
+
/** Text direction hint for localized carousels. */
|
|
20
|
+
direction?: CarouselDirection
|
|
21
|
+
/** Loop prev/next navigation at the ends. */
|
|
22
|
+
loop?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Lifecycle events emitted by a Carousel controller. */
|
|
26
|
+
export type CarouselEvent =
|
|
27
|
+
| 'reInit'
|
|
28
|
+
| 'select'
|
|
29
|
+
|
|
30
|
+
/** Imperative view of the current Carousel position and controls. */
|
|
31
|
+
export type CarouselApi = {
|
|
32
|
+
canScrollNext: () => boolean
|
|
33
|
+
canScrollPrev: () => boolean
|
|
34
|
+
off: (event: CarouselEvent, listener: () => void) => void
|
|
35
|
+
on: (event: CarouselEvent, listener: () => void) => void
|
|
36
|
+
reInit: () => void
|
|
37
|
+
scrollNext: () => void
|
|
38
|
+
scrollPrev: () => void
|
|
39
|
+
scrollSnapList: () => number[]
|
|
40
|
+
scrollTo: (index: number) => void
|
|
41
|
+
selectedScrollSnap: () => number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Arguments for the Carousel root. */
|
|
45
|
+
export type CarouselArgs = WithChildren<IntrinsicElements['div'] & {
|
|
46
|
+
/** Orientation of the carousel track. */
|
|
47
|
+
orientation?: CarouselOrientation
|
|
48
|
+
/** Native carousel options. */
|
|
49
|
+
opts?: CarouselOptions
|
|
50
|
+
/** Receives the small native carousel controller. */
|
|
51
|
+
setApi?: (api: CarouselApi) => void
|
|
52
|
+
}>
|
|
53
|
+
|
|
54
|
+
/** Arguments for the Carousel track and viewport composition. */
|
|
55
|
+
export type CarouselContentArgs = WithChildren<IntrinsicElements['div'] & {
|
|
56
|
+
/** Classes for the viewport wrapper. */
|
|
57
|
+
viewportClass?: string
|
|
58
|
+
}>
|
|
59
|
+
|
|
60
|
+
/** Arguments for one snap-aligned Carousel item. */
|
|
61
|
+
export type CarouselItemArgs = WithChildren<IntrinsicElements['div']>
|
|
62
|
+
|
|
63
|
+
/** Shared arguments for previous and next Carousel buttons. */
|
|
64
|
+
export type CarouselButtonArgs = WithChildren<IntrinsicElements['button']>
|
|
65
|
+
|
|
66
|
+
/** Observable state and controls inherited from the nearest Carousel. */
|
|
67
|
+
export type CarouselContextValue = {
|
|
68
|
+
canScrollNext: boolean
|
|
69
|
+
canScrollPrev: boolean
|
|
70
|
+
count: number
|
|
71
|
+
orientation: CarouselOrientation
|
|
72
|
+
scrollNext: () => void
|
|
73
|
+
scrollPrev: () => void
|
|
74
|
+
selected: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type CarouselPartsContextValue = {
|
|
78
|
+
setViewport: (element: HTMLElement | null) => void
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Read the observable state and controls inherited from the nearest Carousel. */
|
|
82
|
+
export const CarouselContext = context<CarouselContextValue | null>(null)
|
|
83
|
+
const CarouselPartsContext = context<CarouselPartsContextValue | null>(null)
|
|
84
|
+
|
|
85
|
+
const carousel = () => {
|
|
86
|
+
const value = CarouselContext()
|
|
87
|
+
if (!value) throw new Error('Carousel parts must be used within a <Carousel />')
|
|
88
|
+
return value
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const carouselParts = () => {
|
|
92
|
+
const value = CarouselPartsContext()
|
|
93
|
+
if (!value) throw new Error('Carousel parts must be used within a <Carousel />')
|
|
94
|
+
return value
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const axis = (orientation: CarouselOrientation) =>
|
|
98
|
+
orientation === 'horizontal' ? 'x' : 'y'
|
|
99
|
+
|
|
100
|
+
const alignBlock = (orientation: CarouselOrientation) =>
|
|
101
|
+
orientation === 'vertical' ? 'start' : 'nearest'
|
|
102
|
+
|
|
103
|
+
const alignInline = (orientation: CarouselOrientation) =>
|
|
104
|
+
orientation === 'horizontal' ? 'start' : 'nearest'
|
|
105
|
+
|
|
106
|
+
const itemPosition = (item: HTMLElement, orientation: CarouselOrientation) =>
|
|
107
|
+
orientation === 'horizontal' ? item.offsetLeft : item.offsetTop
|
|
108
|
+
|
|
109
|
+
const viewportPosition = (viewport: HTMLElement, orientation: CarouselOrientation) =>
|
|
110
|
+
orientation === 'horizontal' ? viewport.scrollLeft : viewport.scrollTop
|
|
111
|
+
|
|
112
|
+
const viewportSize = (viewport: HTMLElement, orientation: CarouselOrientation) =>
|
|
113
|
+
orientation === 'horizontal' ? viewport.clientWidth : viewport.clientHeight
|
|
114
|
+
|
|
115
|
+
const itemSize = (item: HTMLElement, orientation: CarouselOrientation) =>
|
|
116
|
+
orientation === 'horizontal' ? item.offsetWidth : item.offsetHeight
|
|
117
|
+
|
|
118
|
+
const CarouselRoot: Stateful<Pick<CarouselArgs, 'children' | 'opts' | 'orientation' | 'setApi'>> = function* () {
|
|
119
|
+
let viewport: HTMLElement | null = null
|
|
120
|
+
let orientation: CarouselOrientation = 'horizontal'
|
|
121
|
+
let loop = false
|
|
122
|
+
let selected = 0
|
|
123
|
+
let count = 0
|
|
124
|
+
let canScrollPrev = false
|
|
125
|
+
let canScrollNext = false
|
|
126
|
+
let apiReceiver: CarouselArgs['setApi']
|
|
127
|
+
const listeners = new Map<CarouselEvent, Set<() => void>>()
|
|
128
|
+
|
|
129
|
+
const items = () => viewport
|
|
130
|
+
? Array.from(viewport.querySelectorAll<HTMLElement>('[data-slot="carousel-item"]'))
|
|
131
|
+
: []
|
|
132
|
+
|
|
133
|
+
const emit = (event: CarouselEvent) => {
|
|
134
|
+
for (const listener of listeners.get(event) ?? []) listener()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const syncScrollability = () => {
|
|
138
|
+
canScrollPrev = Boolean(count) && (loop || selected > 0)
|
|
139
|
+
canScrollNext = Boolean(count) && (loop || selected < count - 1)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const sync = () => {
|
|
143
|
+
const nodes = items()
|
|
144
|
+
const previousSelected = selected
|
|
145
|
+
const previousCount = count
|
|
146
|
+
const previousCanScrollPrev = canScrollPrev
|
|
147
|
+
const previousCanScrollNext = canScrollNext
|
|
148
|
+
count = nodes.length
|
|
149
|
+
|
|
150
|
+
if (!viewport || !count) {
|
|
151
|
+
selected = 0
|
|
152
|
+
} else {
|
|
153
|
+
const center = viewportPosition(viewport, orientation) + viewportSize(viewport, orientation) / 2
|
|
154
|
+
let nextSelected = 0
|
|
155
|
+
let distance = Number.POSITIVE_INFINITY
|
|
156
|
+
|
|
157
|
+
nodes.forEach((item, index) => {
|
|
158
|
+
const next = Math.abs(itemPosition(item, orientation) + itemSize(item, orientation) / 2 - center)
|
|
159
|
+
if (next < distance) {
|
|
160
|
+
distance = next
|
|
161
|
+
nextSelected = index
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
selected = nextSelected
|
|
166
|
+
}
|
|
167
|
+
syncScrollability()
|
|
168
|
+
|
|
169
|
+
const changed = previousSelected !== selected
|
|
170
|
+
|| previousCount !== count
|
|
171
|
+
|| previousCanScrollPrev !== canScrollPrev
|
|
172
|
+
|| previousCanScrollNext !== canScrollNext
|
|
173
|
+
if (previousCount !== count) emit('reInit')
|
|
174
|
+
if (previousSelected !== selected) emit('select')
|
|
175
|
+
|
|
176
|
+
return changed
|
|
177
|
+
}
|
|
178
|
+
const invalidate = () => {
|
|
179
|
+
if (sync()) this.next()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const scroll = scrolling(this, {
|
|
183
|
+
target: () => viewport,
|
|
184
|
+
onScroll: invalidate,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const size = resize(this, {
|
|
188
|
+
target: () => viewport,
|
|
189
|
+
onResize: invalidate,
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const scrollTo = (index: number) => {
|
|
193
|
+
const nodes = items()
|
|
194
|
+
if (!nodes.length) return
|
|
195
|
+
const target = clamp(index, 0, nodes.length - 1)
|
|
196
|
+
nodes[target]?.scrollIntoView({
|
|
197
|
+
behavior: 'smooth',
|
|
198
|
+
block: alignBlock(orientation),
|
|
199
|
+
inline: alignInline(orientation),
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const scrollPrev = () => {
|
|
204
|
+
if (!count) return
|
|
205
|
+
scrollTo(selected <= 0 && loop ? count - 1 : selected - 1)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const scrollNext = () => {
|
|
209
|
+
if (!count) return
|
|
210
|
+
scrollTo(selected >= count - 1 && loop ? 0 : selected + 1)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const api: CarouselApi = {
|
|
214
|
+
canScrollNext: () => canScrollNext,
|
|
215
|
+
canScrollPrev: () => canScrollPrev,
|
|
216
|
+
off: (event, listener) => listeners.get(event)?.delete(listener),
|
|
217
|
+
on: (event, listener) => {
|
|
218
|
+
const set = listeners.get(event) ?? new Set()
|
|
219
|
+
set.add(listener)
|
|
220
|
+
listeners.set(event, set)
|
|
221
|
+
},
|
|
222
|
+
reInit: () => {
|
|
223
|
+
this.next(sync)
|
|
224
|
+
},
|
|
225
|
+
scrollNext,
|
|
226
|
+
scrollPrev,
|
|
227
|
+
scrollSnapList: () => items().map((_, index) => index),
|
|
228
|
+
scrollTo,
|
|
229
|
+
selectedScrollSnap: () => selected,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const setViewport = (element: HTMLElement | null) => {
|
|
233
|
+
if (viewport === element) return
|
|
234
|
+
|
|
235
|
+
viewport = element
|
|
236
|
+
scroll.sync()
|
|
237
|
+
size.sync()
|
|
238
|
+
this.next(sync)
|
|
239
|
+
}
|
|
240
|
+
const parts: CarouselPartsContextValue = { setViewport }
|
|
241
|
+
|
|
242
|
+
const keydown = (event: KeyboardEvent) => {
|
|
243
|
+
if (orientation === 'horizontal') {
|
|
244
|
+
if (event.key === 'ArrowLeft') {
|
|
245
|
+
event.preventDefault()
|
|
246
|
+
scrollPrev()
|
|
247
|
+
} else if (event.key === 'ArrowRight') {
|
|
248
|
+
event.preventDefault()
|
|
249
|
+
scrollNext()
|
|
250
|
+
}
|
|
251
|
+
} else if (event.key === 'ArrowUp') {
|
|
252
|
+
event.preventDefault()
|
|
253
|
+
scrollPrev()
|
|
254
|
+
} else if (event.key === 'ArrowDown') {
|
|
255
|
+
event.preventDefault()
|
|
256
|
+
scrollNext()
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
listen(this, 'keydown', keydown)
|
|
261
|
+
|
|
262
|
+
for (const {
|
|
263
|
+
children,
|
|
264
|
+
opts,
|
|
265
|
+
orientation: nextOrientation,
|
|
266
|
+
setApi,
|
|
267
|
+
} of this) {
|
|
268
|
+
orientation = nextOrientation ?? (opts?.axis === 'y' ? 'vertical' : 'horizontal')
|
|
269
|
+
loop = Boolean(opts?.loop)
|
|
270
|
+
syncScrollability()
|
|
271
|
+
if (setApi && setApi !== apiReceiver) {
|
|
272
|
+
apiReceiver = setApi
|
|
273
|
+
setApi(api)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
scroll.sync()
|
|
277
|
+
size.sync()
|
|
278
|
+
|
|
279
|
+
CarouselContext({
|
|
280
|
+
canScrollNext,
|
|
281
|
+
canScrollPrev,
|
|
282
|
+
count,
|
|
283
|
+
orientation,
|
|
284
|
+
scrollNext,
|
|
285
|
+
scrollPrev,
|
|
286
|
+
selected,
|
|
287
|
+
})
|
|
288
|
+
CarouselPartsContext(parts)
|
|
289
|
+
|
|
290
|
+
yield <>{children}</>
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Unstyled native scroll-snap carousel root. */
|
|
295
|
+
const Carousel: Stateless<CarouselArgs> = ({
|
|
296
|
+
children,
|
|
297
|
+
dir,
|
|
298
|
+
opts,
|
|
299
|
+
orientation,
|
|
300
|
+
role = 'region',
|
|
301
|
+
setApi,
|
|
302
|
+
...attrs
|
|
303
|
+
}) => (
|
|
304
|
+
<CarouselRoot
|
|
305
|
+
{...rootAttrs(attrs as Record<string, unknown>)}
|
|
306
|
+
opts={opts}
|
|
307
|
+
orientation={orientation}
|
|
308
|
+
setApi={setApi}
|
|
309
|
+
attr:aria-roledescription="carousel"
|
|
310
|
+
attr:data-axis={axis(orientation ?? (opts?.axis === 'y' ? 'vertical' : 'horizontal'))}
|
|
311
|
+
attr:data-slot="carousel"
|
|
312
|
+
attr:dir={opts?.direction ?? dir}
|
|
313
|
+
attr:role={role}
|
|
314
|
+
>
|
|
315
|
+
{children}
|
|
316
|
+
</CarouselRoot>
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
/** Unstyled scroll viewport and track for carousel slides. */
|
|
320
|
+
const CarouselContent: Stateless<CarouselContentArgs> = ({
|
|
321
|
+
children,
|
|
322
|
+
viewportClass,
|
|
323
|
+
...attrs
|
|
324
|
+
}) => {
|
|
325
|
+
const state = carousel()
|
|
326
|
+
const parts = carouselParts()
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<div class={viewportClass} data-slot="carousel-content">
|
|
330
|
+
<div
|
|
331
|
+
{...attrs}
|
|
332
|
+
data-axis={axis(state.orientation)}
|
|
333
|
+
data-slot="carousel-track"
|
|
334
|
+
ref={parts.setViewport}
|
|
335
|
+
>
|
|
336
|
+
{children}
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Unstyled carousel slide item. */
|
|
343
|
+
const CarouselItem: Stateless<CarouselItemArgs> = ({
|
|
344
|
+
children,
|
|
345
|
+
role = 'group',
|
|
346
|
+
...attrs
|
|
347
|
+
}) => (
|
|
348
|
+
<div
|
|
349
|
+
{...attrs}
|
|
350
|
+
aria-roledescription="slide"
|
|
351
|
+
data-slot="carousel-item"
|
|
352
|
+
role={role}
|
|
353
|
+
>
|
|
354
|
+
{children}
|
|
355
|
+
</div>
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
/** Unstyled previous slide button. */
|
|
359
|
+
const CarouselPrevious: Stateless<CarouselButtonArgs> = ({
|
|
360
|
+
children,
|
|
361
|
+
'aria-label': label = 'Previous slide',
|
|
362
|
+
disabled,
|
|
363
|
+
type = 'button',
|
|
364
|
+
'set:onclick': onClick,
|
|
365
|
+
...attrs
|
|
366
|
+
}) => {
|
|
367
|
+
const { canScrollPrev, scrollPrev } = carousel()
|
|
368
|
+
const click = (event: MouseEvent) => {
|
|
369
|
+
callHandler(onClick, event)
|
|
370
|
+
if (event.defaultPrevented || disabled || !canScrollPrev) return
|
|
371
|
+
scrollPrev()
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<button
|
|
376
|
+
{...attrs}
|
|
377
|
+
aria-label={label}
|
|
378
|
+
data-slot="carousel-previous"
|
|
379
|
+
disabled={disabled || !canScrollPrev}
|
|
380
|
+
set:onclick={click}
|
|
381
|
+
type={type}
|
|
382
|
+
>
|
|
383
|
+
{children}
|
|
384
|
+
</button>
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Unstyled next slide button. */
|
|
389
|
+
const CarouselNext: Stateless<CarouselButtonArgs> = ({
|
|
390
|
+
children,
|
|
391
|
+
'aria-label': label = 'Next slide',
|
|
392
|
+
disabled,
|
|
393
|
+
type = 'button',
|
|
394
|
+
'set:onclick': onClick,
|
|
395
|
+
...attrs
|
|
396
|
+
}) => {
|
|
397
|
+
const { canScrollNext, scrollNext } = carousel()
|
|
398
|
+
const click = (event: MouseEvent) => {
|
|
399
|
+
callHandler(onClick, event)
|
|
400
|
+
if (event.defaultPrevented || disabled || !canScrollNext) return
|
|
401
|
+
scrollNext()
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<button
|
|
406
|
+
{...attrs}
|
|
407
|
+
aria-label={label}
|
|
408
|
+
data-slot="carousel-next"
|
|
409
|
+
disabled={disabled || !canScrollNext}
|
|
410
|
+
set:onclick={click}
|
|
411
|
+
type={type}
|
|
412
|
+
>
|
|
413
|
+
{children}
|
|
414
|
+
</button>
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export {
|
|
419
|
+
Carousel,
|
|
420
|
+
CarouselContent,
|
|
421
|
+
CarouselItem,
|
|
422
|
+
CarouselNext,
|
|
423
|
+
CarouselPrevious,
|
|
424
|
+
}
|