@underverse-ui/underverse 0.2.42 → 0.2.44

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/dist/index.cjs CHANGED
@@ -5118,13 +5118,11 @@ var Pagination = ({
5118
5118
  onChange,
5119
5119
  className,
5120
5120
  size = "md",
5121
- variant = "outline",
5122
5121
  showFirstLast = true,
5123
5122
  showPrevNext = true,
5124
5123
  showPageNumbers = true,
5125
5124
  showInfo = false,
5126
5125
  disabled = false,
5127
- alignment = "left",
5128
5126
  pageSize,
5129
5127
  pageSizeOptions,
5130
5128
  onPageSizeChange,
@@ -5132,35 +5130,6 @@ var Pagination = ({
5132
5130
  labels
5133
5131
  }) => {
5134
5132
  const t = useTranslations("Pagination");
5135
- const getTextAlignmentClass = (align) => {
5136
- switch (align) {
5137
- case "left":
5138
- return "text-left";
5139
- case "center":
5140
- return "text-center";
5141
- case "right":
5142
- return "text-right";
5143
- }
5144
- };
5145
- const textAlignmentClass = getTextAlignmentClass(alignment);
5146
- const createPageArray = () => {
5147
- const delta = 2;
5148
- const range = [];
5149
- const left = Math.max(2, page - delta);
5150
- const right = Math.min(totalPages - 1, page + delta);
5151
- range.push(1);
5152
- if (left > 2) range.push("...");
5153
- for (let i = left; i <= right; i++) {
5154
- range.push(i);
5155
- }
5156
- if (right < totalPages - 1) range.push("...");
5157
- if (totalPages > 1) range.push(totalPages);
5158
- return range;
5159
- };
5160
- const getButtonVariant = (isActive) => {
5161
- if (isActive) return "primary";
5162
- return variant === "default" ? "default" : variant;
5163
- };
5164
5133
  React21.useEffect(() => {
5165
5134
  if (disabled) return;
5166
5135
  const handleKey = (e) => {
@@ -5194,116 +5163,135 @@ var Pagination = ({
5194
5163
  }
5195
5164
  };
5196
5165
  if (totalPages <= 1) return null;
5197
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("nav", { className: cn("flex flex-col gap-4", className), "aria-label": labels?.navigationLabel || t("navigationLabel"), children: [
5198
- showInfo && totalItems && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn("text-sm text-muted-foreground", textAlignmentClass), children: labels?.showingResults ? labels.showingResults({ startItem: startItem || 0, endItem: endItem || 0, totalItems }) : t("showingResults", { startItem: startItem || 0, endItem: endItem || 0, totalItems }) }),
5199
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
5200
- "div",
5201
- {
5202
- className: cn("flex items-center justify-between", {
5203
- "flex-row-reverse": alignment === "right" || alignment === "center"
5204
- }),
5205
- children: [
5206
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-center gap-1"), children: [
5207
- showFirstLast && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5208
- Button_default,
5209
- {
5210
- variant: getButtonVariant(false),
5211
- size,
5212
- icon: import_lucide_react13.ChevronsLeft,
5213
- onClick: () => onChange(1),
5214
- disabled: disabled || page === 1,
5215
- className: "hidden sm:flex",
5216
- title: labels?.firstPage || t("firstPage"),
5217
- "aria-label": labels?.firstPage || t("firstPage"),
5218
- "aria-disabled": disabled || page === 1
5219
- }
5220
- ),
5221
- showPrevNext && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5222
- Button_default,
5223
- {
5224
- variant: getButtonVariant(false),
5225
- size,
5226
- icon: import_lucide_react13.ChevronLeft,
5227
- onClick: () => onChange(Math.max(1, page - 1)),
5228
- disabled: disabled || page === 1,
5229
- title: labels?.previousPage || t("previousPage"),
5230
- "aria-label": labels?.previousPage || t("previousPage"),
5231
- "aria-disabled": disabled || page === 1,
5232
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "hidden sm:inline", children: labels?.previous || t("previous") })
5233
- }
5234
- ),
5235
- showPageNumbers && createPageArray().map((p, i) => {
5236
- if (p === "...") {
5237
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Button_default, { variant: "ghost", size, disabled: true, icon: import_lucide_react13.MoreHorizontal, className: "cursor-default" }, i);
5238
- }
5239
- const pageNumber = p;
5240
- const isActive = page === pageNumber;
5241
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5242
- Button_default,
5243
- {
5244
- variant: getButtonVariant(isActive),
5245
- size,
5246
- onClick: () => onChange(pageNumber),
5247
- disabled,
5248
- className: cn("min-w-10", isActive && "font-semibold"),
5249
- "aria-label": labels?.pageNumber ? labels.pageNumber(pageNumber) : t("pageNumber", { page: pageNumber }),
5250
- "aria-current": isActive ? "page" : void 0,
5251
- children: pageNumber
5252
- },
5253
- i
5254
- );
5255
- }),
5256
- showPrevNext && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5257
- Button_default,
5258
- {
5259
- variant: getButtonVariant(false),
5260
- size,
5261
- iconRight: import_lucide_react13.ChevronRight,
5262
- onClick: () => onChange(Math.min(totalPages, page + 1)),
5263
- disabled: disabled || page === totalPages,
5264
- title: labels?.nextPage || t("nextPage"),
5265
- "aria-label": labels?.nextPage || t("nextPage"),
5266
- "aria-disabled": disabled || page === totalPages,
5267
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "hidden sm:inline", children: labels?.next || t("next") })
5268
- }
5269
- ),
5270
- showFirstLast && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5271
- Button_default,
5166
+ const createCompactPageArray = () => {
5167
+ const pages = [];
5168
+ if (totalPages <= 5) {
5169
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
5170
+ } else {
5171
+ pages.push(1);
5172
+ if (page > 3) pages.push("...");
5173
+ const start = Math.max(2, page - 1);
5174
+ const end = Math.min(totalPages - 1, page + 1);
5175
+ for (let i = start; i <= end; i++) pages.push(i);
5176
+ if (page < totalPages - 2) pages.push("...");
5177
+ pages.push(totalPages);
5178
+ }
5179
+ return pages;
5180
+ };
5181
+ const sizeClasses2 = {
5182
+ sm: { btn: "h-7 w-7", text: "text-xs", page: "h-7 min-w-7 px-1.5" },
5183
+ md: { btn: "h-8 w-8", text: "text-sm", page: "h-8 min-w-8 px-2" },
5184
+ lg: { btn: "h-9 w-9", text: "text-base", page: "h-9 min-w-9 px-2.5" }
5185
+ };
5186
+ const sizeClass = sizeClasses2[size];
5187
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
5188
+ "nav",
5189
+ {
5190
+ className: cn("flex items-center justify-between gap-2", sizeClass.text, "text-muted-foreground", className),
5191
+ "aria-label": labels?.navigationLabel || t("navigationLabel"),
5192
+ children: [
5193
+ showInfo && totalItems && startItem && endItem ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "tabular-nums shrink-0", children: [
5194
+ startItem,
5195
+ "-",
5196
+ endItem,
5197
+ "/",
5198
+ totalItems
5199
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", {}),
5200
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", children: [
5201
+ showFirstLast && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5202
+ "button",
5203
+ {
5204
+ onClick: () => onChange(1),
5205
+ disabled: disabled || page === 1,
5206
+ className: cn(
5207
+ sizeClass.btn,
5208
+ "p-0 rounded transition-colors hidden sm:flex items-center justify-center",
5209
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5210
+ ),
5211
+ title: labels?.firstPage || t("firstPage"),
5212
+ "aria-label": labels?.firstPage || t("firstPage"),
5213
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronsLeft, { className: "h-4 w-4" })
5214
+ }
5215
+ ),
5216
+ showPrevNext && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5217
+ "button",
5218
+ {
5219
+ onClick: () => onChange(Math.max(1, page - 1)),
5220
+ disabled: disabled || page === 1,
5221
+ className: cn(
5222
+ sizeClass.btn,
5223
+ "p-0 rounded transition-colors flex items-center justify-center",
5224
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5225
+ ),
5226
+ title: labels?.previousPage || t("previousPage"),
5227
+ "aria-label": labels?.previousPage || t("previousPage"),
5228
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronLeft, { className: "h-4 w-4" })
5229
+ }
5230
+ ),
5231
+ showPageNumbers && createCompactPageArray().map(
5232
+ (p, i) => p === "..." ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "px-1 text-muted-foreground/60", children: "\u2026" }, `dots-${i}`) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5233
+ "button",
5272
5234
  {
5273
- variant: getButtonVariant(false),
5274
- size,
5275
- icon: import_lucide_react13.ChevronsRight,
5276
- onClick: () => onChange(totalPages),
5277
- disabled: disabled || page === totalPages,
5278
- className: "hidden sm:flex",
5279
- title: labels?.lastPage || t("lastPage"),
5280
- "aria-label": labels?.lastPage || t("lastPage"),
5281
- "aria-disabled": disabled || page === totalPages
5282
- }
5235
+ onClick: () => onChange(p),
5236
+ disabled,
5237
+ className: cn(
5238
+ sizeClass.page,
5239
+ "rounded font-medium transition-colors",
5240
+ page === p ? "bg-primary text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground",
5241
+ disabled && "opacity-40 cursor-not-allowed"
5242
+ ),
5243
+ "aria-label": labels?.pageNumber ? labels.pageNumber(p) : t("pageNumber", { page: p }),
5244
+ "aria-current": page === p ? "page" : void 0,
5245
+ children: p
5246
+ },
5247
+ p
5283
5248
  )
5284
- ] }),
5285
- pageSizeOptions && onPageSizeChange && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-center gap-2 text-sm"), children: [
5286
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "text-muted-foreground", children: [
5287
- labels?.itemsPerPage || t("itemsPerPage"),
5288
- ":"
5289
- ] }),
5290
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "w-20", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5291
- Combobox,
5292
- {
5293
- options: pageSizeOptionsStrings,
5294
- value: pageSize?.toString() || "10",
5295
- onChange: handlePageSizeChange,
5296
- placeholder: "10",
5297
- searchPlaceholder: labels?.search || t("search"),
5298
- emptyText: labels?.noOptions || t("noOptions"),
5299
- disabled
5300
- }
5301
- ) })
5302
- ] })
5303
- ]
5304
- }
5305
- )
5306
- ] });
5249
+ ),
5250
+ showPrevNext && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5251
+ "button",
5252
+ {
5253
+ onClick: () => onChange(Math.min(totalPages, page + 1)),
5254
+ disabled: disabled || page === totalPages,
5255
+ className: cn(
5256
+ sizeClass.btn,
5257
+ "p-0 rounded transition-colors flex items-center justify-center",
5258
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5259
+ ),
5260
+ title: labels?.nextPage || t("nextPage"),
5261
+ "aria-label": labels?.nextPage || t("nextPage"),
5262
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronRight, { className: "h-4 w-4" })
5263
+ }
5264
+ ),
5265
+ showFirstLast && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5266
+ "button",
5267
+ {
5268
+ onClick: () => onChange(totalPages),
5269
+ disabled: disabled || page === totalPages,
5270
+ className: cn(
5271
+ sizeClass.btn,
5272
+ "p-0 rounded transition-colors hidden sm:flex items-center justify-center",
5273
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5274
+ ),
5275
+ title: labels?.lastPage || t("lastPage"),
5276
+ "aria-label": labels?.lastPage || t("lastPage"),
5277
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronsRight, { className: "h-4 w-4" })
5278
+ }
5279
+ )
5280
+ ] }),
5281
+ pageSizeOptions && onPageSizeChange ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5282
+ Combobox,
5283
+ {
5284
+ options: pageSizeOptionsStrings,
5285
+ value: pageSize?.toString() || "10",
5286
+ onChange: handlePageSizeChange,
5287
+ size: "sm",
5288
+ className: "w-14",
5289
+ disabled
5290
+ }
5291
+ ) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", {})
5292
+ ]
5293
+ }
5294
+ );
5307
5295
  };
5308
5296
  var SimplePagination = ({
5309
5297
  page,
@@ -5311,103 +5299,128 @@ var SimplePagination = ({
5311
5299
  onChange,
5312
5300
  className,
5313
5301
  size = "md",
5314
- variant = "outline",
5315
5302
  disabled = false,
5316
5303
  showInfo = false,
5317
5304
  totalItems,
5318
5305
  pageSize = 10
5319
5306
  }) => {
5320
5307
  if (totalPages <= 1) return null;
5321
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex flex-col gap-2", className), children: [
5322
- showInfo && totalItems && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "text-sm text-muted-foreground text-center", children: [
5323
- "Page ",
5324
- page,
5325
- " of ",
5326
- totalPages,
5327
- " (",
5328
- totalItems,
5329
- " total items)"
5330
- ] }),
5331
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between", children: [
5332
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Button_default, { variant, size, icon: import_lucide_react13.ChevronLeft, onClick: () => onChange(Math.max(1, page - 1)), disabled: disabled || page === 1, children: "Previous" }),
5333
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
5334
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: "Page" }),
5335
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "font-medium text-foreground", children: page }),
5336
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: "of" }),
5337
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "font-medium text-foreground", children: totalPages })
5308
+ const sizeClasses2 = {
5309
+ sm: { btn: "h-7 w-7", text: "text-xs" },
5310
+ md: { btn: "h-8 w-8", text: "text-sm" },
5311
+ lg: { btn: "h-9 w-9", text: "text-base" }
5312
+ };
5313
+ const sizeClass = sizeClasses2[size];
5314
+ const startItem = totalItems ? (page - 1) * pageSize + 1 : null;
5315
+ const endItem = totalItems ? Math.min(page * pageSize, totalItems) : null;
5316
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-center justify-between gap-2", sizeClass.text, "text-muted-foreground", className), children: [
5317
+ showInfo && totalItems && startItem && endItem ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "tabular-nums", children: [
5318
+ startItem,
5319
+ "-",
5320
+ endItem,
5321
+ "/",
5322
+ totalItems
5323
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", {}),
5324
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", children: [
5325
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5326
+ "button",
5327
+ {
5328
+ onClick: () => onChange(Math.max(1, page - 1)),
5329
+ disabled: disabled || page === 1,
5330
+ className: cn(
5331
+ sizeClass.btn,
5332
+ "p-0 rounded transition-colors flex items-center justify-center",
5333
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5334
+ ),
5335
+ "aria-label": "Previous page",
5336
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronLeft, { className: "h-4 w-4" })
5337
+ }
5338
+ ),
5339
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "px-2 tabular-nums font-medium text-foreground", children: [
5340
+ page,
5341
+ "/",
5342
+ totalPages
5338
5343
  ] }),
5339
5344
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5340
- Button_default,
5345
+ "button",
5341
5346
  {
5342
- variant,
5343
- size,
5344
- iconRight: import_lucide_react13.ChevronRight,
5345
5347
  onClick: () => onChange(Math.min(totalPages, page + 1)),
5346
5348
  disabled: disabled || page === totalPages,
5347
- children: "Next"
5349
+ className: cn(
5350
+ sizeClass.btn,
5351
+ "p-0 rounded transition-colors flex items-center justify-center",
5352
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5353
+ ),
5354
+ "aria-label": "Next page",
5355
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronRight, { className: "h-4 w-4" })
5348
5356
  }
5349
5357
  )
5350
- ] })
5358
+ ] }),
5359
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", {})
5351
5360
  ] });
5352
5361
  };
5353
- var CompactPagination = ({
5354
- page,
5355
- totalPages,
5356
- onChange,
5357
- className,
5358
- variant = "outline",
5359
- disabled = false
5360
- }) => {
5362
+ var CompactPagination = ({ page, totalPages, onChange, className, disabled = false }) => {
5361
5363
  if (totalPages <= 1) return null;
5362
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("nav", { className: cn("flex items-center gap-1", className), "aria-label": "Compact Pagination", children: [
5364
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("nav", { className: cn("flex items-center gap-0.5 text-xs text-muted-foreground", className), "aria-label": "Compact Pagination", children: [
5363
5365
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5364
- Button_default,
5366
+ "button",
5365
5367
  {
5366
- variant,
5367
- size: "icon",
5368
- icon: import_lucide_react13.ChevronsLeft,
5369
5368
  onClick: () => onChange(1),
5370
5369
  disabled: disabled || page === 1,
5370
+ className: cn(
5371
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5372
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5373
+ ),
5371
5374
  title: "First page",
5372
- "aria-label": "First page"
5375
+ "aria-label": "First page",
5376
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronsLeft, { className: "h-3.5 w-3.5" })
5373
5377
  }
5374
5378
  ),
5375
5379
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5376
- Button_default,
5380
+ "button",
5377
5381
  {
5378
- variant,
5379
- size: "icon",
5380
- icon: import_lucide_react13.ChevronLeft,
5381
5382
  onClick: () => onChange(Math.max(1, page - 1)),
5382
5383
  disabled: disabled || page === 1,
5383
- title: "Previous page"
5384
+ className: cn(
5385
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5386
+ disabled || page === 1 ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5387
+ ),
5388
+ title: "Previous page",
5389
+ "aria-label": "Previous page",
5390
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronLeft, { className: "h-3.5 w-3.5" })
5384
5391
  }
5385
5392
  ),
5386
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "px-2 py-1 text-sm text-muted-foreground min-w-16 text-center", children: [
5393
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "px-1.5 tabular-nums", children: [
5387
5394
  page,
5388
- " / ",
5395
+ "/",
5389
5396
  totalPages
5390
5397
  ] }),
5391
5398
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5392
- Button_default,
5399
+ "button",
5393
5400
  {
5394
- variant,
5395
- size: "icon",
5396
- icon: import_lucide_react13.ChevronRight,
5397
5401
  onClick: () => onChange(Math.min(totalPages, page + 1)),
5398
5402
  disabled: disabled || page === totalPages,
5399
- title: "Next page"
5403
+ className: cn(
5404
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5405
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5406
+ ),
5407
+ title: "Next page",
5408
+ "aria-label": "Next page",
5409
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronRight, { className: "h-3.5 w-3.5" })
5400
5410
  }
5401
5411
  ),
5402
5412
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
5403
- Button_default,
5413
+ "button",
5404
5414
  {
5405
- variant,
5406
- size: "icon",
5407
- icon: import_lucide_react13.ChevronsRight,
5408
5415
  onClick: () => onChange(totalPages),
5409
5416
  disabled: disabled || page === totalPages,
5410
- title: "Last page"
5417
+ className: cn(
5418
+ "h-6 w-6 p-0 rounded transition-colors flex items-center justify-center",
5419
+ disabled || page === totalPages ? "opacity-40 cursor-not-allowed" : "hover:bg-accent hover:text-accent-foreground"
5420
+ ),
5421
+ title: "Last page",
5422
+ "aria-label": "Last page",
5423
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react13.ChevronsRight, { className: "h-3.5 w-3.5" })
5411
5424
  }
5412
5425
  )
5413
5426
  ] });
@@ -5516,7 +5529,8 @@ var DatePicker = ({
5516
5529
  required,
5517
5530
  todayLabel,
5518
5531
  clearLabel,
5519
- weekdayLabels
5532
+ weekdayLabels,
5533
+ disablePastDates = false
5520
5534
  }) => {
5521
5535
  const t = useTranslations("DatePicker");
5522
5536
  const locale = useLocale();
@@ -5620,17 +5634,21 @@ var DatePicker = ({
5620
5634
  for (let i = 0; i < firstDayOfMonth; i++) {
5621
5635
  days.push(/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-8 h-8" }, `empty-${i}`));
5622
5636
  }
5637
+ const today = /* @__PURE__ */ new Date();
5638
+ today.setHours(0, 0, 0, 0);
5623
5639
  for (let day = 1; day <= daysInMonth; day++) {
5624
5640
  const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), day);
5625
5641
  const isSelected = value && date.getDate() === value.getDate() && date.getMonth() === value.getMonth() && date.getFullYear() === value.getFullYear();
5626
5642
  const isToday2 = date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
5643
+ const isPastDate = disablePastDates && date < today;
5627
5644
  const totalDaysFromStart = firstDayOfMonth + day - 1;
5628
5645
  const rowIndex = Math.floor(totalDaysFromStart / 7);
5629
5646
  days.push(
5630
5647
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5631
5648
  "button",
5632
5649
  {
5633
- onClick: () => handleDateSelect(date),
5650
+ onClick: () => !isPastDate && handleDateSelect(date),
5651
+ disabled: isPastDate,
5634
5652
  style: {
5635
5653
  animationDelay: isOpen ? `${rowIndex * 50}ms` : "0ms"
5636
5654
  },
@@ -5638,7 +5656,8 @@ var DatePicker = ({
5638
5656
  size === "sm" ? "w-7 h-7 text-[12px]" : "w-8 h-8 text-sm",
5639
5657
  "datepicker-day rounded-md focus:outline-none",
5640
5658
  "transition-colors",
5641
- isSelected ? "bg-primary! text-primary-foreground font-bold ring-2 ring-primary-foreground/60 shadow-lg scale-105 z-10 hover:bg-primary! focus:bg-primary! focus:text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
5659
+ isPastDate && "opacity-40 cursor-not-allowed text-muted-foreground",
5660
+ isSelected ? "bg-primary! text-primary-foreground font-bold ring-2 ring-primary-foreground/60 shadow-lg scale-105 z-10 hover:bg-primary! focus:bg-primary! focus:text-primary-foreground" : !isPastDate && "hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
5642
5661
  isToday2 && !isSelected && "bg-accent text-accent-foreground font-semibold"
5643
5662
  ),
5644
5663
  children: day
@@ -5789,7 +5808,7 @@ var DatePicker = ({
5789
5808
  isOpen && dropdownPosition && typeof window !== "undefined" && (0, import_react_dom7.createPortal)(datePickerContent, document.body)
5790
5809
  ] });
5791
5810
  };
5792
- var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select date range...", className }) => {
5811
+ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select date range...", className, disablePastDates = false }) => {
5793
5812
  const locale = useLocale();
5794
5813
  const [isOpen, setIsOpen] = React23.useState(false);
5795
5814
  const [dropdownPosition, setDropdownPosition] = React23.useState(null);
@@ -5850,16 +5869,17 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5850
5869
  const getDaysInMonth = (d) => new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
5851
5870
  const getFirstDayOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1).getDay();
5852
5871
  const handleSelect = (date) => {
5872
+ const localDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
5853
5873
  if (!tempStart || tempStart && tempEnd) {
5854
- setTempStart(date);
5874
+ setTempStart(localDate);
5855
5875
  setTempEnd(null);
5856
5876
  setHoveredDate(null);
5857
5877
  } else if (tempStart && !tempEnd) {
5858
- if (date < tempStart) {
5859
- setTempStart(date);
5878
+ if (localDate < tempStart) {
5879
+ setTempStart(localDate);
5860
5880
  } else {
5861
- setTempEnd(date);
5862
- onChange(tempStart, date);
5881
+ setTempEnd(localDate);
5882
+ onChange(tempStart, localDate);
5863
5883
  setIsOpen(false);
5864
5884
  }
5865
5885
  }
@@ -5868,9 +5888,12 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5868
5888
  const nodes = [];
5869
5889
  const daysInMonth = getDaysInMonth(viewDate);
5870
5890
  const firstDay = getFirstDayOfMonth(viewDate);
5891
+ const today = /* @__PURE__ */ new Date();
5892
+ today.setHours(0, 0, 0, 0);
5871
5893
  for (let i = 0; i < firstDay; i++) nodes.push(/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "w-8 h-8" }, `e-${i}`));
5872
5894
  for (let d = 1; d <= daysInMonth; d++) {
5873
5895
  const date = new Date(viewDate.getFullYear(), viewDate.getMonth(), d);
5896
+ const isPastDate = disablePastDates && date < today;
5874
5897
  const isSelectedStart = isSameDay2(date, tempStart);
5875
5898
  const isSelectedEnd = isSameDay2(date, tempEnd);
5876
5899
  const isHovering = hoveredDate && tempStart && !tempEnd;
@@ -5896,13 +5919,16 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5896
5919
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
5897
5920
  "button",
5898
5921
  {
5899
- onClick: () => handleSelect(date),
5900
- onMouseEnter: () => tempStart && !tempEnd && setHoveredDate(date),
5922
+ onClick: () => !isPastDate && handleSelect(date),
5923
+ disabled: isPastDate,
5924
+ onMouseEnter: () => !isPastDate && tempStart && !tempEnd && setHoveredDate(date),
5901
5925
  onMouseLeave: () => tempStart && !tempEnd && setHoveredDate(null),
5902
5926
  className: cn(
5903
5927
  "w-8 h-8 text-sm transition-all duration-200 focus:outline-none relative font-medium",
5928
+ // Disabled/past date state
5929
+ isPastDate && "opacity-40 cursor-not-allowed text-muted-foreground",
5904
5930
  // Default state
5905
- !isInRange && !isRangeStart && !isRangeEnd && "hover:bg-accent hover:text-accent-foreground rounded-md",
5931
+ !isPastDate && !isInRange && !isRangeStart && !isRangeEnd && "hover:bg-accent hover:text-accent-foreground rounded-md",
5906
5932
  // Range selection styling - smooth continuous background
5907
5933
  isInRange && "bg-primary/15 text-foreground shadow-sm",
5908
5934
  (isRangeStart || isRangeEnd) && "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm",
@@ -5912,8 +5938,8 @@ var DateRangePicker = ({ startDate, endDate, onChange, placeholder = "Select dat
5912
5938
  isRangeStart && isRangeEnd && "rounded-md",
5913
5939
  // Single day selection
5914
5940
  // Hover effects for range
5915
- isInRange && "hover:bg-primary/25",
5916
- "focus:bg-accent focus:text-accent-foreground focus:z-10 focus:shadow-md"
5941
+ isInRange && !isPastDate && "hover:bg-primary/25",
5942
+ !isPastDate && "focus:bg-accent focus:text-accent-foreground focus:z-10 focus:shadow-md"
5917
5943
  ),
5918
5944
  children: d
5919
5945
  },
@@ -8093,9 +8119,23 @@ function OverlayControls({
8093
8119
  var import_react18 = require("react");
8094
8120
  var import_lucide_react19 = require("lucide-react");
8095
8121
  var import_jsx_runtime36 = require("react/jsx-runtime");
8096
- function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1ECDn danh m\u1EE5c", disabled }) {
8122
+ var defaultLabels = {
8123
+ emptyText: "No categories",
8124
+ selectedText: (count) => `${count} selected`
8125
+ };
8126
+ function CategoryTreeSelect({
8127
+ categories,
8128
+ value = [],
8129
+ onChange,
8130
+ placeholder = "Select category",
8131
+ disabled,
8132
+ viewOnly = false,
8133
+ defaultExpanded = false,
8134
+ labels
8135
+ }) {
8097
8136
  const [isOpen, setIsOpen] = (0, import_react18.useState)(false);
8098
8137
  const [expandedNodes, setExpandedNodes] = (0, import_react18.useState)(/* @__PURE__ */ new Set());
8138
+ const mergedLabels = { ...defaultLabels, ...labels };
8099
8139
  const parentCategories = categories.filter((c) => !c.parent_id);
8100
8140
  const childrenMap = /* @__PURE__ */ new Map();
8101
8141
  categories.forEach((cat) => {
@@ -8106,6 +8146,12 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8106
8146
  childrenMap.get(cat.parent_id).push(cat);
8107
8147
  }
8108
8148
  });
8149
+ (0, import_react18.useEffect)(() => {
8150
+ if (viewOnly && defaultExpanded) {
8151
+ const allParentIds = categories.filter((c) => childrenMap.has(c.id)).map((c) => c.id);
8152
+ setExpandedNodes(new Set(allParentIds));
8153
+ }
8154
+ }, [viewOnly, defaultExpanded, categories]);
8109
8155
  const toggleExpand = (id) => {
8110
8156
  const newExpanded = new Set(expandedNodes);
8111
8157
  if (newExpanded.has(id)) {
@@ -8116,6 +8162,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8116
8162
  setExpandedNodes(newExpanded);
8117
8163
  };
8118
8164
  const handleSelect = (categoryId, category) => {
8165
+ if (viewOnly || !onChange) return;
8119
8166
  const newSelected = new Set(value);
8120
8167
  if (newSelected.has(categoryId)) {
8121
8168
  newSelected.delete(categoryId);
@@ -8139,20 +8186,14 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8139
8186
  "div",
8140
8187
  {
8141
8188
  className: cn(
8142
- "relative flex items-center gap-2 px-3 py-2 cursor-pointer rounded-md transition-colors",
8143
- "hover:bg-accent",
8144
- // Selected state: subtle bg + square left indicator, avoid left rounding
8145
- isSelected && "bg-primary/10 rounded-r-md"
8189
+ "relative flex items-center gap-2 px-3 py-2 rounded-md transition-colors",
8190
+ !viewOnly && "cursor-pointer hover:bg-accent",
8191
+ // Selected state: subtle bg + square left indicator (only in select mode)
8192
+ !viewOnly && isSelected && "bg-primary/10 rounded-r-md"
8146
8193
  ),
8147
8194
  style: { paddingLeft: `${level * 1.5 + 0.75}rem` },
8148
8195
  children: [
8149
- isSelected && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8150
- "span",
8151
- {
8152
- "aria-hidden": true,
8153
- className: "absolute left-0 top-0 bottom-0 w-1 bg-primary"
8154
- }
8155
- ),
8196
+ !viewOnly && isSelected && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { "aria-hidden": true, className: "absolute left-0 top-0 bottom-0 w-1 bg-primary" }),
8156
8197
  hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8157
8198
  "button",
8158
8199
  {
@@ -8165,25 +8206,24 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8165
8206
  children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react19.ChevronDown, { className: "w-4 h-4" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react19.ChevronRight, { className: "w-4 h-4" })
8166
8207
  }
8167
8208
  ) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "w-5" }),
8168
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
8169
- "div",
8170
- {
8171
- onClick: () => handleSelect(category.id, category),
8172
- className: "flex items-center gap-2 flex-1",
8173
- children: [
8174
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8175
- "div",
8176
- {
8177
- className: cn(
8178
- "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8179
- isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8180
- ),
8181
- children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react19.Check, { className: "w-3 h-3 text-primary-foreground" })
8182
- }
8183
- ),
8184
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8185
- ]
8186
- }
8209
+ viewOnly ? (
8210
+ // View-only mode: just display the name
8211
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-sm", children: category.name })
8212
+ ) : (
8213
+ // Select mode: clickable with checkbox
8214
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { onClick: () => handleSelect(category.id, category), className: "flex items-center gap-2 flex-1", children: [
8215
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
8216
+ "div",
8217
+ {
8218
+ className: cn(
8219
+ "w-4 h-4 border-2 rounded flex items-center justify-center transition-colors",
8220
+ isSelected ? "bg-primary border-primary" : "border-muted-foreground/30"
8221
+ ),
8222
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react19.Check, { className: "w-3 h-3 text-primary-foreground" })
8223
+ }
8224
+ ),
8225
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: cn("text-sm", isSelected && "font-medium text-primary"), children: category.name })
8226
+ ] })
8187
8227
  )
8188
8228
  ]
8189
8229
  }
@@ -8191,8 +8231,11 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8191
8231
  hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { children: children.map((child) => renderCategory(child, level + 1)) })
8192
8232
  ] }, category.id);
8193
8233
  };
8234
+ if (viewOnly) {
8235
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cn("rounded-md border bg-background p-2", disabled && "opacity-50"), children: parentCategories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) });
8236
+ }
8194
8237
  const selectedCount = value.length;
8195
- const displayText = selectedCount > 0 ? `\u0110\xE3 ch\u1ECDn ${selectedCount} danh m\u1EE5c` : placeholder;
8238
+ const displayText = selectedCount > 0 ? mergedLabels.selectedText(selectedCount) : placeholder;
8196
8239
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "relative", children: [
8197
8240
  /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
8198
8241
  "button",
@@ -8225,7 +8268,7 @@ function CategoryTreeSelect({ categories, value, onChange, placeholder = "Ch\u1E
8225
8268
  "rounded-md border bg-popover text-popover-foreground shadow-md",
8226
8269
  "backdrop-blur-sm bg-popover/95 border-border/60"
8227
8270
  ),
8228
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: "Kh\xF4ng c\xF3 danh m\u1EE5c n\xE0o" }) : parentCategories.map((cat) => renderCategory(cat)) })
8271
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "p-1", children: parentCategories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "px-3 py-2 text-sm text-muted-foreground", children: mergedLabels.emptyText }) : parentCategories.map((cat) => renderCategory(cat)) })
8229
8272
  }
8230
8273
  )
8231
8274
  ] })
@@ -12386,23 +12429,71 @@ function DataTable({
12386
12429
  ]
12387
12430
  }
12388
12431
  ) }),
12389
- totalItems > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "border-t bg-muted/30 p-4 rounded-b-md", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
12390
- Pagination,
12391
- {
12392
- page: curPage,
12393
- totalPages: Math.ceil(totalItems / curPageSize),
12394
- onChange: (p) => setCurPage(p),
12395
- className: "",
12396
- showInfo: true,
12397
- totalItems,
12398
- pageSize: curPageSize,
12399
- pageSizeOptions,
12400
- onPageSizeChange: (s) => {
12401
- setCurPage(1);
12402
- setCurPageSize(s);
12432
+ totalItems > 0 && Math.ceil(totalItems / curPageSize) > 1 && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-center justify-between gap-2 px-1 pt-3 text-xs text-muted-foreground", children: [
12433
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "tabular-nums", children: [
12434
+ (curPage - 1) * curPageSize + 1,
12435
+ "-",
12436
+ Math.min(curPage * curPageSize, totalItems),
12437
+ "/",
12438
+ totalItems
12439
+ ] }),
12440
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-center gap-0.5", children: [
12441
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Button_default, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setCurPage(Math.max(1, curPage - 1)), disabled: curPage === 1, children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
12442
+ (() => {
12443
+ const totalPages = Math.ceil(totalItems / curPageSize);
12444
+ const pages = [];
12445
+ if (totalPages <= 5) {
12446
+ for (let i = 1; i <= totalPages; i++) pages.push(i);
12447
+ } else {
12448
+ pages.push(1);
12449
+ if (curPage > 3) pages.push("...");
12450
+ const start = Math.max(2, curPage - 1);
12451
+ const end = Math.min(totalPages - 1, curPage + 1);
12452
+ for (let i = start; i <= end; i++) pages.push(i);
12453
+ if (curPage < totalPages - 2) pages.push("...");
12454
+ pages.push(totalPages);
12455
+ }
12456
+ return pages.map(
12457
+ (p, i) => p === "..." ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "px-1 text-muted-foreground/60", children: "\u2026" }, `dots-${i}`) : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
12458
+ "button",
12459
+ {
12460
+ onClick: () => setCurPage(p),
12461
+ className: cn(
12462
+ "h-7 min-w-7 px-1.5 rounded text-xs font-medium transition-colors",
12463
+ curPage === p ? "bg-primary text-primary-foreground" : "hover:bg-accent hover:text-accent-foreground"
12464
+ ),
12465
+ children: p
12466
+ },
12467
+ p
12468
+ )
12469
+ );
12470
+ })(),
12471
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
12472
+ Button_default,
12473
+ {
12474
+ variant: "ghost",
12475
+ size: "sm",
12476
+ className: "h-7 w-7 p-0",
12477
+ onClick: () => setCurPage(Math.min(Math.ceil(totalItems / curPageSize), curPage + 1)),
12478
+ disabled: curPage === Math.ceil(totalItems / curPageSize),
12479
+ children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("svg", { className: "h-4 w-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
12480
+ }
12481
+ )
12482
+ ] }),
12483
+ pageSizeOptions && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
12484
+ Combobox,
12485
+ {
12486
+ options: pageSizeOptions.map(String),
12487
+ value: String(curPageSize),
12488
+ onChange: (v) => {
12489
+ setCurPage(1);
12490
+ setCurPageSize(Number(v));
12491
+ },
12492
+ size: "sm",
12493
+ className: "w-14 h-7"
12403
12494
  }
12404
- }
12405
- ) })
12495
+ )
12496
+ ] })
12406
12497
  ] });
12407
12498
  }
12408
12499
  var DataTable_default = DataTable;