@docubook/flame 1.0.0-beta.70 → 1.0.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.
@@ -1,548 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "../../node/utils";
4
- import { ChevronLeft, ChevronRight } from "lucide-react";
5
- import { useMemo, type ReactNode } from "react";
6
-
7
- type PaginationSize = "lg" | "md" | "sm" | "xs";
8
- type PaginationVariant = "default" | "square" | "rounded";
9
-
10
- interface PaginationRootProps {
11
- children?: ReactNode;
12
- className?: string;
13
- size?: PaginationSize;
14
- variant?: PaginationVariant;
15
- }
16
-
17
- interface PaginationItemProps {
18
- children: ReactNode;
19
- active?: boolean;
20
- disabled?: boolean;
21
- onClick?: () => void;
22
- className?: string;
23
- size?: PaginationSize;
24
- variant?: PaginationVariant;
25
- "aria-label"?: string;
26
- }
27
-
28
- interface PaginationRangeProps {
29
- start: number;
30
- end: number;
31
- total: number;
32
- current: number;
33
- onPageChange: (page: number) => void;
34
- siblingCount?: number;
35
- className?: string;
36
- size?: PaginationSize;
37
- variant?: PaginationVariant;
38
- }
39
-
40
- interface PaginationButtonsProps {
41
- page: number;
42
- totalPages: number;
43
- onPageChange: (page: number) => void;
44
- className?: string;
45
- size?: PaginationSize;
46
- showFirstLast?: boolean;
47
- showPrevNext?: boolean;
48
- prevLabel?: string;
49
- nextLabel?: string;
50
- firstLabel?: string;
51
- lastLabel?: string;
52
- disabled?: boolean;
53
- }
54
-
55
- interface PaginationInfoProps {
56
- current: number;
57
- total: number;
58
- pageSize?: number;
59
- className?: string;
60
- label?: string;
61
- showTotal?: boolean;
62
- }
63
-
64
- interface PaginationFullProps {
65
- current: number;
66
- total: number;
67
- pageSize?: number;
68
- onPageChange: (page: number) => void;
69
- siblingCount?: number;
70
- className?: string;
71
- size?: PaginationSize;
72
- variant?: PaginationVariant;
73
- showFirstLast?: boolean;
74
- showPrevNext?: boolean;
75
- infoClassName?: string;
76
- }
77
-
78
- interface PaginationDocsProps {
79
- prev?: { href: string; title: string };
80
- next?: { href: string; title: string };
81
- className?: string;
82
- }
83
-
84
- function getPaginationRange({
85
- totalCount,
86
- pageSize,
87
- siblingCount = 1,
88
- currentPage,
89
- }: {
90
- totalCount: number;
91
- pageSize: number;
92
- siblingCount?: number;
93
- currentPage: number;
94
- }): (number | "ellipsis")[] {
95
- const totalPages = Math.ceil(totalCount / pageSize);
96
- const DOTS = "ellipsis" as const;
97
-
98
- if (totalPages === 1) {
99
- return [1];
100
- }
101
-
102
- const totalPageNumbers = siblingCount + 5;
103
-
104
- if (totalPages < totalPageNumbers) {
105
- return Array.from({ length: totalPages }, (_, i) => i + 1);
106
- }
107
-
108
- const leftSiblingIndex = Math.max(currentPage - siblingCount, 1);
109
- const rightSiblingIndex = Math.min(currentPage + siblingCount, totalPages);
110
-
111
- const showLeftDots = leftSiblingIndex > 2;
112
- const showRightDots = rightSiblingIndex < totalPages - 1;
113
-
114
- if (!showLeftDots && showRightDots) {
115
- const leftRange = Array.from({ length: 3 }, (_, i) => i + 1);
116
- return [...leftRange, DOTS, totalPages - 1, totalPages];
117
- }
118
-
119
- if (showLeftDots && !showRightDots) {
120
- const rightRange = Array.from({ length: 3 }, (_, i) => totalPages - 2 + i);
121
- return [1, 2, DOTS, ...rightRange];
122
- }
123
-
124
- if (showLeftDots && showRightDots) {
125
- const middleRange = Array.from(
126
- { length: rightSiblingIndex - leftSiblingIndex + 1 },
127
- (_, i) => leftSiblingIndex + i
128
- );
129
- return [1, 2, DOTS, ...middleRange, DOTS, totalPages - 1, totalPages];
130
- }
131
-
132
- return [1];
133
- }
134
-
135
- export function Pagination({
136
- children,
137
- className,
138
- size = "md",
139
- variant = "default",
140
- }: PaginationRootProps) {
141
- const sizeClass = {
142
- lg: "join-lg",
143
- md: "",
144
- sm: "join-sm",
145
- xs: "join-xs",
146
- }[size];
147
-
148
- const variantClass =
149
- variant === "square"
150
- ? "[&_.join-item]:btn-square"
151
- : variant === "rounded"
152
- ? "[&_.join-item]:btn-circle"
153
- : "";
154
-
155
- return <div className={cn("join", sizeClass, variantClass, className)}>{children}</div>;
156
- }
157
-
158
- export function PaginationItem({
159
- children,
160
- active = false,
161
- disabled = false,
162
- onClick,
163
- className,
164
- size = "md",
165
- "aria-label": ariaLabel,
166
- }: PaginationItemProps) {
167
- const sizeClasses = {
168
- lg: "btn-lg",
169
- md: "",
170
- sm: "btn-sm",
171
- xs: "btn-xs",
172
- }[size];
173
-
174
- return (
175
- <button
176
- type="button"
177
- className={cn(
178
- "join-item btn",
179
- sizeClasses,
180
- active && "btn-active",
181
- disabled && "btn-disabled",
182
- className
183
- )}
184
- onClick={onClick}
185
- disabled={disabled}
186
- aria-label={ariaLabel}
187
- aria-current={active ? "page" : undefined}
188
- >
189
- {children}
190
- </button>
191
- );
192
- }
193
-
194
- export function PaginationButtons({
195
- page,
196
- totalPages,
197
- onPageChange,
198
- className,
199
- size = "md",
200
- showFirstLast = false,
201
- showPrevNext = true,
202
- prevLabel,
203
- nextLabel,
204
- firstLabel,
205
- lastLabel,
206
- disabled = false,
207
- }: PaginationButtonsProps) {
208
- const handlePrev = () => {
209
- if (page > 1) onPageChange(page - 1);
210
- };
211
-
212
- const handleNext = () => {
213
- if (page < totalPages) onPageChange(page + 1);
214
- };
215
-
216
- const handleFirst = () => {
217
- onPageChange(1);
218
- };
219
-
220
- const handleLast = () => {
221
- onPageChange(totalPages);
222
- };
223
-
224
- return (
225
- <>
226
- {showFirstLast && (
227
- <button
228
- type="button"
229
- className={cn("join-item btn", size !== "md" && `btn-${size}`, className)}
230
- onClick={handleFirst}
231
- disabled={disabled || page <= 1}
232
- aria-label={firstLabel ?? "First page"}
233
- >
234
- {firstLabel ?? <ChevronLeft className="h-4 w-4" />}
235
- </button>
236
- )}
237
-
238
- {showPrevNext && (
239
- <button
240
- type="button"
241
- className={cn("join-item btn", size !== "md" && `btn-${size}`, className)}
242
- onClick={handlePrev}
243
- disabled={disabled || page <= 1}
244
- aria-label={prevLabel ?? "Previous page"}
245
- >
246
- {prevLabel ?? <ChevronLeft className="h-4 w-4" />}
247
- </button>
248
- )}
249
-
250
- <span className="join-item btn btn-disabled no-animation cursor-default">
251
- {page} / {totalPages}
252
- </span>
253
-
254
- {showPrevNext && (
255
- <button
256
- type="button"
257
- className={cn("join-item btn", size !== "md" && `btn-${size}`, className)}
258
- onClick={handleNext}
259
- disabled={disabled || page >= totalPages}
260
- aria-label={nextLabel ?? "Next page"}
261
- >
262
- {nextLabel ?? <ChevronRight className="h-4 w-4" />}
263
- </button>
264
- )}
265
-
266
- {showFirstLast && (
267
- <button
268
- type="button"
269
- className={cn("join-item btn", size !== "md" && `btn-${size}`, className)}
270
- onClick={handleLast}
271
- disabled={disabled || page >= totalPages}
272
- aria-label={lastLabel ?? "Last page"}
273
- >
274
- {lastLabel ?? <ChevronRight className="h-4 w-4" />}
275
- </button>
276
- )}
277
- </>
278
- );
279
- }
280
-
281
- export function PaginationRange({
282
- start,
283
- end,
284
- total,
285
- current,
286
- onPageChange,
287
- siblingCount = 1,
288
- className,
289
- size = "md",
290
- variant = "default",
291
- }: PaginationRangeProps) {
292
- const range = useMemo(
293
- () =>
294
- getPaginationRange({
295
- totalCount: total,
296
- pageSize: end - start + 1,
297
- siblingCount,
298
- currentPage: current,
299
- }),
300
- [total, start, end, siblingCount, current]
301
- );
302
-
303
- const sizeClasses = {
304
- lg: "join-lg",
305
- md: "",
306
- sm: "join-sm",
307
- xs: "join-xs",
308
- }[size];
309
-
310
- const variantClass =
311
- variant === "square"
312
- ? "[&_.join-item]:btn-square"
313
- : variant === "rounded"
314
- ? "[&_.join-item]:btn-circle"
315
- : "";
316
-
317
- return (
318
- <div className={cn("join", sizeClasses, variantClass, className)}>
319
- {range.map((item, index) => {
320
- if (item === "ellipsis") {
321
- return (
322
- <span key={`ellipsis-${index}`} className="join-item btn btn-disabled no-animation">
323
- ...
324
- </span>
325
- );
326
- }
327
-
328
- const isActive = item === current;
329
-
330
- return (
331
- <button
332
- key={item}
333
- type="button"
334
- className={cn(
335
- "join-item btn",
336
- isActive && "btn-active",
337
- size !== "md" && `btn-${size}`
338
- )}
339
- onClick={() => onPageChange(item)}
340
- aria-label={`Page ${item}`}
341
- aria-current={isActive ? "page" : undefined}
342
- >
343
- {item}
344
- </button>
345
- );
346
- })}
347
- </div>
348
- );
349
- }
350
-
351
- export function PaginationInfo({
352
- current,
353
- total,
354
- pageSize = 10,
355
- className,
356
- label = "Showing",
357
- showTotal = true,
358
- }: PaginationInfoProps) {
359
- const start = (current - 1) * pageSize + 1;
360
- const end = Math.min(current * pageSize, total);
361
-
362
- return (
363
- <div className={cn("text-base-content/70 text-sm", className)}>
364
- {label} {start}-{end} {showTotal && `of ${total}`}
365
- </div>
366
- );
367
- }
368
-
369
- export function PaginationFull({
370
- current,
371
- total,
372
- pageSize = 10,
373
- onPageChange,
374
- siblingCount = 1,
375
- className,
376
- size = "md",
377
- variant = "default",
378
- showFirstLast = true,
379
- showPrevNext = true,
380
- infoClassName,
381
- }: PaginationFullProps) {
382
- const totalPages = Math.ceil(total / pageSize);
383
-
384
- const range = useMemo(
385
- () =>
386
- getPaginationRange({
387
- totalCount: total,
388
- pageSize,
389
- siblingCount,
390
- currentPage: current,
391
- }),
392
- [total, pageSize, siblingCount, current]
393
- );
394
-
395
- const sizeClasses = {
396
- lg: "join-lg",
397
- md: "",
398
- sm: "join-sm",
399
- xs: "join-xs",
400
- }[size];
401
-
402
- const variantClass =
403
- variant === "square"
404
- ? "[&_.join-item]:btn-square"
405
- : variant === "rounded"
406
- ? "[&_.join-item]:btn-circle"
407
- : "";
408
-
409
- return (
410
- <div className={cn("flex flex-col items-center justify-between gap-4 sm:flex-row", className)}>
411
- <PaginationInfo
412
- current={current}
413
- total={total}
414
- pageSize={pageSize}
415
- className={infoClassName}
416
- />
417
-
418
- <div className={cn("join", sizeClasses, variantClass)}>
419
- {showFirstLast && (
420
- <button
421
- type="button"
422
- className={cn("join-item btn", size !== "md" && `btn-${size}`)}
423
- onClick={() => onPageChange(1)}
424
- disabled={current <= 1}
425
- aria-label="First page"
426
- >
427
- <ChevronLeft className="h-4 w-4" />
428
- </button>
429
- )}
430
-
431
- {showPrevNext && (
432
- <button
433
- type="button"
434
- className={cn("join-item btn", size !== "md" && `btn-${size}`)}
435
- onClick={() => onPageChange(current - 1)}
436
- disabled={current <= 1}
437
- aria-label="Previous page"
438
- >
439
- <ChevronLeft className="h-4 w-4" />
440
- </button>
441
- )}
442
-
443
- {range.map((item, index) => {
444
- if (item === "ellipsis") {
445
- return (
446
- <span key={`ellipsis-${index}`} className="join-item btn btn-disabled no-animation">
447
- ...
448
- </span>
449
- );
450
- }
451
-
452
- const isActive = item === current;
453
-
454
- return (
455
- <button
456
- key={item}
457
- type="button"
458
- className={cn(
459
- "join-item btn",
460
- isActive && "btn-active",
461
- size !== "md" && `btn-${size}`
462
- )}
463
- onClick={() => onPageChange(item)}
464
- aria-label={`Page ${item}`}
465
- aria-current={isActive ? "page" : undefined}
466
- >
467
- {item}
468
- </button>
469
- );
470
- })}
471
-
472
- {showPrevNext && (
473
- <button
474
- type="button"
475
- className={cn("join-item btn", size !== "md" && `btn-${size}`)}
476
- onClick={() => onPageChange(current + 1)}
477
- disabled={current >= totalPages}
478
- aria-label="Next page"
479
- >
480
- <ChevronRight className="h-4 w-4" />
481
- </button>
482
- )}
483
-
484
- {showFirstLast && (
485
- <button
486
- type="button"
487
- className={cn("join-item btn", size !== "md" && `btn-${size}`)}
488
- onClick={() => onPageChange(totalPages)}
489
- disabled={current >= totalPages}
490
- aria-label="Last page"
491
- >
492
- <ChevronRight className="h-4 w-4" />
493
- </button>
494
- )}
495
- </div>
496
- </div>
497
- );
498
- }
499
-
500
- export function PaginationDocs({ prev, next, className }: PaginationDocsProps) {
501
- return (
502
- <div className={cn("grid grow grid-cols-1 gap-4 py-8 sm:grid-cols-2", className)}>
503
- <div>
504
- {prev && (
505
- <a
506
- href={prev.href}
507
- className="btn btn-outline border-base-300 items-start! py-2! h-auto w-full flex-col pl-4 no-underline"
508
- >
509
- <span className="text-muted-foreground flex items-center text-xs">
510
- <ChevronLeft className="mr-1 h-4 w-4" />
511
- Previous
512
- </span>
513
- <span className="text-base-content mt-1 text-sm font-medium">{prev.title}</span>
514
- </a>
515
- )}
516
- </div>
517
-
518
- <div>
519
- {next && (
520
- <a
521
- href={next.href}
522
- className="btn btn-outline border-base-300 items-end! py-2! h-auto w-full flex-col pr-4 no-underline"
523
- >
524
- <span className="text-muted-foreground flex items-center text-xs">
525
- Next
526
- <ChevronRight className="ml-1 h-4 w-4" />
527
- </span>
528
- <span className="text-base-content mt-1 text-sm font-medium">{next.title}</span>
529
- </a>
530
- )}
531
- </div>
532
- </div>
533
- );
534
- }
535
-
536
- export type {
537
- PaginationRootProps as PaginationProps,
538
- PaginationItemProps,
539
- PaginationRangeProps,
540
- PaginationButtonsProps,
541
- PaginationInfoProps,
542
- PaginationFullProps,
543
- PaginationDocsProps,
544
- PaginationSize,
545
- PaginationVariant,
546
- };
547
-
548
- export { getPaginationRange };