@goplusvn/core 0.1.4 → 0.1.5

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/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.4",
4
+ "version": "0.1.5",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org",
@@ -136,6 +136,22 @@ export function CrudDialog({
136
136
  };
137
137
  }, [open]);
138
138
 
139
+ // FIX (lỗi conflict event kinh điển): Radix Dialog + Select/Combobox/Popover
140
+ // lồng nhau đôi khi để sót `pointer-events: none` / scroll-lock trên <body> sau
141
+ // khi đóng, khiến TOÀN trang không click được. Reset thủ công SAU khi đóng, có
142
+ // guard `=== "none"` và clearTimeout khi mở lại để không gỡ khoá lúc đang mở.
143
+ useEffect(() => {
144
+ if (open) return;
145
+ const timer = setTimeout(() => {
146
+ if (document.body.style.pointerEvents === "none") {
147
+ document.body.style.pointerEvents = "";
148
+ }
149
+ document.body.style.overflow = "";
150
+ document.body.removeAttribute("data-scroll-locked");
151
+ }, 300);
152
+ return () => clearTimeout(timer);
153
+ }, [open]);
154
+
139
155
  // Keyboard shortcuts
140
156
  useEffect(() => {
141
157
  if (!open) return;
@@ -139,6 +139,22 @@ export function CrudSheet({
139
139
  };
140
140
  }, [open]);
141
141
 
142
+ // FIX (lỗi conflict event kinh điển): Radix Sheet + Select/Combobox/Popover lồng
143
+ // nhau đôi khi để sót `pointer-events: none` / scroll-lock trên <body> sau khi
144
+ // đóng, khiến TOÀN trang không click được. Reset thủ công SAU khi đóng, có guard
145
+ // `=== "none"` và clearTimeout khi mở lại để không gỡ khoá lúc đang mở.
146
+ useEffect(() => {
147
+ if (open) return;
148
+ const timer = setTimeout(() => {
149
+ if (document.body.style.pointerEvents === "none") {
150
+ document.body.style.pointerEvents = "";
151
+ }
152
+ document.body.style.overflow = "";
153
+ document.body.removeAttribute("data-scroll-locked");
154
+ }, 300);
155
+ return () => clearTimeout(timer);
156
+ }, [open]);
157
+
142
158
  // Keyboard shortcuts
143
159
  useEffect(() => {
144
160
  if (!open) return;
@@ -4,6 +4,7 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
4
4
  import { cva } from "class-variance-authority";
5
5
  import type { ComponentProps } from "react";
6
6
  import { cn } from "../../utils";
7
+ import { useReleaseStuckBodyLock } from "../primitives/use-release-stuck-body-lock";
7
8
 
8
9
  export function Sheet({
9
10
  ...props
@@ -84,6 +85,7 @@ export function SheetContent({
84
85
  side = "right",
85
86
  ...props
86
87
  }: SheetContentProps) {
88
+ useReleaseStuckBodyLock();
87
89
  return (
88
90
  <SheetPortal>
89
91
  <SheetOverlay />
@@ -72,7 +72,10 @@ export function Editor({
72
72
  showOnlyCurrent: true,
73
73
  }),
74
74
  Typography,
75
- ],
75
+ // Cast: các gói @tiptap/* có thể lệch version @tiptap/core trong monorepo,
76
+ // gây lỗi type (Extension không khớp AnyExtension) khi build dts. An toàn
77
+ // vì runtime giống nhau; cast về đúng kiểu options yêu cầu.
78
+ ] as unknown as UseEditorOptions["extensions"],
76
79
  content: value,
77
80
  onUpdate: ({ editor }) => {
78
81
  onValueChange?.(editor.getHTML());
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  import * as DialogPrimitive from "@radix-ui/react-dialog";
5
5
  import { X } from "lucide-react";
6
6
  import { cn } from "../../utils";
7
+ import { useReleaseStuckBodyLock } from "./use-release-stuck-body-lock";
7
8
 
8
9
  const Dialog = DialogPrimitive.Root;
9
10
 
@@ -31,7 +32,9 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
31
32
  const DialogContent = React.forwardRef<
32
33
  React.ElementRef<typeof DialogPrimitive.Content>,
33
34
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
34
- >(({ className, children, ...props }, ref) => (
35
+ >(({ className, children, ...props }, ref) => {
36
+ useReleaseStuckBodyLock();
37
+ return (
35
38
  <DialogPortal>
36
39
  <DialogOverlay />
37
40
  <DialogPrimitive.Content
@@ -49,7 +52,8 @@ const DialogContent = React.forwardRef<
49
52
  </DialogPrimitive.Close>
50
53
  </DialogPrimitive.Content>
51
54
  </DialogPortal>
52
- ));
55
+ );
56
+ });
53
57
  DialogContent.displayName = DialogPrimitive.Content.displayName;
54
58
 
55
59
  const DialogHeader = ({
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ /**
6
+ * Lưới an toàn cho "lỗi conflict event kinh điển": Radix (Dialog/AlertDialog/Sheet
7
+ * + Select/Combobox/Popover lồng bên trong) đôi khi để sót `pointer-events: none`
8
+ * / scroll-lock trên <body> sau khi đóng, khiến TOÀN trang không click được.
9
+ *
10
+ * Gọi hook này trong *Content của mỗi primitive overlay. Content chỉ mount khi
11
+ * overlay mở; khi nó unmount (đã đóng) ta gỡ khoá — nhưng CHỈ khi không còn lớp
12
+ * dialog/popover nào đang mở, để không phá modal khác đang chồng lên.
13
+ */
14
+ export function useReleaseStuckBodyLock() {
15
+ React.useEffect(() => {
16
+ return () => {
17
+ setTimeout(() => {
18
+ const hasOpenLayer = document.querySelector(
19
+ '[role="dialog"][data-state="open"], [role="alertdialog"][data-state="open"], [data-radix-popper-content-wrapper]',
20
+ );
21
+ if (!hasOpenLayer && document.body.style.pointerEvents === "none") {
22
+ document.body.style.pointerEvents = "";
23
+ document.body.style.overflow = "";
24
+ document.body.removeAttribute("data-scroll-locked");
25
+ }
26
+ }, 0);
27
+ };
28
+ }, []);
29
+ }