@open-mercato/ui 0.6.4-develop.4178.1.aad9ddaa95 → 0.6.4-develop.4210.1.d412061cfe

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.
Files changed (92) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +14 -0
  3. package/dist/backend/DataTable.js +28 -62
  4. package/dist/backend/DataTable.js.map +2 -2
  5. package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
  6. package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
  7. package/dist/primitives/activity-feed.js +196 -0
  8. package/dist/primitives/activity-feed.js.map +7 -0
  9. package/dist/primitives/avatar.js +140 -14
  10. package/dist/primitives/avatar.js.map +3 -3
  11. package/dist/primitives/badge.js +94 -5
  12. package/dist/primitives/badge.js.map +2 -2
  13. package/dist/primitives/button-group.js +52 -0
  14. package/dist/primitives/button-group.js.map +7 -0
  15. package/dist/primitives/color-picker.js +520 -0
  16. package/dist/primitives/color-picker.js.map +7 -0
  17. package/dist/primitives/command-menu.js +355 -0
  18. package/dist/primitives/command-menu.js.map +7 -0
  19. package/dist/primitives/dialog.js +210 -35
  20. package/dist/primitives/dialog.js.map +2 -2
  21. package/dist/primitives/drawer.js +219 -0
  22. package/dist/primitives/drawer.js.map +7 -0
  23. package/dist/primitives/notification-feed.js +213 -0
  24. package/dist/primitives/notification-feed.js.map +7 -0
  25. package/dist/primitives/notification.js +23 -2
  26. package/dist/primitives/notification.js.map +2 -2
  27. package/dist/primitives/pagination.js +245 -0
  28. package/dist/primitives/pagination.js.map +7 -0
  29. package/dist/primitives/progress.js +178 -5
  30. package/dist/primitives/progress.js.map +2 -2
  31. package/dist/primitives/rating.js +174 -0
  32. package/dist/primitives/rating.js.map +7 -0
  33. package/dist/primitives/scroll-area.js +82 -0
  34. package/dist/primitives/scroll-area.js.map +7 -0
  35. package/dist/primitives/segmented-control.js +101 -0
  36. package/dist/primitives/segmented-control.js.map +7 -0
  37. package/dist/primitives/separator.js +103 -4
  38. package/dist/primitives/separator.js.map +2 -2
  39. package/dist/primitives/slider.js +115 -0
  40. package/dist/primitives/slider.js.map +7 -0
  41. package/dist/primitives/step-indicator.js +187 -0
  42. package/dist/primitives/step-indicator.js.map +7 -0
  43. package/dist/primitives/table.js +102 -10
  44. package/dist/primitives/table.js.map +2 -2
  45. package/dist/primitives/tabs.js +122 -14
  46. package/dist/primitives/tabs.js.map +2 -2
  47. package/dist/primitives/time-picker.js +9 -11
  48. package/dist/primitives/time-picker.js.map +2 -2
  49. package/package.json +8 -4
  50. package/src/backend/DataTable.tsx +43 -60
  51. package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
  52. package/src/primitives/__tests__/activity-feed.test.tsx +216 -0
  53. package/src/primitives/__tests__/avatar.test.tsx +131 -0
  54. package/src/primitives/__tests__/badge.test.tsx +189 -0
  55. package/src/primitives/__tests__/button-group.test.tsx +117 -0
  56. package/src/primitives/__tests__/color-picker.test.tsx +589 -0
  57. package/src/primitives/__tests__/command-menu.test.tsx +231 -0
  58. package/src/primitives/__tests__/dialog.test.tsx +290 -0
  59. package/src/primitives/__tests__/drawer.test.tsx +264 -0
  60. package/src/primitives/__tests__/notification-feed.test.tsx +303 -0
  61. package/src/primitives/__tests__/notification.test.tsx +102 -2
  62. package/src/primitives/__tests__/pagination.test.tsx +268 -0
  63. package/src/primitives/__tests__/progress.test.tsx +222 -0
  64. package/src/primitives/__tests__/rating.test.tsx +164 -0
  65. package/src/primitives/__tests__/scroll-area.test.tsx +109 -0
  66. package/src/primitives/__tests__/segmented-control.test.tsx +129 -0
  67. package/src/primitives/__tests__/separator.test.tsx +143 -0
  68. package/src/primitives/__tests__/slider.test.tsx +86 -0
  69. package/src/primitives/__tests__/step-indicator.test.tsx +205 -0
  70. package/src/primitives/__tests__/table.test.tsx +199 -0
  71. package/src/primitives/__tests__/tabs.test.tsx +139 -0
  72. package/src/primitives/activity-feed.tsx +326 -0
  73. package/src/primitives/avatar.tsx +225 -22
  74. package/src/primitives/badge.tsx +141 -6
  75. package/src/primitives/button-group.tsx +100 -0
  76. package/src/primitives/color-picker.tsx +693 -0
  77. package/src/primitives/command-menu.tsx +503 -0
  78. package/src/primitives/dialog.tsx +315 -51
  79. package/src/primitives/drawer.tsx +338 -0
  80. package/src/primitives/notification-feed.tsx +358 -0
  81. package/src/primitives/notification.tsx +42 -2
  82. package/src/primitives/pagination.tsx +376 -0
  83. package/src/primitives/progress.tsx +267 -6
  84. package/src/primitives/rating.tsx +245 -0
  85. package/src/primitives/scroll-area.tsx +169 -0
  86. package/src/primitives/segmented-control.tsx +162 -0
  87. package/src/primitives/separator.tsx +163 -3
  88. package/src/primitives/slider.tsx +160 -0
  89. package/src/primitives/step-indicator.tsx +287 -0
  90. package/src/primitives/table.tsx +142 -11
  91. package/src/primitives/tabs.tsx +174 -12
  92. package/src/primitives/time-picker.tsx +9 -11
@@ -60,13 +60,27 @@ export type NotificationProps = {
60
60
  className?: string
61
61
  /** Stable id used by the `NotificationStack` (or external trackers) to identify the card. Forwarded as `data-notification-id`. */
62
62
  id?: string
63
+ /**
64
+ * Auto-dismiss the card after N milliseconds. When set, an internal
65
+ * timer fires `onDismiss` after the delay. Standard toast UX:
66
+ * 4000–6000 for success / info, 8000+ for warnings, no auto-dismiss
67
+ * for errors. Defaults to undefined (manual dismiss only).
68
+ */
69
+ autoDismissMs?: number
70
+ /**
71
+ * Pause the auto-dismiss timer while the user is hovering the card.
72
+ * Defaults to `true` whenever `autoDismissMs` is set — gives users
73
+ * time to read/click without the card disappearing under their
74
+ * cursor. Pass `false` to keep the timer running unconditionally.
75
+ */
76
+ pauseOnHover?: boolean
63
77
  }
64
78
 
65
79
  /**
66
80
  * Card composition over the `Alert` primitive matching the Figma
67
81
  * Notification reference (`170:1839` — Error/Light/Large). Renders a
68
82
  * status icon (or custom `avatar`), a title + timestamp row, an
69
- * `opacity-72` description, and an optional row of action links. Use
83
+ * `opacity-70` description, and an optional row of action links. Use
70
84
  * with `NotificationStack` for corner-floating manual-dismiss UX, or
71
85
  * standalone inside a panel / dialog content area.
72
86
  *
@@ -89,9 +103,30 @@ export const Notification = React.forwardRef<HTMLDivElement, NotificationProps>(
89
103
  dismissAriaLabel,
90
104
  className,
91
105
  id,
106
+ autoDismissMs,
107
+ pauseOnHover,
92
108
  },
93
109
  ref,
94
110
  ) => {
111
+ const shouldPauseOnHover = pauseOnHover ?? autoDismissMs !== undefined
112
+ const [hovered, setHovered] = React.useState(false)
113
+
114
+ React.useEffect(() => {
115
+ if (autoDismissMs === undefined || autoDismissMs <= 0) return
116
+ if (shouldPauseOnHover && hovered) return
117
+ const timer = window.setTimeout(() => {
118
+ onDismiss?.()
119
+ }, autoDismissMs)
120
+ return () => window.clearTimeout(timer)
121
+ }, [autoDismissMs, hovered, onDismiss, shouldPauseOnHover])
122
+
123
+ const handleMouseEnter = React.useCallback(() => {
124
+ if (shouldPauseOnHover) setHovered(true)
125
+ }, [shouldPauseOnHover])
126
+ const handleMouseLeave = React.useCallback(() => {
127
+ if (shouldPauseOnHover) setHovered(false)
128
+ }, [shouldPauseOnHover])
129
+
95
130
  return (
96
131
  <Alert
97
132
  ref={ref}
@@ -105,6 +140,11 @@ export const Notification = React.forwardRef<HTMLDivElement, NotificationProps>(
105
140
  className={className}
106
141
  data-slot="notification"
107
142
  data-notification-id={id}
143
+ data-auto-dismiss-paused={
144
+ autoDismissMs !== undefined && shouldPauseOnHover && hovered ? 'true' : undefined
145
+ }
146
+ onMouseEnter={handleMouseEnter}
147
+ onMouseLeave={handleMouseLeave}
108
148
  >
109
149
  {(title || timestamp) && (
110
150
  <div className="flex items-start gap-2">
@@ -117,7 +157,7 @@ export const Notification = React.forwardRef<HTMLDivElement, NotificationProps>(
117
157
  </div>
118
158
  )}
119
159
  {description ? (
120
- <AlertDescription className={cn('opacity-72', title && 'mt-1')} data-slot="notification-description">
160
+ <AlertDescription className={cn('opacity-70', title && 'mt-1')} data-slot="notification-description">
121
161
  {description}
122
162
  </AlertDescription>
123
163
  ) : null}
@@ -0,0 +1,376 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import {
5
+ ChevronLeft,
6
+ ChevronRight,
7
+ ChevronsLeft,
8
+ ChevronsRight,
9
+ } from 'lucide-react'
10
+ import { cva, type VariantProps } from 'class-variance-authority'
11
+
12
+ import { cn } from '@open-mercato/shared/lib/utils'
13
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
14
+ import {
15
+ CompactSelectTrigger,
16
+ Select,
17
+ SelectContent,
18
+ SelectItem,
19
+ SelectValue,
20
+ } from './compact-select'
21
+
22
+ /**
23
+ * Page navigation primitive per Figma `Pagination Group [1.1]` (DS Open
24
+ * Mercato componentSet `199985:4135`).
25
+ *
26
+ * Layout (Figma `Basic` variant):
27
+ *
28
+ * [Left] "Page 2 of 16"
29
+ * [Center] ⏮ ◀ [1][2]…[N-1][N] ▶ ⏭
30
+ * [Right] "7 / page" CompactSelect
31
+ *
32
+ * Figma's three boolean variant props are mapped 1:1 to React props:
33
+ *
34
+ * 🥇 First / Last → `showFirstLast` (default true)
35
+ * ⏭️ Next / Previous → `showPrevNext` (default true)
36
+ * 🧪 Advanced → `showInfo` + `showPageSize` (default both true)
37
+ *
38
+ * The numeric page list uses the same ellipsis algorithm Material /
39
+ * MUI / shadcn ship: `boundaryCount` pages at each end (default 1),
40
+ * `siblingCount` pages on either side of the current page (default 1),
41
+ * collapse the rest into `…` placeholders.
42
+ *
43
+ * ```tsx
44
+ * const [page, setPage] = React.useState(1)
45
+ * const [pageSize, setPageSize] = React.useState(25)
46
+ * <Pagination
47
+ * page={page}
48
+ * pageSize={pageSize}
49
+ * total={items.length}
50
+ * onPageChange={setPage}
51
+ * onPageSizeChange={setPageSize}
52
+ * />
53
+ *
54
+ * // Compact (no first/last, no page-size select)
55
+ * <Pagination
56
+ * page={page}
57
+ * pageSize={20}
58
+ * total={120}
59
+ * onPageChange={setPage}
60
+ * showFirstLast={false}
61
+ * showPageSize={false}
62
+ * />
63
+ * ```
64
+ */
65
+
66
+ /**
67
+ * Build a stable list of pages + ellipsis placeholders that fits a
68
+ * pagination row of `boundaryCount + 2 + siblingCount * 2 + 1` cells.
69
+ * Returns `number` entries (1-indexed) and `'ellipsis-left'` /
70
+ * `'ellipsis-right'` placeholders that the renderer paints as `…`.
71
+ *
72
+ * Exported for tests + consumer reuse (e.g. a server-side rendered
73
+ * pagination indicator that needs the same shape).
74
+ */
75
+ export function buildPaginationItems(
76
+ page: number,
77
+ totalPages: number,
78
+ siblingCount: number,
79
+ boundaryCount: number,
80
+ ): Array<number | 'ellipsis-left' | 'ellipsis-right'> {
81
+ if (totalPages <= 0) return []
82
+ // When everything fits, just list every page.
83
+ const totalSlots = boundaryCount * 2 + siblingCount * 2 + 3
84
+ if (totalPages <= totalSlots) {
85
+ return Array.from({ length: totalPages }, (_, i) => i + 1)
86
+ }
87
+ const startPages = Array.from({ length: boundaryCount }, (_, i) => i + 1)
88
+ const endPages = Array.from(
89
+ { length: boundaryCount },
90
+ (_, i) => totalPages - boundaryCount + 1 + i,
91
+ )
92
+ const siblingStart = Math.max(
93
+ Math.min(page - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1),
94
+ boundaryCount + 2,
95
+ )
96
+ const siblingEnd = Math.min(
97
+ Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2),
98
+ endPages.length > 0 ? endPages[0] - 2 : totalPages - 1,
99
+ )
100
+ const middle: Array<number> = []
101
+ for (let i = siblingStart; i <= siblingEnd; i += 1) middle.push(i)
102
+
103
+ // Bridge between the start boundary and the sibling window. The
104
+ // ellipsis is only worth showing when ≥2 page numbers fall between
105
+ // them; if exactly 1 page is missing, render that single number
106
+ // instead of "…" (cleaner UX — same width, no information loss).
107
+ const result: Array<number | 'ellipsis-left' | 'ellipsis-right'> = []
108
+ for (const p of startPages) result.push(p)
109
+ if (siblingStart > boundaryCount + 2) {
110
+ result.push('ellipsis-left')
111
+ } else if (siblingStart === boundaryCount + 2) {
112
+ result.push(boundaryCount + 1)
113
+ }
114
+ for (const p of middle) result.push(p)
115
+ const firstEnd = endPages[0] ?? totalPages
116
+ if (siblingEnd < firstEnd - 2) {
117
+ result.push('ellipsis-right')
118
+ } else if (siblingEnd === firstEnd - 2) {
119
+ result.push(firstEnd - 1)
120
+ }
121
+ for (const p of endPages) result.push(p)
122
+ return result
123
+ }
124
+
125
+ const cellVariants = cva(
126
+ 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-sm font-medium outline-none transition-colors tabular-nums ' +
127
+ 'focus-visible:shadow-focus ' +
128
+ 'disabled:cursor-not-allowed disabled:opacity-50',
129
+ {
130
+ variants: {
131
+ selected: {
132
+ true: 'bg-muted text-foreground',
133
+ false: 'bg-background text-foreground hover:bg-muted/40',
134
+ },
135
+ },
136
+ defaultVariants: { selected: false },
137
+ },
138
+ )
139
+
140
+ const navButtonVariants = cva(
141
+ 'inline-flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground outline-none transition-colors ' +
142
+ 'hover:bg-muted/40 hover:text-foreground ' +
143
+ 'focus-visible:shadow-focus ' +
144
+ 'disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-muted-foreground',
145
+ )
146
+
147
+ export type PaginationProps = React.HTMLAttributes<HTMLDivElement> & {
148
+ /** Current 1-indexed page. */
149
+ page: number
150
+ /** Items per page. */
151
+ pageSize: number
152
+ /** Total item count. Used to derive `Math.ceil(total / pageSize)` pages. */
153
+ total: number
154
+ /** Called when the user changes page. */
155
+ onPageChange: (next: number) => void
156
+ /** Called when the user changes page size. Optional — when omitted,
157
+ * the "X / page" select is hidden. */
158
+ onPageSizeChange?: (next: number) => void
159
+ /** Page size options for the select. Default `[10, 25, 50, 100]`. */
160
+ pageSizeOptions?: readonly number[]
161
+ /** Show the left "Page X of Y" indicator. Default `true`. */
162
+ showInfo?: boolean
163
+ /** Show the right "X / page" select. Default `true` when
164
+ * `onPageSizeChange` is provided; ignored otherwise. */
165
+ showPageSize?: boolean
166
+ /** Show ⏮ / ⏭ first / last buttons. Default `true`. */
167
+ showFirstLast?: boolean
168
+ /** Show ◀ / ▶ prev / next buttons. Default `true`. */
169
+ showPrevNext?: boolean
170
+ /** Pages on either side of the current page in the page list. Default `1`. */
171
+ siblingCount?: number
172
+ /** Pages pinned at each end of the page list. Default `1`. */
173
+ boundaryCount?: number
174
+ /** Block all interactions. Default `false`. */
175
+ disabled?: boolean
176
+ /** ARIA label for the navigation landmark. Default `"Pagination"`. */
177
+ 'aria-label'?: string
178
+ /** Format the "Page X of Y" label. */
179
+ formatPageInfo?: (page: number, totalPages: number) => string
180
+ /** Format the "X / page" label. */
181
+ formatPageSizeLabel?: (pageSize: number) => string
182
+ }
183
+
184
+ export const Pagination = React.forwardRef<HTMLDivElement, PaginationProps>(
185
+ (
186
+ {
187
+ className,
188
+ page,
189
+ pageSize,
190
+ total,
191
+ onPageChange,
192
+ onPageSizeChange,
193
+ pageSizeOptions = [10, 25, 50, 100],
194
+ showInfo = true,
195
+ showPageSize: showPageSizeProp,
196
+ showFirstLast = true,
197
+ showPrevNext = true,
198
+ siblingCount = 1,
199
+ boundaryCount = 1,
200
+ disabled = false,
201
+ formatPageInfo,
202
+ formatPageSizeLabel,
203
+ ...props
204
+ },
205
+ ref,
206
+ ) => {
207
+ const t = useT()
208
+ const resolvedFormatPageInfo =
209
+ formatPageInfo ??
210
+ ((p: number, total: number) =>
211
+ t('ui.pagination.info.pageOf', 'Page {page} of {total}', { page: p, total }))
212
+ const resolvedFormatPageSizeLabel =
213
+ formatPageSizeLabel ??
214
+ ((size: number) =>
215
+ t('ui.pagination.itemsPerPage.label', '{size} / page', { size }))
216
+ const totalPages = Math.max(1, Math.ceil(total / Math.max(1, pageSize)))
217
+ const safePage = Math.min(Math.max(1, page), totalPages)
218
+ const items = React.useMemo(
219
+ () => buildPaginationItems(safePage, totalPages, siblingCount, boundaryCount),
220
+ [safePage, totalPages, siblingCount, boundaryCount],
221
+ )
222
+ const showPageSize = showPageSizeProp ?? Boolean(onPageSizeChange)
223
+
224
+ const goTo = React.useCallback(
225
+ (next: number) => {
226
+ if (disabled) return
227
+ const bounded = Math.min(Math.max(1, next), totalPages)
228
+ if (bounded !== safePage) onPageChange(bounded)
229
+ },
230
+ [disabled, onPageChange, safePage, totalPages],
231
+ )
232
+
233
+ return (
234
+ <nav
235
+ ref={ref}
236
+ data-slot="pagination"
237
+ aria-label={props['aria-label'] ?? t('ui.pagination.landmark.ariaLabel', 'Pagination')}
238
+ className={cn('flex w-full items-center justify-between gap-6', className)}
239
+ {...props}
240
+ >
241
+ {showInfo ? (
242
+ <div
243
+ data-slot="pagination-info"
244
+ className="shrink-0 text-sm text-muted-foreground tabular-nums"
245
+ >
246
+ {resolvedFormatPageInfo(safePage, totalPages)}
247
+ </div>
248
+ ) : (
249
+ <div />
250
+ )}
251
+
252
+ <div
253
+ data-slot="pagination-controls"
254
+ className="inline-flex items-center gap-2"
255
+ >
256
+ {showFirstLast ? (
257
+ <button
258
+ type="button"
259
+ data-slot="pagination-first"
260
+ aria-label={t('ui.pagination.first.ariaLabel', 'First page')}
261
+ disabled={disabled || safePage <= 1}
262
+ onClick={() => goTo(1)}
263
+ className={cn(navButtonVariants())}
264
+ >
265
+ <ChevronsLeft aria-hidden="true" className="size-5" />
266
+ </button>
267
+ ) : null}
268
+ {showPrevNext ? (
269
+ <button
270
+ type="button"
271
+ data-slot="pagination-prev"
272
+ aria-label={t('ui.pagination.previous.ariaLabel', 'Previous page')}
273
+ disabled={disabled || safePage <= 1}
274
+ onClick={() => goTo(safePage - 1)}
275
+ className={cn(navButtonVariants())}
276
+ >
277
+ <ChevronLeft aria-hidden="true" className="size-5" />
278
+ </button>
279
+ ) : null}
280
+
281
+ <ol
282
+ data-slot="pagination-pages"
283
+ className="inline-flex items-center gap-2 list-none"
284
+ >
285
+ {items.map((entry, index) => {
286
+ if (entry === 'ellipsis-left' || entry === 'ellipsis-right') {
287
+ return (
288
+ <li
289
+ key={`${entry}-${index}`}
290
+ data-slot="pagination-ellipsis"
291
+ aria-hidden="true"
292
+ className="inline-flex size-8 items-center justify-center text-sm text-muted-foreground"
293
+ >
294
+ …
295
+ </li>
296
+ )
297
+ }
298
+ const selected = entry === safePage
299
+ return (
300
+ <li key={entry}>
301
+ <button
302
+ type="button"
303
+ data-slot="pagination-page"
304
+ data-state={selected ? 'on' : 'off'}
305
+ aria-current={selected ? 'page' : undefined}
306
+ aria-label={
307
+ selected
308
+ ? t('ui.pagination.page.currentAriaLabel', 'Page {page}, current page', { page: entry })
309
+ : t('ui.pagination.page.goToAriaLabel', 'Go to page {page}', { page: entry })
310
+ }
311
+ disabled={disabled}
312
+ onClick={() => goTo(entry)}
313
+ className={cn(cellVariants({ selected }))}
314
+ >
315
+ {entry}
316
+ </button>
317
+ </li>
318
+ )
319
+ })}
320
+ </ol>
321
+
322
+ {showPrevNext ? (
323
+ <button
324
+ type="button"
325
+ data-slot="pagination-next"
326
+ aria-label={t('ui.pagination.next.ariaLabel', 'Next page')}
327
+ disabled={disabled || safePage >= totalPages}
328
+ onClick={() => goTo(safePage + 1)}
329
+ className={cn(navButtonVariants())}
330
+ >
331
+ <ChevronRight aria-hidden="true" className="size-5" />
332
+ </button>
333
+ ) : null}
334
+ {showFirstLast ? (
335
+ <button
336
+ type="button"
337
+ data-slot="pagination-last"
338
+ aria-label={t('ui.pagination.last.ariaLabel', 'Last page')}
339
+ disabled={disabled || safePage >= totalPages}
340
+ onClick={() => goTo(totalPages)}
341
+ className={cn(navButtonVariants())}
342
+ >
343
+ <ChevronsRight aria-hidden="true" className="size-5" />
344
+ </button>
345
+ ) : null}
346
+ </div>
347
+
348
+ {showPageSize && onPageSizeChange ? (
349
+ <div data-slot="pagination-page-size" className="shrink-0">
350
+ <Select
351
+ value={String(pageSize)}
352
+ onValueChange={(next) => onPageSizeChange(Number(next))}
353
+ disabled={disabled}
354
+ >
355
+ <CompactSelectTrigger aria-label={t('ui.pagination.itemsPerPage.ariaLabel', 'Items per page')}>
356
+ <SelectValue />
357
+ </CompactSelectTrigger>
358
+ <SelectContent>
359
+ {pageSizeOptions.map((option) => (
360
+ <SelectItem key={option} value={String(option)}>
361
+ {resolvedFormatPageSizeLabel(option)}
362
+ </SelectItem>
363
+ ))}
364
+ </SelectContent>
365
+ </Select>
366
+ </div>
367
+ ) : (
368
+ <div />
369
+ )}
370
+ </nav>
371
+ )
372
+ },
373
+ )
374
+ Pagination.displayName = 'Pagination'
375
+
376
+ export { cellVariants as paginationCellVariants, navButtonVariants as paginationNavVariants }