@djangocfg/ui-core 2.1.338 → 2.1.340

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.338",
3
+ "version": "2.1.340",
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.338",
94
+ "@djangocfg/i18n": "^2.1.340",
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.338",
163
+ "@djangocfg/i18n": "^2.1.340",
164
164
  "@djangocfg/playground": "workspace:*",
165
- "@djangocfg/typescript-config": "^2.1.338",
165
+ "@djangocfg/typescript-config": "^2.1.340",
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" />
@@ -6,17 +6,36 @@ import { Drawer as DrawerPrimitive } from 'vaul';
6
6
  import { cn } from '../../../lib/utils';
7
7
  import { useIsMobile } from '../../../hooks/media/useMobile';
8
8
 
9
+ // Direction context lets <DrawerContent /> inherit the Root's direction without
10
+ // callers having to pass it twice (a frequent rebase / refactor footgun).
11
+ const DrawerDirectionContext = React.createContext<'bottom' | 'right' | 'left' | 'top' | undefined>(undefined);
12
+
13
+ /** @internal — used by DrawerContent to inherit `direction` from the Root. */
14
+ export const useDrawerDirection = () => React.useContext(DrawerDirectionContext);
15
+
9
16
  const Drawer = ({
10
- shouldScaleBackground = true,
11
- direction,
17
+ shouldScaleBackground,
18
+ direction = 'bottom',
12
19
  ...props
13
- }: React.ComponentProps<typeof DrawerPrimitive.Root> & { direction?: 'bottom' | 'right' | 'left' | 'top' }) => (
14
- <DrawerPrimitive.Root
15
- shouldScaleBackground={shouldScaleBackground}
16
- direction={direction}
17
- {...props}
18
- />
19
- )
20
+ }: React.ComponentProps<typeof DrawerPrimitive.Root> & { direction?: 'bottom' | 'right' | 'left' | 'top' }) => {
21
+ // vaul's body-scale animation is a mobile-bottom-sheet effect by design.
22
+ // Applying it to side drawers (right/left) keeps the <body> transformed for
23
+ // ~300ms during open, which on heavy pages stalls the main thread *before*
24
+ // any data fetch the consumer kicks off in response to opening — so the
25
+ // network panel shows the request appearing only after the scale finishes.
26
+ // Default to enabled only for bottom sheets; callers can still override.
27
+ const resolvedScale = shouldScaleBackground ?? direction === 'bottom';
28
+
29
+ return (
30
+ <DrawerDirectionContext.Provider value={direction}>
31
+ <DrawerPrimitive.Root
32
+ shouldScaleBackground={resolvedScale}
33
+ direction={direction}
34
+ {...props}
35
+ />
36
+ </DrawerDirectionContext.Provider>
37
+ );
38
+ };
20
39
  Drawer.displayName = "Drawer"
21
40
 
22
41
  const DrawerTrigger = DrawerPrimitive.Trigger
@@ -134,7 +153,7 @@ const DrawerContent = React.forwardRef<
134
153
  >(({
135
154
  className,
136
155
  children,
137
- direction = 'bottom',
156
+ direction: directionProp,
138
157
  size = 'md',
139
158
  width,
140
159
  height,
@@ -147,6 +166,11 @@ const DrawerContent = React.forwardRef<
147
166
  onSizeChange,
148
167
  ...props
149
168
  }, ref) => {
169
+ // Inherit direction from the Drawer Root when not explicitly overridden.
170
+ // This avoids the common bug where `<Drawer direction="right">` is paired
171
+ // with a `<DrawerContent />` that silently falls back to "bottom".
172
+ const inherited = useDrawerDirection();
173
+ const direction = directionProp ?? inherited ?? 'bottom';
150
174
  const isVertical = direction === 'bottom' || direction === 'top';
151
175
  const isMobile = useIsMobile();
152
176
  const resizeEnabled = resizable && (!resizableOnDesktopOnly || !isMobile);
@@ -242,6 +266,7 @@ const DrawerContent = React.forwardRef<
242
266
  <DrawerOverlay />
243
267
  <DrawerPrimitive.Content
244
268
  ref={ref}
269
+ aria-describedby={undefined}
245
270
  className={cn(
246
271
  "fixed z-500 flex flex-col bg-background",
247
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",
@@ -29,10 +29,18 @@ export type Breakpoint = keyof typeof BREAKPOINTS
29
29
  * const isLandscape = useMediaQuery('(orientation: landscape)')
30
30
  */
31
31
  export function useMediaQuery(query: string): boolean {
32
- const [matches, setMatches] = useState<boolean>(false)
32
+ // Lazy initializer: read the real match on first render in the browser
33
+ // so we never paint a "desktop" frame on a phone (and vice-versa) before
34
+ // the first effect runs. SSR / non-browser → false.
35
+ const [matches, setMatches] = useState<boolean>(() => {
36
+ if (typeof window === 'undefined') return false
37
+ return window.matchMedia(query).matches
38
+ })
33
39
 
34
40
  useEffect(() => {
35
41
  const mediaQuery = window.matchMedia(query)
42
+ // Re-sync once on mount in case the lazy initializer was skipped
43
+ // (e.g. hydration mismatch where SSR gave false).
36
44
  setMatches(mediaQuery.matches)
37
45
  const handler = (event: MediaQueryListEvent) => setMatches(event.matches)
38
46
  mediaQuery.addEventListener('change', handler)