@djangocfg/ui-core 2.1.528 → 2.1.530

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.528",
3
+ "version": "2.1.530",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -128,7 +128,7 @@
128
128
  "check:contrast": "node scripts/check-preset-contrast.mjs"
129
129
  },
130
130
  "peerDependencies": {
131
- "@djangocfg/i18n": "^2.1.528",
131
+ "@djangocfg/i18n": "^2.1.530",
132
132
  "consola": "^3.4.2",
133
133
  "lucide-react": "^0.545.0",
134
134
  "moment": "^2.30.1",
@@ -206,8 +206,8 @@
206
206
  "@chenglou/pretext": "^0.0.8"
207
207
  },
208
208
  "devDependencies": {
209
- "@djangocfg/i18n": "^2.1.528",
210
- "@djangocfg/typescript-config": "^2.1.528",
209
+ "@djangocfg/i18n": "^2.1.530",
210
+ "@djangocfg/typescript-config": "^2.1.530",
211
211
  "@types/node": "^24.13.3",
212
212
  "@types/react": "19.2.15",
213
213
  "@types/react-dom": "19.2.3",
@@ -97,7 +97,11 @@ const verticalSizePx: Record<DrawerSize, number> = {
97
97
  };
98
98
 
99
99
  const directionStyles = {
100
- bottom: "inset-x-0 bottom-0 mt-24 rounded-t-lg border-t",
100
+ // `max-h-[85dvh]` belongs to the panel, not to a caller: with `height:auto`
101
+ // a tall panel would otherwise run off the top of a phone. A caller's own
102
+ // `max-h` cannot do this job — as a flex column the panel FILLS it, so the
103
+ // cap becomes a fixed height around short content.
104
+ bottom: "inset-x-0 bottom-0 mt-24 max-h-[85dvh] rounded-t-lg border-t",
101
105
  top: "inset-x-0 top-0 mb-24 rounded-b-lg border-b",
102
106
  right: "inset-y-0 right-0 h-full border-l",
103
107
  left: "inset-y-0 left-0 h-full border-r",
@@ -297,7 +301,25 @@ const DrawerContent = React.forwardRef<
297
301
  />
298
302
  )}
299
303
  {isVertical && <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />}
300
- {children}
304
+ {/*
305
+ Content padding lives HERE, not on each caller. `DialogContent` has
306
+ its own; this did not, so every surface that renders as a Dialog on
307
+ desktop and a Drawer on a phone lost its inset at the breakpoint —
308
+ section headings and values sat flush against the panel edge and read
309
+ as clipped. A default each caller must remember is a default nobody
310
+ applies.
311
+
312
+ The bottom inset is larger than the sides on purpose: it is what tells
313
+ a reader whether the list has ended or continues under the edge, and
314
+ on a phone it also clears the home indicator.
315
+
316
+ It owns the SCROLL but never the height: `flex-1` here filled the
317
+ panel and left the last row hundreds of pixels above the edge. Only
318
+ the panel's own `max-h` decides how tall the drawer gets.
319
+ */}
320
+ <div className="min-h-0 overflow-y-auto overscroll-contain px-5 pb-[max(1.25rem,env(safe-area-inset-bottom))]">
321
+ {children}
322
+ </div>
301
323
  </DrawerPrimitive.Content>
302
324
  </DrawerPortal>
303
325
  );
@@ -10,6 +10,7 @@
10
10
  import * as React from 'react';
11
11
 
12
12
  import { useIsMobile } from '../../../hooks';
13
+ import { cn } from '../../../lib/utils';
13
14
 
14
15
  import {
15
16
  Dialog,
@@ -74,20 +75,58 @@ ResponsiveSheet.displayName = "ResponsiveSheet"
74
75
  // Content
75
76
  // ─────────────────────────────────────────────────────────────────────────────
76
77
 
78
+ /** Reading width of the panel. `md` suits a form or a detail card. */
79
+ type ResponsiveSheetWidth = "sm" | "md" | "lg" | "xl";
80
+
81
+ const WIDTH: Record<ResponsiveSheetWidth, string> = {
82
+ sm: "sm:max-w-sm",
83
+ md: "sm:max-w-md",
84
+ lg: "sm:max-w-lg",
85
+ xl: "sm:max-w-xl",
86
+ };
87
+
77
88
  interface ResponsiveSheetContentProps {
78
89
  children: React.ReactNode;
79
90
  className?: string;
91
+ /** Panel width on a wide viewport. A phone always takes the full width. */
92
+ width?: ResponsiveSheetWidth;
80
93
  }
81
94
 
82
- function ResponsiveSheetContent({ children, className }: ResponsiveSheetContentProps) {
95
+ /**
96
+ * The panel.
97
+ *
98
+ * It arrives sized, inset and centred — a caller passes CONTENT, not a layout.
99
+ * Every default here exists because a caller had to supply it by hand and the
100
+ * two callers we had disagreed: the same `w-[calc(100%-1rem)]` twice, one of
101
+ * them with a `flex flex-col` that made a short card stand full height.
102
+ *
103
+ * The phone case is where this matters. `DialogContent` carries its own
104
+ * padding and centring; `DrawerContent` does not, so anything that renders as
105
+ * both lost its layout at the breakpoint and nowhere else.
106
+ */
107
+ function ResponsiveSheetContent({
108
+ children,
109
+ className,
110
+ width = "md",
111
+ }: ResponsiveSheetContentProps) {
83
112
  const { isMobile } = React.useContext(ResponsiveSheetContext);
84
113
 
85
114
  if (isMobile) {
86
- // Preserve legacy content-hugging behavior; new Drawer default is `md`.
87
- return <DrawerContent className={className} height="auto">{children}</DrawerContent>;
115
+ // `height="auto"` hugs the content; the drawer's own `max-h` is the
116
+ // ceiling. A cap passed from here would become a fixed height instead,
117
+ // because the panel is a flex column and fills what it is given.
118
+ return (
119
+ <DrawerContent className={className} height="auto">
120
+ {children}
121
+ </DrawerContent>
122
+ );
88
123
  }
89
124
 
90
- return <DialogContent className={className}>{children}</DialogContent>;
125
+ return (
126
+ <DialogContent className={cn("w-[calc(100%-1rem)]", WIDTH[width], className)}>
127
+ {children}
128
+ </DialogContent>
129
+ );
91
130
  }
92
131
  ResponsiveSheetContent.displayName = "ResponsiveSheetContent"
93
132