@gfazioli/mantine-window 2.0.0 → 3.1.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 (36) hide show
  1. package/README.md +4 -0
  2. package/dist/cjs/Window.cjs +147 -106
  3. package/dist/cjs/Window.cjs.map +1 -1
  4. package/dist/cjs/WindowGroup.cjs +138 -41
  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 +9 -4
  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 +35 -12
  14. package/dist/cjs/hooks/use-window-state.cjs.map +1 -1
  15. package/dist/esm/Window.mjs +149 -108
  16. package/dist/esm/Window.mjs.map +1 -1
  17. package/dist/esm/WindowGroup.context.mjs +6 -2
  18. package/dist/esm/WindowGroup.context.mjs.map +1 -1
  19. package/dist/esm/WindowGroup.mjs +139 -42
  20. package/dist/esm/WindowGroup.mjs.map +1 -1
  21. package/dist/esm/hooks/use-mantine-window.mjs +9 -4
  22. package/dist/esm/hooks/use-mantine-window.mjs.map +1 -1
  23. package/dist/esm/hooks/use-window-dimensions.mjs +22 -11
  24. package/dist/esm/hooks/use-window-dimensions.mjs.map +1 -1
  25. package/dist/esm/hooks/use-window-drag.mjs.map +1 -1
  26. package/dist/esm/hooks/use-window-state.mjs +35 -12
  27. package/dist/esm/hooks/use-window-state.mjs.map +1 -1
  28. package/dist/types/Window.d.ts +17 -0
  29. package/dist/types/WindowGroup.context.d.ts +16 -4
  30. package/dist/types/WindowGroup.d.ts +23 -1
  31. package/dist/types/hooks/use-mantine-window.d.ts +1 -1
  32. package/dist/types/hooks/use-window-constraints.d.ts +7 -7
  33. package/dist/types/hooks/use-window-dimensions.d.ts +1 -1
  34. package/dist/types/hooks/use-window-drag.d.ts +1 -1
  35. package/dist/types/hooks/use-window-state.d.ts +4 -0
  36. 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,
@@ -84,6 +89,8 @@ const Window = core.factory((_props, _) => {
84
89
  defaultHeight,
85
90
  onPositionChange,
86
91
  onSizeChange,
92
+ initialZIndex,
93
+ maxZIndex,
87
94
  withBorder,
88
95
  fullSizeResizeHandles,
89
96
  mod,
@@ -140,11 +147,11 @@ const Window = core.factory((_props, _) => {
140
147
  mod: [{ "data-with-border": withBorder, "data-window-draggable": draggableWindow }, mod],
141
148
  onMouseDown: draggableWindow ? handleMouseDownDrag : void 0,
142
149
  onTouchStart: draggableWindow ? handleTouchStartDrag : void 0,
143
- bg: color,
144
150
  role: "dialog",
145
151
  "aria-label": title || props.id || "Window",
146
152
  "data-mantine-window": true,
147
153
  ...others,
154
+ bg: color,
148
155
  ...getStyles("root", {
149
156
  style: {
150
157
  position: resolvedWithinPortal ? "fixed" : "absolute",
@@ -161,114 +168,137 @@ const Window = core.factory((_props, _) => {
161
168
  {
162
169
  ...getStyles("header"),
163
170
  onClick: bringToFront,
164
- mod: { "window-draggable": draggableHeader },
171
+ mod: { "window-draggable": draggableHeader, "controls-position": controlsPosition },
165
172
  onMouseDown: draggableHeader ? handleMouseDownDrag : void 0,
166
173
  onTouchStart: draggableHeader ? handleTouchStartDrag : void 0,
167
174
  onDoubleClick: () => collapsable && setIsCollapsed(!isCollapsed)
168
175
  },
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(
176
+ (() => {
177
+ const defaultOrder = controlsPosition === "right" ? ["tools", "collapse", "close"] : ["close", "collapse", "tools"];
178
+ const order = controlsOrderProp ?? defaultOrder;
179
+ const controlElements = {
180
+ close: withCloseButton ? /* @__PURE__ */ React.createElement(
181
+ core.ActionIcon,
182
+ {
183
+ key: "close",
184
+ radius: 256,
185
+ color: "red",
186
+ onClick: handleClose,
187
+ "aria-label": "Close window",
188
+ ...getStyles("closeButton")
189
+ },
190
+ /* @__PURE__ */ React.createElement(iconsReact.IconX, { size: 14 })
191
+ ) : null,
192
+ collapse: withCollapseButton && collapsable ? /* @__PURE__ */ React.createElement(
193
+ core.ActionIcon,
194
+ {
195
+ key: "collapse",
196
+ radius: 256,
197
+ color: "yellow",
198
+ onClick: () => setIsCollapsed(!isCollapsed),
199
+ "aria-label": isCollapsed ? "Expand window" : "Collapse window",
200
+ ...getStyles("collapseButton")
201
+ },
202
+ isCollapsed ? /* @__PURE__ */ React.createElement(iconsReact.IconPlus, { size: 14 }) : /* @__PURE__ */ React.createElement(iconsReact.IconMinus, { size: 14 })
203
+ ) : null,
204
+ 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(
205
+ core.ActionIcon,
206
+ {
207
+ radius: 256,
208
+ color: "green",
209
+ "aria-label": "Window layout options",
210
+ ...getStyles("windowToolsButton")
211
+ },
212
+ /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, { size: 14 })
213
+ )), /* @__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(
214
+ core.ActionIcon,
215
+ {
216
+ variant: "subtle",
217
+ size: "lg",
218
+ "aria-label": "Snap left",
219
+ onClick: () => applySingleLayout("snap-left")
220
+ },
221
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapLeftIcon, null)
222
+ ), /* @__PURE__ */ React.createElement(
223
+ core.ActionIcon,
224
+ {
225
+ variant: "subtle",
226
+ size: "lg",
227
+ "aria-label": "Snap right",
228
+ onClick: () => applySingleLayout("snap-right")
229
+ },
230
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapRightIcon, null)
231
+ ), /* @__PURE__ */ React.createElement(
232
+ core.ActionIcon,
233
+ {
234
+ variant: "subtle",
235
+ size: "lg",
236
+ "aria-label": "Snap top",
237
+ onClick: () => applySingleLayout("snap-top")
238
+ },
239
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapTopIcon, null)
240
+ ), /* @__PURE__ */ React.createElement(
241
+ core.ActionIcon,
242
+ {
243
+ variant: "subtle",
244
+ size: "lg",
245
+ "aria-label": "Snap bottom",
246
+ onClick: () => applySingleLayout("snap-bottom")
247
+ },
248
+ /* @__PURE__ */ React.createElement(LayoutIcons.SnapBottomIcon, null)
249
+ )), /* @__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(
250
+ core.ActionIcon,
251
+ {
252
+ variant: "subtle",
253
+ size: "lg",
254
+ "aria-label": "Fill",
255
+ onClick: () => applySingleLayout("fill")
256
+ },
257
+ /* @__PURE__ */ React.createElement(LayoutIcons.FillIcon, null)
258
+ ), groupCtx && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
259
+ core.ActionIcon,
260
+ {
261
+ variant: "subtle",
262
+ size: "lg",
263
+ "aria-label": "Arrange columns",
264
+ onClick: () => groupCtx.applyLayout("arrange-columns")
265
+ },
266
+ /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeColumnsIcon, null)
267
+ ), /* @__PURE__ */ React.createElement(
268
+ core.ActionIcon,
269
+ {
270
+ variant: "subtle",
271
+ size: "lg",
272
+ "aria-label": "Arrange rows",
273
+ onClick: () => groupCtx.applyLayout("arrange-rows")
274
+ },
275
+ /* @__PURE__ */ React.createElement(LayoutIcons.ArrangeRowsIcon, null)
276
+ ), /* @__PURE__ */ React.createElement(
277
+ core.ActionIcon,
278
+ {
279
+ variant: "subtle",
280
+ size: "lg",
281
+ "aria-label": "Tile",
282
+ onClick: () => groupCtx.applyLayout("tile")
283
+ },
284
+ /* @__PURE__ */ React.createElement(LayoutIcons.TileIcon, null)
285
+ ))), 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
286
+ };
287
+ const controlsGroup = /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: 8 }, order.map((name) => controlElements[name]));
288
+ const titleGroup = /* @__PURE__ */ React.createElement(core.Flex, { align: "center", gap: "xs", miw: 0 }, /* @__PURE__ */ React.createElement(core.Text, { truncate: true, ...getStyles("title") }, title));
289
+ return /* @__PURE__ */ React.createElement(
290
+ core.Flex,
291
+ {
292
+ align: "center",
293
+ gap: "xs",
294
+ miw: 0,
295
+ justify: controlsPosition === "right" ? "space-between" : void 0,
296
+ w: controlsPosition === "right" ? "100%" : void 0
297
+ },
298
+ controlsPosition === "left" ? /* @__PURE__ */ React.createElement(React.Fragment, null, controlsGroup, titleGroup) : /* @__PURE__ */ React.createElement(React.Fragment, null, titleGroup, controlsGroup)
299
+ );
300
+ })()
301
+ ), !isCollapsed && /* @__PURE__ */ React.createElement(React.Fragment, null, withScrollArea ? /* @__PURE__ */ React.createElement(
272
302
  core.ScrollArea,
273
303
  {
274
304
  ...getStyles("content", {
@@ -278,6 +308,17 @@ const Window = core.factory((_props, _) => {
278
308
  })
279
309
  },
280
310
  children
311
+ ) : /* @__PURE__ */ React.createElement(
312
+ core.Box,
313
+ {
314
+ ...getStyles("content", {
315
+ style: {
316
+ height: Math.max(0, size.height - HEADER_HEIGHT),
317
+ overflow: "hidden"
318
+ }
319
+ })
320
+ },
321
+ children
281
322
  ), resizable !== "none" && /* @__PURE__ */ React.createElement(React.Fragment, null, resizable === "both" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
282
323
  core.Box,
283
324
  {