@gfazioli/mantine-window 2.0.0 → 3.0.0

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.
Files changed (32) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/Window.cjs +145 -106
  3. package/dist/cjs/Window.cjs.map +1 -1
  4. package/dist/cjs/WindowGroup.cjs +37 -18
  5. package/dist/cjs/WindowGroup.cjs.map +1 -1
  6. package/dist/cjs/WindowGroup.context.cjs +6 -2
  7. package/dist/cjs/WindowGroup.context.cjs.map +1 -1
  8. package/dist/cjs/hooks/use-mantine-window.cjs +4 -3
  9. package/dist/cjs/hooks/use-mantine-window.cjs.map +1 -1
  10. package/dist/cjs/hooks/use-window-dimensions.cjs +20 -9
  11. package/dist/cjs/hooks/use-window-dimensions.cjs.map +1 -1
  12. package/dist/cjs/hooks/use-window-drag.cjs.map +1 -1
  13. package/dist/cjs/hooks/use-window-state.cjs.map +1 -1
  14. package/dist/esm/Window.mjs +147 -108
  15. package/dist/esm/Window.mjs.map +1 -1
  16. package/dist/esm/WindowGroup.context.mjs +6 -2
  17. package/dist/esm/WindowGroup.context.mjs.map +1 -1
  18. package/dist/esm/WindowGroup.mjs +38 -19
  19. package/dist/esm/WindowGroup.mjs.map +1 -1
  20. package/dist/esm/hooks/use-mantine-window.mjs +4 -3
  21. package/dist/esm/hooks/use-mantine-window.mjs.map +1 -1
  22. package/dist/esm/hooks/use-window-dimensions.mjs +22 -11
  23. package/dist/esm/hooks/use-window-dimensions.mjs.map +1 -1
  24. package/dist/esm/hooks/use-window-drag.mjs.map +1 -1
  25. package/dist/esm/hooks/use-window-state.mjs.map +1 -1
  26. package/dist/types/Window.d.ts +6 -0
  27. package/dist/types/WindowGroup.context.d.ts +2 -4
  28. package/dist/types/hooks/use-mantine-window.d.ts +1 -1
  29. package/dist/types/hooks/use-window-constraints.d.ts +7 -7
  30. package/dist/types/hooks/use-window-dimensions.d.ts +1 -1
  31. package/dist/types/hooks/use-window-drag.d.ts +1 -1
  32. package/package.json +2 -2
package/README.md CHANGED
@@ -18,6 +18,7 @@
18
18
  ## Overview
19
19
 
20
20
  This component is created on top of the [Mantine](https://mantine.dev/) library.
21
+ It requires **Mantine 9.x** and **React 19**.
21
22
 
22
23
  [Mantine Window](https://gfazioli.github.io/mantine-window/) is a versatile UI container that brings a familiar desktop‑like window experience to React applications built with Mantine.
23
24
 
@@ -37,6 +38,9 @@ This component is created on top of the [Mantine](https://mantine.dev/) library.
37
38
  - Full Mantine Styles API support with fine-grained classNames
38
39
  - SSR-safe with hydration-compatible viewport unit resolution
39
40
  - `onPositionChange` and `onSizeChange` callbacks
41
+ - `withScrollArea` prop to disable the internal ScrollArea wrapper
42
+ - `controlsPosition` prop for macOS-style (left) or Windows-style (right) button placement
43
+ - `controlsOrder` prop for custom button ordering (close, collapse, tools)
40
44
 
41
45
  > [!note]
42
46
  >
@@ -20,6 +20,8 @@ const defaultProps = {
20
20
  withCollapseButton: true,
21
21
  collapsable: true,
22
22
  withToolsButton: true,
23
+ withScrollArea: true,
24
+ controlsPosition: "left",
23
25
  persistState: false,
24
26
  withinPortal: true,
25
27
  minWidth: 250,
@@ -50,7 +52,7 @@ const varsResolver = core.createVarsResolver((_, { radius, shadow }) => {
50
52
  resizeHandleLeft: {}
51
53
  };
52
54
  });
53
- const Window = core.factory((_props, _) => {
55
+ const Window = core.factory((_props) => {
54
56
  const props = core.useProps("Window", defaultProps, _props);
55
57
  const {
56
58
  opened,
@@ -64,6 +66,9 @@ const Window = core.factory((_props, _) => {
64
66
  collapsable,
65
67
  withCloseButton,
66
68
  withToolsButton,
69
+ withScrollArea,
70
+ controlsPosition,
71
+ controlsOrder: controlsOrderProp,
67
72
  onClose,
68
73
  radius,
69
74
  shadow,
@@ -140,11 +145,11 @@ const Window = core.factory((_props, _) => {
140
145
  mod: [{ "data-with-border": withBorder, "data-window-draggable": draggableWindow }, mod],
141
146
  onMouseDown: draggableWindow ? handleMouseDownDrag : void 0,
142
147
  onTouchStart: draggableWindow ? handleTouchStartDrag : void 0,
143
- bg: color,
144
148
  role: "dialog",
145
149
  "aria-label": title || props.id || "Window",
146
150
  "data-mantine-window": true,
147
151
  ...others,
152
+ bg: color,
148
153
  ...getStyles("root", {
149
154
  style: {
150
155
  position: resolvedWithinPortal ? "fixed" : "absolute",
@@ -161,114 +166,137 @@ const Window = core.factory((_props, _) => {
161
166
  {
162
167
  ...getStyles("header"),
163
168
  onClick: bringToFront,
164
- mod: { "window-draggable": draggableHeader },
169
+ mod: { "window-draggable": draggableHeader, "controls-position": controlsPosition },
165
170
  onMouseDown: draggableHeader ? handleMouseDownDrag : void 0,
166
171
  onTouchStart: draggableHeader ? handleTouchStartDrag : void 0,
167
172
  onDoubleClick: () => collapsable && setIsCollapsed(!isCollapsed)
168
173
  },
169
- /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: "xs", miw: 0 }, /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: 8 }, withCloseButton && /* @__PURE__ */ React.createElement(
170
- core.ActionIcon,
171
- {
172
- radius: 256,
173
- color: "red",
174
- onClick: handleClose,
175
- "aria-label": "Close window",
176
- ...getStyles("closeButton")
177
- },
178
- /* @__PURE__ */ React.createElement(iconsReact.IconX, { size: 14 })
179
- ), withCollapseButton && collapsable && /* @__PURE__ */ React.createElement(
180
- core.ActionIcon,
181
- {
182
- radius: 256,
183
- color: "yellow",
184
- onClick: () => setIsCollapsed(!isCollapsed),
185
- "aria-label": isCollapsed ? "Expand window" : "Collapse window",
186
- ...getStyles("collapseButton")
187
- },
188
- isCollapsed ? /* @__PURE__ */ React.createElement(iconsReact.IconPlus, { size: 14 }) : /* @__PURE__ */ React.createElement(iconsReact.IconMinus, { size: 14 })
189
- ), withToolsButton && (groupCtx?.showToolsButton ?? true) && /* @__PURE__ */ React.createElement(core.Menu, { shadow: "md", width: 220, position: "bottom-start", withArrow: true }, /* @__PURE__ */ React.createElement(core.Menu.Target, null, /* @__PURE__ */ React.createElement(
190
- core.ActionIcon,
191
- {
192
- radius: 256,
193
- color: "green",
194
- "aria-label": "Window layout options",
195
- ...getStyles("windowToolsButton")
196
- },
197
- /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, { size: 14 })
198
- )), /* @__PURE__ */ React.createElement(core.Menu.Dropdown, null, /* @__PURE__ */ React.createElement(core.Menu.Label, null, "Move & Resize"), /* @__PURE__ */ React.createElement(core.Flex, { justify: "space-between", pb: "xs", px: "sm" }, /* @__PURE__ */ React.createElement(
199
- core.ActionIcon,
200
- {
201
- variant: "subtle",
202
- size: "lg",
203
- "aria-label": "Snap left",
204
- onClick: () => applySingleLayout("snap-left")
205
- },
206
- /* @__PURE__ */ React.createElement(LayoutIcons.SnapLeftIcon, null)
207
- ), /* @__PURE__ */ React.createElement(
208
- core.ActionIcon,
209
- {
210
- variant: "subtle",
211
- size: "lg",
212
- "aria-label": "Snap right",
213
- onClick: () => applySingleLayout("snap-right")
214
- },
215
- /* @__PURE__ */ React.createElement(LayoutIcons.SnapRightIcon, null)
216
- ), /* @__PURE__ */ React.createElement(
217
- core.ActionIcon,
218
- {
219
- variant: "subtle",
220
- size: "lg",
221
- "aria-label": "Snap top",
222
- onClick: () => applySingleLayout("snap-top")
223
- },
224
- /* @__PURE__ */ React.createElement(LayoutIcons.SnapTopIcon, null)
225
- ), /* @__PURE__ */ React.createElement(
226
- core.ActionIcon,
227
- {
228
- variant: "subtle",
229
- size: "lg",
230
- "aria-label": "Snap bottom",
231
- onClick: () => applySingleLayout("snap-bottom")
232
- },
233
- /* @__PURE__ */ React.createElement(LayoutIcons.SnapBottomIcon, null)
234
- )), /* @__PURE__ */ React.createElement(core.Menu.Divider, null), /* @__PURE__ */ React.createElement(core.Menu.Label, null, "Fill & Arrange"), /* @__PURE__ */ React.createElement(core.Flex, { justify: "space-between", pb: "xs", px: "sm" }, /* @__PURE__ */ React.createElement(
235
- core.ActionIcon,
236
- {
237
- variant: "subtle",
238
- size: "lg",
239
- "aria-label": "Fill",
240
- onClick: () => applySingleLayout("fill")
241
- },
242
- /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, null)
243
- ), groupCtx && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
244
- core.ActionIcon,
245
- {
246
- variant: "subtle",
247
- size: "lg",
248
- "aria-label": "Arrange columns",
249
- onClick: () => groupCtx.applyLayout("arrange-columns")
250
- },
251
- /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeColumnsIcon, null)
252
- ), /* @__PURE__ */ React.createElement(
253
- core.ActionIcon,
254
- {
255
- variant: "subtle",
256
- size: "lg",
257
- "aria-label": "Arrange rows",
258
- onClick: () => groupCtx.applyLayout("arrange-rows")
259
- },
260
- /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeRowsIcon, null)
261
- ), /* @__PURE__ */ React.createElement(
262
- core.ActionIcon,
263
- {
264
- variant: "subtle",
265
- size: "lg",
266
- "aria-label": "Tile",
267
- onClick: () => groupCtx.applyLayout("tile")
268
- },
269
- /* @__PURE__ */ React.createElement(LayoutIcons.TileIcon, null)
270
- ))), groupCtx && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Menu.Divider, null), /* @__PURE__ */ React.createElement(core.Menu.Item, { onClick: () => groupCtx.collapseAll() }, "Collapse all"), /* @__PURE__ */ React.createElement(core.Menu.Item, { onClick: () => groupCtx.expandAll() }, "Expand all"), /* @__PURE__ */ React.createElement(core.Menu.Item, { color: "red", onClick: () => groupCtx.closeAll() }, "Close all"))))), /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: "xs", miw: 0 }, /* @__PURE__ */ React.createElement(core.Text, { truncate: true, ...getStyles("title") }, title)))
271
- ), !isCollapsed && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
174
+ (() => {
175
+ const defaultOrder = controlsPosition === "right" ? ["tools", "collapse", "close"] : ["close", "collapse", "tools"];
176
+ const order = controlsOrderProp ?? defaultOrder;
177
+ const controlElements = {
178
+ close: withCloseButton ? /* @__PURE__ */ React.createElement(
179
+ core.ActionIcon,
180
+ {
181
+ key: "close",
182
+ radius: 256,
183
+ color: "red",
184
+ onClick: handleClose,
185
+ "aria-label": "Close window",
186
+ ...getStyles("closeButton")
187
+ },
188
+ /* @__PURE__ */ React.createElement(iconsReact.IconX, { size: 14 })
189
+ ) : null,
190
+ collapse: withCollapseButton && collapsable ? /* @__PURE__ */ React.createElement(
191
+ core.ActionIcon,
192
+ {
193
+ key: "collapse",
194
+ radius: 256,
195
+ color: "yellow",
196
+ onClick: () => setIsCollapsed(!isCollapsed),
197
+ "aria-label": isCollapsed ? "Expand window" : "Collapse window",
198
+ ...getStyles("collapseButton")
199
+ },
200
+ isCollapsed ? /* @__PURE__ */ React.createElement(iconsReact.IconPlus, { size: 14 }) : /* @__PURE__ */ React.createElement(iconsReact.IconMinus, { size: 14 })
201
+ ) : null,
202
+ tools: withToolsButton && (groupCtx?.showToolsButton ?? true) ? /* @__PURE__ */ React.createElement(core.Menu, { key: "tools", shadow: "md", width: 220, position: "bottom-start", withArrow: true }, /* @__PURE__ */ React.createElement(core.Menu.Target, null, /* @__PURE__ */ React.createElement(
203
+ core.ActionIcon,
204
+ {
205
+ radius: 256,
206
+ color: "green",
207
+ "aria-label": "Window layout options",
208
+ ...getStyles("windowToolsButton")
209
+ },
210
+ /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, { size: 14 })
211
+ )), /* @__PURE__ */ React.createElement(core.Menu.Dropdown, null, /* @__PURE__ */ React.createElement(core.Menu.Label, null, "Move & Resize"), /* @__PURE__ */ React.createElement(core.Flex, { justify: "space-between", pb: "xs", px: "sm" }, /* @__PURE__ */ React.createElement(
212
+ core.ActionIcon,
213
+ {
214
+ variant: "subtle",
215
+ size: "lg",
216
+ "aria-label": "Snap left",
217
+ onClick: () => applySingleLayout("snap-left")
218
+ },
219
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapLeftIcon, null)
220
+ ), /* @__PURE__ */ React.createElement(
221
+ core.ActionIcon,
222
+ {
223
+ variant: "subtle",
224
+ size: "lg",
225
+ "aria-label": "Snap right",
226
+ onClick: () => applySingleLayout("snap-right")
227
+ },
228
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapRightIcon, null)
229
+ ), /* @__PURE__ */ React.createElement(
230
+ core.ActionIcon,
231
+ {
232
+ variant: "subtle",
233
+ size: "lg",
234
+ "aria-label": "Snap top",
235
+ onClick: () => applySingleLayout("snap-top")
236
+ },
237
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapTopIcon, null)
238
+ ), /* @__PURE__ */ React.createElement(
239
+ core.ActionIcon,
240
+ {
241
+ variant: "subtle",
242
+ size: "lg",
243
+ "aria-label": "Snap bottom",
244
+ onClick: () => applySingleLayout("snap-bottom")
245
+ },
246
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapBottomIcon, null)
247
+ )), /* @__PURE__ */ React.createElement(core.Menu.Divider, null), /* @__PURE__ */ React.createElement(core.Menu.Label, null, "Fill & Arrange"), /* @__PURE__ */ React.createElement(core.Flex, { justify: "space-between", pb: "xs", px: "sm" }, /* @__PURE__ */ React.createElement(
248
+ core.ActionIcon,
249
+ {
250
+ variant: "subtle",
251
+ size: "lg",
252
+ "aria-label": "Fill",
253
+ onClick: () => applySingleLayout("fill")
254
+ },
255
+ /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, null)
256
+ ), groupCtx && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
257
+ core.ActionIcon,
258
+ {
259
+ variant: "subtle",
260
+ size: "lg",
261
+ "aria-label": "Arrange columns",
262
+ onClick: () => groupCtx.applyLayout("arrange-columns")
263
+ },
264
+ /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeColumnsIcon, null)
265
+ ), /* @__PURE__ */ React.createElement(
266
+ core.ActionIcon,
267
+ {
268
+ variant: "subtle",
269
+ size: "lg",
270
+ "aria-label": "Arrange rows",
271
+ onClick: () => groupCtx.applyLayout("arrange-rows")
272
+ },
273
+ /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeRowsIcon, null)
274
+ ), /* @__PURE__ */ React.createElement(
275
+ core.ActionIcon,
276
+ {
277
+ variant: "subtle",
278
+ size: "lg",
279
+ "aria-label": "Tile",
280
+ onClick: () => groupCtx.applyLayout("tile")
281
+ },
282
+ /* @__PURE__ */ React.createElement(LayoutIcons.TileIcon, null)
283
+ ))), groupCtx && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Menu.Divider, null), /* @__PURE__ */ React.createElement(core.Menu.Item, { onClick: () => groupCtx.collapseAll() }, "Collapse all"), /* @__PURE__ */ React.createElement(core.Menu.Item, { onClick: () => groupCtx.expandAll() }, "Expand all"), /* @__PURE__ */ React.createElement(core.Menu.Item, { color: "red", onClick: () => groupCtx.closeAll() }, "Close all")))) : null
284
+ };
285
+ const controlsGroup = /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: 8 }, order.map((name) => controlElements[name]));
286
+ const titleGroup = /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: "xs", miw: 0 }, /* @__PURE__ */ React.createElement(core.Text, { truncate: true, ...getStyles("title") }, title));
287
+ return /* @__PURE__ */ React.createElement(
288
+ core.Flex,
289
+ {
290
+ align: "center",
291
+ gap: "xs",
292
+ miw: 0,
293
+ justify: controlsPosition === "right" ? "space-between" : void 0,
294
+ w: controlsPosition === "right" ? "100%" : void 0
295
+ },
296
+ controlsPosition === "left" ? /* @__PURE__ */ React.createElement(React.Fragment, null, controlsGroup, titleGroup) : /* @__PURE__ */ React.createElement(React.Fragment, null, titleGroup, controlsGroup)
297
+ );
298
+ })()
299
+ ), !isCollapsed && /* @__PURE__ */ React.createElement(React.Fragment, null, withScrollArea ? /* @__PURE__ */ React.createElement(
272
300
  core.ScrollArea,
273
301
  {
274
302
  ...getStyles("content", {
@@ -278,6 +306,17 @@ const Window = core.factory((_props, _) => {
278
306
  })
279
307
  },
280
308
  children
309
+ ) : /* @__PURE__ */ React.createElement(
310
+ core.Box,
311
+ {
312
+ ...getStyles("content", {
313
+ style: {
314
+ height: Math.max(0, size.height - HEADER_HEIGHT),
315
+ overflow: "hidden"
316
+ }
317
+ })
318
+ },
319
+ children
281
320
  ), resizable !== "none" && /* @__PURE__ */ React.createElement(React.Fragment, null, resizable === "both" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
282
321
  core.Box,
283
322
  {