@underverse-ui/underverse 0.2.5 → 0.2.8

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
@@ -2262,7 +2262,7 @@ var sizeStyles3 = {
2262
2262
  md: "max-w-md",
2263
2263
  lg: "max-w-lg",
2264
2264
  xl: "max-w-xl",
2265
- full: "max-w-full mx-4"
2265
+ full: "max-w-full"
2266
2266
  };
2267
2267
  var Modal = ({
2268
2268
  isOpen,
@@ -2271,11 +2271,16 @@ var Modal = ({
2271
2271
  title,
2272
2272
  description,
2273
2273
  className,
2274
+ contentClassName,
2274
2275
  overlayClassName,
2275
2276
  showCloseButton = true,
2276
2277
  closeOnOverlayClick = true,
2277
2278
  closeOnEsc = true,
2278
- size = "md"
2279
+ size = "md",
2280
+ noPadding = false,
2281
+ fullWidth = false,
2282
+ width,
2283
+ height
2279
2284
  }) => {
2280
2285
  const [isMounted, setIsMounted] = React9.useState(false);
2281
2286
  const [isVisible, setIsVisible] = React9.useState(false);
@@ -2343,14 +2348,17 @@ var Modal = ({
2343
2348
  className: cn(
2344
2349
  "relative w-full rounded-lg bg-card text-card-foreground shadow-xl",
2345
2350
  "transition-all duration-200 ease-out",
2346
- sizeStyles3[size],
2351
+ fullWidth ? "max-w-full" : sizeStyles3[size],
2352
+ fullWidth && "mx-0",
2347
2353
  className
2348
2354
  ),
2349
2355
  style: {
2350
2356
  opacity: isOpen && !isAnimating ? 1 : 0,
2351
2357
  transform: isOpen && !isAnimating ? "scale(1)" : "scale(0.9)",
2352
2358
  // Thêm dòng này để tạo hiệu ứng nảy
2353
- transition: "all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)"
2359
+ transition: "all 300ms cubic-bezier(0.34, 1.76, 0.64, 1)",
2360
+ width,
2361
+ height
2354
2362
  },
2355
2363
  onClick: (e) => e.stopPropagation(),
2356
2364
  children: [
@@ -2372,7 +2380,7 @@ var Modal = ({
2372
2380
  }
2373
2381
  )
2374
2382
  ] }),
2375
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "p-6", children })
2383
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: cn("p-6", noPadding && "p-0", contentClassName), children })
2376
2384
  ]
2377
2385
  }
2378
2386
  )
@@ -4027,7 +4035,9 @@ var Combobox = ({
4027
4035
  usePortal = true,
4028
4036
  label,
4029
4037
  required,
4030
- fontBold = false
4038
+ fontBold = false,
4039
+ loading: loading2 = false,
4040
+ loadingText = "Loading..."
4031
4041
  }) => {
4032
4042
  const [open, setOpen] = React18.useState(false);
4033
4043
  const [query, setQuery] = React18.useState("");
@@ -4045,6 +4055,7 @@ var Combobox = ({
4045
4055
  );
4046
4056
  const [dropdownPosition, setDropdownPosition] = React18.useState(null);
4047
4057
  const triggerRef = React18.useRef(null);
4058
+ const dropdownRef = React18.useRef(null);
4048
4059
  const calculatePosition = React18.useCallback(() => {
4049
4060
  if (!triggerRef.current) return null;
4050
4061
  const rect = triggerRef.current.getBoundingClientRect();
@@ -4073,11 +4084,10 @@ var Combobox = ({
4073
4084
  if (!open) return;
4074
4085
  const handleClickOutside = (event) => {
4075
4086
  const target = event.target;
4076
- if (triggerRef.current && !triggerRef.current.contains(target)) {
4077
- const dropdown = document.querySelector("[data-combobox-dropdown]");
4078
- if (dropdown && !dropdown.contains(target)) {
4079
- setOpen(false);
4080
- }
4087
+ const triggerEl = triggerRef.current;
4088
+ const dropdownEl = dropdownRef.current;
4089
+ if (triggerEl && !triggerEl.contains(target) && dropdownEl && !dropdownEl.contains(target)) {
4090
+ setOpen(false);
4081
4091
  }
4082
4092
  };
4083
4093
  const handleEscape = (event) => {
@@ -4121,6 +4131,7 @@ var Combobox = ({
4121
4131
  "div",
4122
4132
  {
4123
4133
  "data-combobox-dropdown": true,
4134
+ ref: dropdownRef,
4124
4135
  style: {
4125
4136
  position: "absolute",
4126
4137
  top: dropdownPosition?.top || 0,
@@ -4178,7 +4189,10 @@ var Combobox = ({
4178
4189
  }
4179
4190
  )
4180
4191
  ] }),
4181
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "max-h-64 overflow-y-auto overscroll-contain", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "p-1 space-y-1", children: filteredOptions.length > 0 ? filteredOptions.map((item, index) => {
4192
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "max-h-64 overflow-y-auto overscroll-contain", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "p-1 space-y-1", children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "px-3 py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
4193
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
4194
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-sm text-muted-foreground", children: loadingText || "Loading..." })
4195
+ ] }) }) : filteredOptions.length > 0 ? filteredOptions.map((item, index) => {
4182
4196
  const itemValue = getOptionValue(item);
4183
4197
  const itemLabel = getOptionLabel(item);
4184
4198
  const isSelected = itemValue === value;
@@ -4212,9 +4226,18 @@ var Combobox = ({
4212
4226
  },
4213
4227
  `${itemValue}-${index}`
4214
4228
  );
4215
- }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "px-3 py-8 text-center text-muted-foreground text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
4216
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Search, { className: "h-6 w-6 opacity-50" }),
4217
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: emptyText })
4229
+ }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "px-3 py-8 text-center text-muted-foreground text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
4230
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
4231
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-sm", children: emptyText }),
4232
+ query && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4233
+ "button",
4234
+ {
4235
+ type: "button",
4236
+ onClick: () => setQuery(""),
4237
+ className: "text-xs text-primary hover:underline",
4238
+ children: "Clear search"
4239
+ }
4240
+ )
4218
4241
  ] }) }) }) })
4219
4242
  ] })
4220
4243
  }
@@ -4290,7 +4313,15 @@ var Combobox = ({
4290
4313
  children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { className: "h-3 w-3" })
4291
4314
  }
4292
4315
  ),
4293
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.ChevronDown, { className: cn("h-4 w-4 text-muted-foreground transition-transform duration-200", open && "rotate-180") })
4316
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4317
+ import_lucide_react12.ChevronDown,
4318
+ {
4319
+ className: cn(
4320
+ "h-4 w-4 text-muted-foreground transition-all duration-200",
4321
+ open && "rotate-180 scale-110 text-primary"
4322
+ )
4323
+ }
4324
+ )
4294
4325
  ] })
4295
4326
  ]
4296
4327
  }
@@ -6217,7 +6248,10 @@ var MultiCombobox = ({
6217
6248
  label,
6218
6249
  title,
6219
6250
  required,
6220
- displayFormat = (option) => option.label
6251
+ displayFormat = (option) => option.label,
6252
+ loading: loading2 = false,
6253
+ loadingText = "Loading...",
6254
+ emptyText = "No results found"
6221
6255
  }) => {
6222
6256
  const [query, setQuery] = React24.useState("");
6223
6257
  const [open, setOpen] = React24.useState(false);
@@ -6226,6 +6260,7 @@ var MultiCombobox = ({
6226
6260
  const listRef = React24.useRef([]);
6227
6261
  const [dropdownPosition, setDropdownPosition] = React24.useState(null);
6228
6262
  const triggerRef = React24.useRef(null);
6263
+ const dropdownRef = React24.useRef(null);
6229
6264
  useShadCNAnimations();
6230
6265
  const calculatePosition = React24.useCallback(() => {
6231
6266
  if (!triggerRef.current) return null;
@@ -6255,11 +6290,10 @@ var MultiCombobox = ({
6255
6290
  if (!open) return;
6256
6291
  const handleClickOutside = (event) => {
6257
6292
  const target = event.target;
6258
- if (triggerRef.current && !triggerRef.current.contains(target)) {
6259
- const dropdown = document.querySelector('[data-dropdown="multicombobox"]');
6260
- if (dropdown && !dropdown.contains(target)) {
6261
- setOpen(false);
6262
- }
6293
+ const triggerEl = triggerRef.current;
6294
+ const dropdownEl = dropdownRef.current;
6295
+ if (triggerEl && !triggerEl.contains(target) && dropdownEl && !dropdownEl.contains(target)) {
6296
+ setOpen(false);
6263
6297
  }
6264
6298
  };
6265
6299
  const handleEscape = (event) => {
@@ -6428,7 +6462,12 @@ var MultiCombobox = ({
6428
6462
  value.length,
6429
6463
  " selected"
6430
6464
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-muted-foreground", children: placeholder || "Select..." }) }),
6431
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.ChevronDown, { className: cn("opacity-50 transition-transform", sizeStyles8[size].icon, open && "rotate-180") })
6465
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6466
+ import_lucide_react17.ChevronDown,
6467
+ {
6468
+ className: cn("opacity-50 transition-all duration-200", sizeStyles8[size].icon, open && "rotate-180 scale-110 text-primary opacity-100")
6469
+ }
6470
+ )
6432
6471
  ]
6433
6472
  }
6434
6473
  ),
@@ -6436,6 +6475,7 @@ var MultiCombobox = ({
6436
6475
  /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6437
6476
  "div",
6438
6477
  {
6478
+ ref: dropdownRef,
6439
6479
  "data-dropdown": "multicombobox",
6440
6480
  style: {
6441
6481
  position: "absolute",
@@ -6453,10 +6493,7 @@ var MultiCombobox = ({
6453
6493
  children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
6454
6494
  "div",
6455
6495
  {
6456
- className: cn(
6457
- "rounded-md border bg-popover text-popover-foreground shadow-md",
6458
- "backdrop-blur-sm bg-popover/95 border-border/60"
6459
- ),
6496
+ className: cn("rounded-md border bg-popover text-popover-foreground shadow-md", "backdrop-blur-sm bg-popover/95 border-border/60"),
6460
6497
  children: [
6461
6498
  showClear && value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "px-3 py-2 border-b border-border/60 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6462
6499
  "button",
@@ -6488,7 +6525,10 @@ var MultiCombobox = ({
6488
6525
  }
6489
6526
  )
6490
6527
  ] }),
6491
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { className: cn("max-h-60 overflow-y-auto p-1", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"), children: filtered.length ? filtered.map((item, index) => {
6528
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { className: cn("max-h-60 overflow-y-auto p-1", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"), children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("li", { className: "px-3 py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
6529
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.Loader2, { className: "h-6 w-6 animate-spin text-primary" }),
6530
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-muted-foreground", children: loadingText })
6531
+ ] }) }) : filtered.length ? filtered.map((item, index) => {
6492
6532
  const isSelected = value.includes(item.value);
6493
6533
  const isDisabled = disabledOptions.includes(item.value);
6494
6534
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
@@ -6520,7 +6560,20 @@ var MultiCombobox = ({
6520
6560
  },
6521
6561
  item.value
6522
6562
  );
6523
- }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("li", { className: cn("px-3 py-2 text-muted-foreground", size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"), children: "No result." }) })
6563
+ }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
6564
+ "li",
6565
+ {
6566
+ className: cn(
6567
+ "px-3 py-8 text-center text-muted-foreground",
6568
+ size === "lg" ? "text-base" : size === "sm" ? "text-xs" : "text-sm"
6569
+ ),
6570
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col items-center gap-2 animate-in fade-in-0 zoom-in-95 duration-300", children: [
6571
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.SearchX, { className: "h-8 w-8 opacity-40 text-muted-foreground" }),
6572
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: emptyText }),
6573
+ query && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { type: "button", onClick: () => setQuery(""), className: "text-xs text-primary hover:underline", children: "Clear search" })
6574
+ ] })
6575
+ }
6576
+ ) })
6524
6577
  ]
6525
6578
  }
6526
6579
  )
@@ -10190,11 +10243,44 @@ function DataTable({
10190
10243
  col.key
10191
10244
  )) });
10192
10245
  const isServerMode = Boolean(onQueryChange);
10246
+ const processedData = import_react23.default.useMemo(() => {
10247
+ if (isServerMode) return data;
10248
+ let result = [...data];
10249
+ if (Object.keys(filters).length > 0) {
10250
+ result = result.filter((row) => {
10251
+ return Object.entries(filters).every(([key, value]) => {
10252
+ if (value === void 0 || value === null || value === "") return true;
10253
+ const col = columns.find((c) => c.key === key);
10254
+ const rowValue = col?.dataIndex ? row[col.dataIndex] : row[key];
10255
+ if (col?.filter?.type === "date" && value instanceof Date) {
10256
+ return new Date(rowValue).toDateString() === value.toDateString();
10257
+ }
10258
+ return String(rowValue ?? "").toLowerCase().includes(String(value).toLowerCase());
10259
+ });
10260
+ });
10261
+ }
10262
+ if (sort) {
10263
+ result.sort((a, b) => {
10264
+ const col = columns.find((c) => c.key === sort.key);
10265
+ const aValue = col?.dataIndex ? a[col.dataIndex] : a[sort.key];
10266
+ const bValue = col?.dataIndex ? b[col.dataIndex] : b[sort.key];
10267
+ if (aValue === bValue) return 0;
10268
+ if (typeof aValue === "number" && typeof bValue === "number") {
10269
+ return sort.order === "asc" ? aValue - bValue : bValue - aValue;
10270
+ }
10271
+ const compare = String(aValue).localeCompare(String(bValue));
10272
+ return sort.order === "asc" ? compare : -compare;
10273
+ });
10274
+ }
10275
+ return result;
10276
+ }, [data, isServerMode, filters, sort, columns]);
10277
+ const totalItems = isServerMode ? total : processedData.length;
10193
10278
  const displayedData = isServerMode ? data : import_react23.default.useMemo(() => {
10194
10279
  const start = (curPage - 1) * curPageSize;
10195
- return data.slice(start, start + curPageSize);
10196
- }, [data, curPage, curPageSize]);
10197
- const totalItems = isServerMode ? total : data.length;
10280
+ if (start >= processedData.length && curPage > 1) {
10281
+ }
10282
+ return processedData.slice(start, start + curPageSize);
10283
+ }, [processedData, curPage, curPageSize]);
10198
10284
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cn("space-y-2", className), children: [
10199
10285
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center justify-between gap-4 mb-1", children: [
10200
10286
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "text-sm text-muted-foreground", children: caption }),
@@ -10266,24 +10352,27 @@ function DataTable({
10266
10352
  )
10267
10353
  ] }),
10268
10354
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-sm", children: "Loading..." })
10269
- ] }) }) }) : !displayedData || displayedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCell, { colSpan: visibleColumns.length, className: "text-center py-6 text-muted-foreground", children: "No data" }) }) : displayedData.map((row, idx) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableRow, { className: cn(densityRowClass, striped && idx % 2 === 0 && "bg-muted/30"), children: visibleColumns.map((col, colIdx) => {
10270
- const value = col.dataIndex ? row[col.dataIndex] : void 0;
10271
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
10272
- TableCell,
10273
- {
10274
- className: cn(
10275
- cellPadding,
10276
- col.align === "right" && "text-right",
10277
- col.align === "center" && "text-center",
10278
- columnDividers && colIdx > 0 && "border-l border-border/60",
10279
- idx === data.length - 1 && col === visibleColumns[0] && "rounded-bl-md",
10280
- idx === data.length - 1 && col === visibleColumns[visibleColumns.length - 1] && "rounded-br-md"
10281
- ),
10282
- children: col.render ? col.render(value, row, idx) : String(value ?? "")
10283
- },
10284
- col.key
10285
- );
10286
- }) }, getRowKey(row, idx))) })
10355
+ ] }) }) }) : !displayedData || displayedData.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableCell, { colSpan: visibleColumns.length, className: "text-center py-6 text-muted-foreground", children: "No data" }) }) : displayedData.map((row, idx) => {
10356
+ const isLastRow = idx === displayedData.length - 1;
10357
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TableRow, { className: cn(densityRowClass, striped && idx % 2 === 0 && "bg-muted/30"), children: visibleColumns.map((col, colIdx) => {
10358
+ const value = col.dataIndex ? row[col.dataIndex] : void 0;
10359
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
10360
+ TableCell,
10361
+ {
10362
+ className: cn(
10363
+ cellPadding,
10364
+ col.align === "right" && "text-right",
10365
+ col.align === "center" && "text-center",
10366
+ columnDividers && colIdx > 0 && "border-l border-border/60",
10367
+ isLastRow && col === visibleColumns[0] && "rounded-bl-md",
10368
+ isLastRow && col === visibleColumns[visibleColumns.length - 1] && "rounded-br-md"
10369
+ ),
10370
+ children: col.render ? col.render(value, row, idx) : String(value ?? "")
10371
+ },
10372
+ col.key
10373
+ );
10374
+ }) }, getRowKey(row, idx));
10375
+ }) })
10287
10376
  ]
10288
10377
  }
10289
10378
  ) }),