Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
576 changes: 288 additions & 288 deletions .yarn/releases/yarn-4.13.0.cjs → .yarn/releases/yarn-4.14.1.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.13.0.cjs
yarnPath: .yarn/releases/yarn-4.14.1.cjs
88 changes: 88 additions & 0 deletions docs/demos/Window.demo.dynamicWindows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useState } from 'react';
import { Window } from '@gfazioli/mantine-window';
import { Button, Group, Title } from '@mantine/core';
import { MantineDemo } from '@mantinex/demo';

const code = `import { useState } from 'react';
import { Window } from '@gfazioli/mantine-window';
import { Button, Group, Title } from '@mantine/core';

function Demo() {
const [ids, setIds] = useState<string[]>(['alpha', 'beta']);

return (
<>
<Group mb="md">
<Button
onClick={() =>
setIds((prev) => [...prev, String.fromCharCode(97 + prev.length)])
}
>
Add window
</Button>
<Button color="red" onClick={() => setIds((prev) => prev.slice(0, -1))}>
Remove last
</Button>
</Group>
<Window.Group style={{ width: '100%', height: 500 }}>
{ids.map((id, i) => (
<Window
id={\`win_\${id}\`}
key={\`win_\${id}\`}
title={\`Window \${id}\`}
opened
defaultX={20 + i * 40}
defaultY={20 + i * 40}
defaultWidth={300}
defaultHeight={200}
>
<Title order={4}>Dynamic {id}</Title>
<p>Click the green tools button to apply a layout preset.</p>
</Window>
))}
</Window.Group>
</>
);
}
`;

function Demo() {
const [ids, setIds] = useState<string[]>(['alpha', 'beta']);

return (
<>
<Group mb="md">
<Button onClick={() => setIds((prev) => [...prev, String.fromCharCode(97 + prev.length)])}>
Add window
</Button>
<Button color="red" onClick={() => setIds((prev) => prev.slice(0, -1))}>
Remove last
</Button>
</Group>
<Window.Group style={{ width: '100%', height: 500 }}>
{ids.map((id, i) => (
<Window
id={`win_${id}`}
key={`win_${id}`}
title={`Window ${id}`}
Comment thread
gfazioli marked this conversation as resolved.
opened
defaultX={20 + i * 40}
defaultY={20 + i * 40}
defaultWidth={300}
defaultHeight={200}
>
<Title order={4}>Dynamic {id}</Title>
<p>Click the green tools button to apply a layout preset.</p>
</Window>
))}
</Window.Group>
</>
);
}

export const dynamicWindows: MantineDemo = {
type: 'code',
component: Demo,
code,
defaultExpanded: false,
};
118 changes: 118 additions & 0 deletions docs/demos/Window.demo.zIndexStrategy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { useState } from 'react';
import { Window } from '@gfazioli/mantine-window';
import { SegmentedControl, Stack, Text, Title } from '@mantine/core';
import { MantineDemo } from '@mantinex/demo';

const code = `import { useState } from 'react';
import { Window } from '@gfazioli/mantine-window';
import { SegmentedControl, Stack, Text, Title } from '@mantine/core';

function Demo() {
const [strategy, setStrategy] = useState<'increment' | 'normalize'>('normalize');

return (
<Stack>
<SegmentedControl
value={strategy}
onChange={(v) => setStrategy(v as 'increment' | 'normalize')}
data={[
{ label: 'increment (legacy)', value: 'increment' },
{ label: 'normalize', value: 'normalize' },
]}
/>
<Text size="sm" c="dimmed">
Click each window in any order. With <code>normalize</code>, z-indexes always stay
compact ({'{100..102}'}). With <code>increment</code>, the counter keeps growing
unless <code>maxZIndex</code> is set.
</Text>
<Window.Group
key={strategy}
style={{ width: '100%', height: 500 }}
zIndexStrategy={strategy}
initialZIndex={100}
maxZIndex={strategy === 'increment' ? 105 : undefined}
>
<Window id="z1" title="Alpha" opened defaultX={20} defaultY={20} defaultWidth={260} defaultHeight={180}>
<Title order={5}>Alpha</Title>
</Window>
<Window id="z2" title="Beta" opened defaultX={80} defaultY={80} defaultWidth={260} defaultHeight={180}>
<Title order={5}>Beta</Title>
</Window>
<Window id="z3" title="Gamma" opened defaultX={140} defaultY={140} defaultWidth={260} defaultHeight={180}>
<Title order={5}>Gamma</Title>
</Window>
</Window.Group>
</Stack>
);
}
`;

function Demo() {
const [strategy, setStrategy] = useState<'increment' | 'normalize'>('normalize');

return (
<Stack>
<SegmentedControl
value={strategy}
onChange={(v) => setStrategy(v as 'increment' | 'normalize')}
data={[
{ label: 'increment (legacy)', value: 'increment' },
{ label: 'normalize', value: 'normalize' },
]}
/>
<Text size="sm" c="dimmed">
Click each window in any order. With <code>normalize</code>, z-indexes always stay compact (
{'{100..102}'}). With <code>increment</code>, the counter keeps growing unless{' '}
<code>maxZIndex</code> is set.
</Text>
<Window.Group
key={strategy}
style={{ width: '100%', height: 500 }}
zIndexStrategy={strategy}
initialZIndex={100}
maxZIndex={strategy === 'increment' ? 105 : undefined}
>
<Window
id="z1"
title="Alpha"
opened
defaultX={20}
defaultY={20}
defaultWidth={260}
defaultHeight={180}
>
<Title order={5}>Alpha</Title>
</Window>
<Window
id="z2"
title="Beta"
opened
defaultX={80}
defaultY={80}
defaultWidth={260}
defaultHeight={180}
>
<Title order={5}>Beta</Title>
</Window>
<Window
id="z3"
title="Gamma"
opened
defaultX={140}
defaultY={140}
defaultWidth={260}
defaultHeight={180}
>
<Title order={5}>Gamma</Title>
</Window>
</Window.Group>
</Stack>
);
}

export const zIndexStrategy: MantineDemo = {
type: 'code',
component: Demo,
code,
defaultExpanded: false,
};
2 changes: 2 additions & 0 deletions docs/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { controlsPositionDemo } from './Window.demo.controlsPosition';
export { controlled } from './Window.demo.controlled';
export { controlledPosition } from './Window.demo.controlledPosition';
export { dragBounds } from './Window.demo.dragBounds';
export { dynamicWindows } from './Window.demo.dynamicWindows';
export { fullSizeHandles } from './Window.demo.fullSizeHandles';
export { group } from './Window.demo.group';
export { groupLayout } from './Window.demo.groupLayout';
Expand All @@ -21,3 +22,4 @@ export { stylesApi } from './Window.demo.stylesApi';
export { unitTypes } from './Window.demo.unitTypes';
export { withScrollAreaDemo } from './Window.demo.withScrollArea';
export { withinPortal } from './Window.demo.withinPortal';
export { zIndexStrategy } from './Window.demo.zIndexStrategy';
18 changes: 18 additions & 0 deletions docs/docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ Use `defaultLayout` to apply an initial layout when the group mounts. To control

<Demo data={demos.layoutPresets} />

### Dynamic Windows

Windows inside a `Window.Group` can be rendered dynamically from an array — for example via `.map()` over a piece of state. Layout presets and group actions wait until the registry is populated and the container is measured, so a layout triggered before all children have mounted is automatically deferred and flushed when everything is ready.

<Demo data={demos.dynamicWindows} />

### Z-index Management

Both `Window.Group` and stand-alone `Window` expose z-index configuration via three props:

- **`initialZIndex`** — starting value for the stacking context. Defaults to `200` when `withinPortal` is `true`, `1` otherwise.
- **`maxZIndex`** — upper bound. When the counter would exceed this value it wraps back to `initialZIndex`, preventing unbounded escalation above modals or menus.
- **`zIndexStrategy`** (`Window.Group` only) — how z-indexes are assigned when a window is focused or registered:
- `'increment'` (default): a counter monotonically increases. Preserves legacy behavior. Combine with `maxZIndex` to keep values bounded.
- `'normalize'`: z-indexes are derived from the stacking order and always stay within `initialZIndex .. initialZIndex + N - 1`. Recommended for long-running applications or when Mantine modals and menus are used alongside.

<Demo data={demos.zIndexStrategy} />

## Styles API

`Window` supports [Styles API](https://mantine.dev/styles/styles-api/), you can add styles to any inner element of the component with `classNames` prop. Follow [Styles API](https://mantine.dev/styles/styles-api/) documentation to learn more.
Expand Down
35 changes: 35 additions & 0 deletions docs/migrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@ This page collects the **breaking changes** and **migration steps** for each maj

New features introduced in a release (that do not break anything) are **not** listed here. See the [GitHub Releases](https://github.com/gfazioli/mantine-window/releases) page or the newsletter for the full changelog.

## v3.1

> **Behavioral changes (not API breaking):**
>
> v3.1 reworks how z-indexes are coordinated across windows. The public API is backwards compatible, but if your application relied on specific stacking behavior you may want to review the changes below.
>
> - **Stand-alone `Window` instances no longer share an unbounded z-index counter.** Before v3.1, every `bringToFront` on any stand-alone `Window` in the page incremented a module-level counter that would eventually exceed Mantine modals and menus. The counter is still shared across stand-alone windows (so activation order is preserved) but each `Window` can now bound it via `maxZIndex`, wrapping back to `initialZIndex` when the cap is reached.
> - **`Window.Group` stacking is now state-driven.** Internally, the group tracks a reactive `stackOrder` instead of mutating refs. This does not change observable behavior unless you were reading intermediate z-index values via internals.
>
> **Recommended migration** — if your app uses Mantine `Modal` or `Menu` alongside `Window`, opt into bounded z-indexes:
>
> ```tsx
> <Window.Group
> zIndexStrategy="normalize"
> initialZIndex={100}
> maxZIndex={199}
> >
> {/* windows */}
> </Window.Group>
> ```
>
> Or on a stand-alone `Window`:
>
> ```tsx
> <Window initialZIndex={100} maxZIndex={199} />
> ```

> **New features:**
>
> - **`zIndexStrategy`** on `Window.Group` — `'increment'` (default, legacy) or `'normalize'` for compact derived z-indexes
> - **`initialZIndex`** on `Window` and `Window.Group` — override the starting z-index
> - **`maxZIndex`** on `Window` and `Window.Group` — cap the counter to avoid escalation above modals
> - **`stackOrder`** exposed on `WindowGroupContextValue` — read-only snapshot of current stacking order
> - **Deferred layout application** — `applyLayout` on dynamically rendered windows (`.map()`) now correctly waits for registration and container measurement instead of silently no-oping

## v2

> **Breaking changes:**
Expand Down
16 changes: 8 additions & 8 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@gfazioli/mantine-window": "workspace:*",
"@mantine/code-highlight": "9.0.0",
"@mantine/core": "9.0.0",
"@mantine/hooks": "9.0.0",
"@mantine/code-highlight": "9.0.2",
"@mantine/core": "9.0.2",
"@mantine/hooks": "9.0.2",
"@mantinex/demo": "^2.0.0",
"@mantinex/dev-icons": "^2.0.0",
"@mantinex/mantine-header": "^2.0.0",
Expand All @@ -21,12 +21,12 @@
"@mantinex/shiki": "^1.1.0",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^15.5.14",
"@next/mdx": "^15.5.15",
"@tabler/icons-react": "^3.41.1",
"@types/mdx": "^2.0.13",
"next": "15.5.14",
"react": "19.2.4",
"react-dom": "19.2.4",
"next": "15.5.15",
"react": "19.2.5",
"react-dom": "19.2.5",
"remark-slug": "^7.0.1",
"shiki": "^3.23.0",
"type-fest": "^4.41.0"
Expand All @@ -35,6 +35,6 @@
"@types/node": "^22.19.17",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"typescript": "6.0.2"
"typescript": "6.0.3"
}
}
30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "m-90d2bb8",
"version": "0.1.1",
"description": "This package.json is not published to npm, modify the file in package/package.json",
"packageManager": "yarn@4.13.0",
"packageManager": "yarn@4.14.1",
"repository": "mantinedev/extension-template.git",
"workspaces": [
"docs",
Expand Down Expand Up @@ -32,13 +32,13 @@
},
"devDependencies": {
"@mantine-tests/core": "^2.1.0",
"@mantine/core": "9.0.0",
"@mantine/hooks": "9.0.0",
"@mantine/core": "9.0.2",
"@mantine/hooks": "9.0.2",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-replace": "^6.0.3",
"@storybook/addon-themes": "^10.3.4",
"@storybook/react": "^10.3.4",
"@storybook/react-vite": "^10.3.4",
"@storybook/addon-themes": "^10.3.5",
"@storybook/react": "^10.3.5",
"@storybook/react-vite": "^10.3.5",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
Expand All @@ -65,27 +65,27 @@
"new-github-release-url": "^2.0.0",
"open": "^10.2.0",
"oxfmt": "^0.42.0",
"oxlint": "^1.58.0",
"postcss": "^8.5.8",
"oxlint": "^1.61.0",
"postcss": "^8.5.10",
"postcss-preset-mantine": "1.18.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"react": "19.2.5",
"react-dom": "19.2.5",
"rimraf": "^6.1.3",
"rollup": "^4.60.1",
"rollup": "^4.60.2",
"rollup-plugin-banner2": "^1.3.1",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-node-externals": "^8.1.2",
"rollup-plugin-postcss": "^4.0.2",
"signale": "^1.4.0",
"simple-git": "^3.33.0",
"storybook": "^10.3.4",
"simple-git": "^3.36.0",
"storybook": "^10.3.5",
"stylelint": "^16.26.1",
"stylelint-config-standard-scss": "^14.0.0",
"syncpack": "^13.0.4",
"tsx": "^4.21.0",
"typescript": "6.0.2",
"typescript": "6.0.3",
"version-next": "^1.0.2",
"vite": "^6.4.1",
"vite": "^6.4.2",
"yargs": "^17.7.2",
"zx": "^8.8.5"
}
Expand Down
Loading
Loading