@djangocfg/ui-core 2.1.339 → 2.1.341

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.339",
3
+ "version": "2.1.341",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -91,7 +91,7 @@
91
91
  "playground": "playground dev"
92
92
  },
93
93
  "peerDependencies": {
94
- "@djangocfg/i18n": "^2.1.339",
94
+ "@djangocfg/i18n": "^2.1.341",
95
95
  "consola": "^3.4.2",
96
96
  "lucide-react": "^0.545.0",
97
97
  "moment": "^2.30.1",
@@ -160,9 +160,9 @@
160
160
  "vaul": "1.1.2"
161
161
  },
162
162
  "devDependencies": {
163
- "@djangocfg/i18n": "^2.1.339",
163
+ "@djangocfg/i18n": "^2.1.341",
164
164
  "@djangocfg/playground": "workspace:*",
165
- "@djangocfg/typescript-config": "^2.1.339",
165
+ "@djangocfg/typescript-config": "^2.1.341",
166
166
  "@types/node": "^24.7.2",
167
167
  "@types/react": "^19.1.0",
168
168
  "@types/react-dom": "^19.1.0",
@@ -58,6 +58,7 @@ const DialogContent = React.forwardRef<
58
58
  <DialogOverlay />
59
59
  <DialogPrimitive.Content
60
60
  ref={ref}
61
+ aria-describedby={undefined}
61
62
  className={cn(
62
63
  "fixed z-600 bg-background duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
63
64
  fullscreen
@@ -68,7 +69,6 @@ const DialogContent = React.forwardRef<
68
69
  {...props}
69
70
  >
70
71
  {children}
71
- <DialogPrimitive.Description className="sr-only" />
72
72
  {closeButton === undefined ? (
73
73
  <DialogPrimitive.Close className="absolute right-4 top-4 cursor-pointer rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
74
74
  <Cross2Icon className="h-4 w-4" />
@@ -266,6 +266,7 @@ const DrawerContent = React.forwardRef<
266
266
  <DrawerOverlay />
267
267
  <DrawerPrimitive.Content
268
268
  ref={ref}
269
+ aria-describedby={undefined}
269
270
  className={cn(
270
271
  "fixed z-500 flex flex-col bg-background",
271
272
  directionStyles[direction],
@@ -16,6 +16,7 @@ const HoverCardContent = React.forwardRef<
16
16
  >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
17
17
  <HoverCardPrimitive.Content
18
18
  ref={ref}
19
+ aria-describedby={undefined}
19
20
  align={align}
20
21
  sideOffset={sideOffset}
21
22
  className={cn(
@@ -19,6 +19,7 @@ const PopoverContent = React.forwardRef<
19
19
  <PopoverPrimitive.Portal>
20
20
  <PopoverPrimitive.Content
21
21
  ref={ref}
22
+ aria-describedby={undefined}
22
23
  align={align}
23
24
  sideOffset={sideOffset}
24
25
  className={cn(
@@ -63,6 +63,7 @@ const SheetContent = React.forwardRef<
63
63
  <SheetOverlay />
64
64
  <SheetPrimitive.Content
65
65
  ref={ref}
66
+ aria-describedby={undefined}
66
67
  className={cn(sheetVariants({ side }), className)}
67
68
  {...props}
68
69
  >
@@ -109,6 +109,7 @@ const SidePanelContent = React.forwardRef<
109
109
  <DrawerPrimitive.Portal>
110
110
  <DrawerPrimitive.Content
111
111
  ref={ref}
112
+ aria-describedby={undefined}
112
113
  className={cn(
113
114
  'fixed z-500 flex flex-col bg-background shadow-2xl shadow-black/20',
114
115
  'h-full max-w-[95vw]',
@@ -19,6 +19,7 @@ const TooltipContent = React.forwardRef<
19
19
  <TooltipPrimitive.Portal>
20
20
  <TooltipPrimitive.Content
21
21
  ref={ref}
22
+ aria-describedby={undefined}
22
23
  sideOffset={sideOffset}
23
24
  className={cn(
24
25
  "z-[700] overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
@@ -4,56 +4,54 @@ import * as React from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
 
6
6
  export interface PortalProps {
7
- /**
8
- * The content to be rendered inside the portal.
9
- */
10
7
  children: React.ReactNode;
11
8
  /**
12
- * The container element to render the portal into.
13
- * Defaults to document.body.
14
- * Can be a DOM element, a function returning a DOM element, or a ref.
9
+ * Target container. Defaults to document.body.
10
+ * Accepts a DOM element, a function returning one, or a ref.
15
11
  */
16
12
  container?: Element | (() => Element | null) | React.RefObject<Element | null> | null;
13
+ /** Render children in-place instead of teleporting. @default false */
14
+ disablePortal?: boolean;
17
15
  /**
18
- * Disable the portal behavior.
19
- * The children stay within the normal DOM hierarchy.
16
+ * Keep children mounted in the DOM even when the portal is "closed"
17
+ * (i.e. when the parent conditionally hides them via CSS / opacity).
18
+ * Useful for exit animations — the node stays in the DOM while the
19
+ * animation plays, then the parent unmounts it via its own state.
20
20
  * @default false
21
21
  */
22
- disablePortal?: boolean;
22
+ keepMounted?: boolean;
23
23
  }
24
24
 
25
25
  /**
26
- * Portal component renders children into a different part of the DOM.
27
- * Similar to MUI Portal - useful for modals, tooltips, floating elements.
26
+ * Portal renders children into a different part of the DOM tree,
27
+ * escaping any CSS stacking context (transform, filter, will-change).
28
28
  *
29
29
  * @example
30
30
  * ```tsx
31
- * // Render to document.body (default)
31
+ * // Default: teleport to document.body
32
32
  * <Portal>
33
- * <div className="fixed inset-0 z-50">Modal content</div>
33
+ * <div className="fixed inset-0 z-50">Modal</div>
34
34
  * </Portal>
35
35
  *
36
- * // Render to custom container
37
- * <Portal container={document.getElementById('modal-root')}>
38
- * <div>Custom container content</div>
36
+ * // Custom container
37
+ * <Portal container={() => document.getElementById('modal-root')}>
38
+ * <div>Content</div>
39
39
  * </Portal>
40
40
  *
41
- * // Disable portal (render in place)
42
- * <Portal disablePortal>
43
- * <div>Rendered normally</div>
41
+ * // Keep mounted for exit animations — hide via CSS, unmount later
42
+ * <Portal keepMounted>
43
+ * <div style={{ display: isOpen ? 'block' : 'none' }}>Animated panel</div>
44
44
  * </Portal>
45
45
  *
46
- * // With ref container
47
- * const containerRef = useRef<HTMLDivElement>(null);
48
- * <div ref={containerRef} />
49
- * <Portal container={containerRef}>
50
- * <div>Content in ref container</div>
46
+ * // Disable portal (render in-place, e.g. in tests or SSR)
47
+ * <Portal disablePortal>
48
+ * <div>In-place</div>
51
49
  * </Portal>
52
50
  * ```
53
51
  */
54
52
  export const Portal = React.forwardRef<HTMLDivElement, PortalProps>(
55
53
  function Portal(props, ref) {
56
- const { children, container, disablePortal = false } = props;
54
+ const { children, container, disablePortal = false, keepMounted = false } = props;
57
55
  const [mountNode, setMountNode] = React.useState<Element | null>(null);
58
56
 
59
57
  React.useEffect(() => {
@@ -64,19 +62,18 @@ export const Portal = React.forwardRef<HTMLDivElement, PortalProps>(
64
62
 
65
63
  if (disablePortal) {
66
64
  if (React.isValidElement(children)) {
67
- return React.cloneElement(children as React.ReactElement<{ ref?: React.Ref<HTMLDivElement> }>, {
68
- ref: ref,
69
- });
65
+ return React.cloneElement(children as React.ReactElement<{ ref?: React.Ref<HTMLDivElement> }>, { ref });
70
66
  }
71
67
  return <>{children}</>;
72
68
  }
73
69
 
74
- if (mountNode) {
75
- return createPortal(children, mountNode);
70
+ // SSR / before first effect: keepMounted renders in-place so content
71
+ // exists in the server HTML; otherwise return null (portal attaches client-side).
72
+ if (!mountNode) {
73
+ return keepMounted ? <>{children}</> : null;
76
74
  }
77
75
 
78
- // SSR: return null until mounted
79
- return null;
76
+ return createPortal(children, mountNode);
80
77
  }
81
78
  );
82
79