@goplusvn/core 0.1.29 → 0.1.31

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.31 — MultiSelect: mặc định 1 DÒNG (hết lệch hàng toolbar)
4
+
5
+ Wrap-mặc-định (0.1.27) làm trigger CAO LÊN khi chọn ≥2 option → lệch hàng
6
+ toolbar (các control h-9 bên cạnh). Sửa:
7
+
8
+ - Đảo mặc định `singleLine = true`; muốn wrap thì truyền `singleLine={false}`
9
+ (chỉ hợp form rộng, không phải toolbar).
10
+ - Chế độ 1 dòng hiện **1 chip đầu + badge "+n"** (thay vì bóp nhiều chip đến
11
+ mất chữ trong trigger hẹp); title tooltip cho label đầy đủ + danh sách mục
12
+ còn lại.
13
+ - Chip ở chế độ 1 dòng KHÔNG có nút × riêng (chật) — bỏ chọn qua dropdown
14
+ hoặc nút × xóa-tất-cả; chế độ wrap giữ × từng chip như cũ.
15
+
16
+ Đã test trên vinhhoa (link file: local): toolbar đơn bán hàng, sheet Bộ lọc,
17
+ báo cáo sổ quỹ — 3 filter cùng h-9, không còn đẩy lệch hàng.
18
+
19
+ ## 0.1.30 — Gỡ menu "Thiết bị & phiên đăng nhập" khỏi UserDropdown
20
+
21
+ Trang phiên đăng nhập chuyển thành trang QUẢN TRỊ (gate quyền) — lối vào đúng
22
+ là sidebar navigation của app (resource-filtered), không phải dropdown cá nhân.
23
+
3
24
  ## 0.1.29 — Footer: link "Goplus.vn" trỏ về https://goplus.vn
4
25
 
5
26
  Trước trỏ nhầm github.com/Qualiora (template gốc).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goplusvn/core",
3
3
  "description": "GoPlusVN Platform Kit - ERP kernel: layout, RBAC, CRUD, multi-tenant, system pages",
4
- "version": "0.1.29",
4
+ "version": "0.1.31",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org",
@@ -47,9 +47,11 @@ interface MultiSelectProps {
47
47
  */
48
48
  selectedFirst?: boolean;
49
49
  /**
50
- * Mặc định chip WRAP xuống dòng khi chọn nhiều (trigger cao lên, không tràn
51
- * ngang theo pattern sersavan/shadcn-multi-select-component). Đặt true để
52
- * giữ 1 dòng cố định: chip co + cắt bằng overflow (hợp cho toolbar chật).
50
+ * Mặc định GIỮ 1 DÒNG cố định: hiện 1 chip đầu (cắt chữ, title=label đầy
51
+ * đủ) + badge "+n" (title=các mục còn lại) trigger không đổi chiều cao,
52
+ * không lệch hàng toolbar; chip không nút × riêng (bỏ chọn qua dropdown
53
+ * hoặc × xóa-tất-cả). Đặt false để chip WRAP xuống dòng kèm × từng chip
54
+ * (trigger cao lên — chỉ hợp form rộng rãi, không phải toolbar).
53
55
  */
54
56
  singleLine?: boolean;
55
57
  /** Đóng dropdown ngay sau mỗi lần chọn (mặc định giữ mở để chọn tiếp). */
@@ -179,7 +181,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
179
181
  disabled = false,
180
182
  responsive = false,
181
183
  selectedFirst = false,
182
- singleLine = false,
184
+ singleLine = true,
183
185
  closeOnSelect = false,
184
186
  deduplicateOptions = false,
185
187
  chipColor = "colorful",
@@ -562,12 +564,16 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
562
564
  [updateValue, value],
563
565
  );
564
566
 
567
+ // singleLine: chỉ hiện 1 chip + "+n" — nhiều chip trong trigger hẹp sẽ bị
568
+ // bóp đến mất chữ (chỉ còn dấu ×), 1 chip cắt chữ + đếm luôn đọc được
569
+ const chipLimit = singleLine ? 1 : responsiveSettings.maxCount;
570
+
565
571
  const clearExtraOptions = useCallback(
566
572
  (e: React.MouseEvent) => {
567
573
  e.stopPropagation();
568
- updateValue(value.slice(0, responsiveSettings.maxCount));
574
+ updateValue(value.slice(0, chipLimit));
569
575
  },
570
- [value, responsiveSettings.maxCount, updateValue],
576
+ [value, chipLimit, updateValue],
571
577
  );
572
578
 
573
579
  // Imperative API via ref
@@ -604,7 +610,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
604
610
 
605
611
  const isAllSelected =
606
612
  !hasAsync && options.length > 1 && value.length === options.length;
607
- const visibleChips = selectedOptions.slice(0, responsiveSettings.maxCount);
613
+ const visibleChips = selectedOptions.slice(0, chipLimit);
608
614
  const extraCount = value.length - visibleChips.length;
609
615
 
610
616
  const visibleOptions = filteredOptions.slice(
@@ -667,43 +673,58 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
667
673
  </Badge>
668
674
  ) : (
669
675
  <>
676
+ {/* singleLine: chip KHÔNG có nút × riêng (trigger hẹp, chữ cần
677
+ chỗ) — bỏ chọn bằng dropdown hoặc × xóa-tất-cả */}
670
678
  {visibleChips.map((opt, chipIndex) => (
671
679
  <Badge
672
680
  key={String(opt.value)}
673
681
  variant="secondary"
682
+ title={opt.label}
674
683
  className={cn(
675
- "min-w-0 max-w-[8.5rem] shrink gap-1 pr-1 font-normal",
684
+ "min-w-0 shrink gap-1 font-normal",
685
+ singleLine ? "max-w-full" : "max-w-[8.5rem] pr-1",
676
686
  chipColor === "neutral"
677
687
  ? "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary"
678
688
  : CHIP_COLORS[chipIndex % CHIP_COLORS.length],
679
689
  )}
680
690
  >
681
691
  <span className="truncate">{opt.label}</span>
682
- <span
683
- role="button"
684
- aria-label={`Bỏ chọn ${opt.label}`}
685
- className="flex h-4 w-4 shrink-0 items-center justify-center rounded-sm opacity-60 transition-opacity hover:opacity-100"
686
- onClick={(e) => handleRemove(opt.value, e)}
687
- >
688
- <X className="h-3 w-3" />
689
- </span>
692
+ {!singleLine && (
693
+ <span
694
+ role="button"
695
+ aria-label={`Bỏ chọn ${opt.label}`}
696
+ className="flex h-4 w-4 shrink-0 items-center justify-center rounded-sm opacity-60 transition-opacity hover:opacity-100"
697
+ onClick={(e) => handleRemove(opt.value, e)}
698
+ >
699
+ <X className="h-3 w-3" />
700
+ </span>
701
+ )}
690
702
  </Badge>
691
703
  ))}
692
704
  {extraCount > 0 && (
693
705
  <Badge
694
706
  variant="secondary"
695
- className="shrink-0 gap-1 border-transparent bg-secondary pr-1 font-normal hover:bg-secondary"
707
+ title={selectedOptions
708
+ .slice(chipLimit)
709
+ .map((o) => o.label)
710
+ .join(", ")}
711
+ className={cn(
712
+ "shrink-0 gap-1 border-transparent bg-secondary font-normal hover:bg-secondary",
713
+ !singleLine && "pr-1",
714
+ )}
696
715
  >
697
716
  +{extraCount}
698
- <span
699
- role="button"
700
- aria-label="Bỏ các mục vượt quá"
701
- title="Bỏ các mục vượt quá"
702
- className="flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted-foreground/20 hover:text-foreground"
703
- onClick={clearExtraOptions}
704
- >
705
- <X className="h-3 w-3" />
706
- </span>
717
+ {!singleLine && (
718
+ <span
719
+ role="button"
720
+ aria-label="Bỏ các mục vượt quá"
721
+ title="Bỏ các mục vượt quá"
722
+ className="flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted-foreground/20 hover:text-foreground"
723
+ onClick={clearExtraOptions}
724
+ >
725
+ <X className="h-3 w-3" />
726
+ </span>
727
+ )}
707
728
  </Badge>
708
729
  )}
709
730
  </>
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Link from "next/link";
4
4
  import { useAuthBridge, useSafeAuthSession } from "../auth/auth-bridge";
5
- import { LogOut, User, UserCog, MonitorSmartphone,
5
+ import { LogOut, User, UserCog,
6
6
  } from "lucide-react";
7
7
 
8
8
  import type { DictionaryType } from "../../hooks";
@@ -152,18 +152,6 @@ export function UserDropdown({
152
152
  <span>{dictionary.navigation.userNav.profile}</span>
153
153
  </Link>
154
154
  </DropdownMenuItem>
155
- <DropdownMenuItem
156
- asChild
157
- className="h-8 px-2 rounded-md cursor-pointer focus:bg-primary/10 focus:text-primary transition-colors duration-200 group text-sm"
158
- >
159
- <Link
160
- href={ensureLocalizedPathname("/security/sessions", locale)}
161
- className="flex items-center w-full"
162
- >
163
- <MonitorSmartphone className="me-2 size-4 text-muted-foreground group-hover:text-primary group-focus:text-primary transition-colors" />
164
- <span>Thiết bị & phiên đăng nhập</span>
165
- </Link>
166
- </DropdownMenuItem>
167
155
  {/* <DropdownMenuItem
168
156
  asChild
169
157
  className="h-8 px-2 rounded-md cursor-pointer focus:bg-primary/10 focus:text-primary transition-colors duration-200 group text-sm"