@djangocfg/ui-core 2.1.537 → 2.1.539

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.537",
3
+ "version": "2.1.539",
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.537",
131
+ "@djangocfg/i18n": "^2.1.539",
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.537",
210
- "@djangocfg/typescript-config": "^2.1.537",
209
+ "@djangocfg/i18n": "^2.1.539",
210
+ "@djangocfg/typescript-config": "^2.1.539",
211
211
  "@types/node": "^24.13.3",
212
212
  "@types/react": "19.2.15",
213
213
  "@types/react-dom": "19.2.3",
@@ -101,7 +101,23 @@ const DialogContent = React.forwardRef<
101
101
  // hard-coded, which made the width unoverridable in practice — a
102
102
  // `max-width` beside a declared `width` is inert, so callers that
103
103
  // needed a wider dialog silently got 32rem.
104
- : "left-1/2 top-1/2 grid w-[min(var(--width-dialog),calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--radius-dialog)] border p-6 shadow-lg data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
104
+ //
105
+ // `grid-cols-[minmax(0,1fr)]` is the same fix as the fullscreen
106
+ // branch's rows, on the other axis: a grid column also defaults to
107
+ // `min-width:auto`, so one unbreakable child — a long description, a
108
+ // path, a token — refuses to shrink and pushes the card past the
109
+ // width declared right here. The clamp reads as inert when that
110
+ // happens, and the overflow lands on whichever child is widest
111
+ // rather than on the one at fault.
112
+ //
113
+ // HEIGHT is capped for the same reason width is clamped. The card is
114
+ // `fixed` and centred by `-translate-y-1/2`, so content taller than
115
+ // the viewport grows the box and then centres it on its own overflow:
116
+ // BOTH ends leave the screen, and because nothing clips, nothing
117
+ // scrolls. The cap is only a ceiling — a card that must scroll adds
118
+ // `overflow-y-auto`, and one laying out its own regions overrides
119
+ // `max-h` as before.
120
+ : "left-1/2 top-1/2 grid max-h-[calc(100dvh-2rem)] grid-cols-[minmax(0,1fr)] w-[min(var(--width-dialog),calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--radius-dialog)] border p-6 shadow-lg data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
105
121
  className
106
122
  )}
107
123
  {...props}
@@ -90,6 +90,14 @@ interface ResponsiveSheetContentProps {
90
90
  className?: string;
91
91
  /** Panel width on a wide viewport. A phone always takes the full width. */
92
92
  width?: ResponsiveSheetWidth;
93
+ /**
94
+ * Cap the panel at the viewport and scroll its body. Default.
95
+ *
96
+ * `false` for a panel that lays out its own scrolling regions — a header and
97
+ * footer that must stay put around a scrolling middle. It then owns the cap
98
+ * too; without one the overflow returns.
99
+ */
100
+ scroll?: boolean;
93
101
  }
94
102
 
95
103
  /**
@@ -103,11 +111,20 @@ interface ResponsiveSheetContentProps {
103
111
  * The phone case is where this matters. `DialogContent` carries its own
104
112
  * padding and centring; `DrawerContent` does not, so anything that renders as
105
113
  * both lost its layout at the breakpoint and nowhere else.
114
+ *
115
+ * HEIGHT is a default here for the same reason width is. The desktop
116
+ * `DialogContent` is `fixed` and centred by `-translate-y-1/2` with no cap, so
117
+ * content taller than the viewport overflows BOTH edges — the top scrolls out
118
+ * of reach, and nothing clips, so nothing scrolls. Every caller that got this
119
+ * right passed the same `max-h-[85vh]` by hand; the one that forgot lost its
120
+ * header off the top. A caller that wants its own scrolling regions passes
121
+ * `scroll={false}` and owns the cap.
106
122
  */
107
123
  function ResponsiveSheetContent({
108
124
  children,
109
125
  className,
110
126
  width = "md",
127
+ scroll = true,
111
128
  }: ResponsiveSheetContentProps) {
112
129
  const { isMobile } = React.useContext(ResponsiveSheetContext);
113
130
 
@@ -122,9 +139,26 @@ function ResponsiveSheetContent({
122
139
  );
123
140
  }
124
141
 
142
+ // The cap sits on the panel and the scroll on an inner box, never both on
143
+ // one element: `DialogContent` is a grid, and a scrollport that is also the
144
+ // grid would clip the close button pinned to its corner.
145
+ if (!scroll) {
146
+ return (
147
+ <DialogContent className={cn("w-[calc(100%-1rem)]", WIDTH[width], className)}>
148
+ {children}
149
+ </DialogContent>
150
+ );
151
+ }
152
+
125
153
  return (
126
- <DialogContent className={cn("w-[calc(100%-1rem)]", WIDTH[width], className)}>
127
- {children}
154
+ <DialogContent
155
+ className={cn(
156
+ "max-h-[85dvh] w-[calc(100%-1rem)] grid-rows-[minmax(0,1fr)] overflow-hidden",
157
+ WIDTH[width],
158
+ className,
159
+ )}
160
+ >
161
+ <div className="min-h-0 overflow-y-auto overscroll-contain">{children}</div>
128
162
  </DialogContent>
129
163
  );
130
164
  }