From 3dd3b17b707e826bb027ad99374604e813020639 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 09:33:27 -0500 Subject: [PATCH 1/6] Bugfix: Panel heights within vertical groups (#622) This regressed in PR #583 with the introduction of `height: 100%`. If `min-height` was used to set the Group's height (rather than an `!important` height override) then Panels would not fully fill the available space in Safari. This [Stack Overflow comment](https://stackoverflow.com/questions/8468066/child-inside-parent-with-min-height-100-not-inheriting-height) explains the reason why: > An element grows in height as much as required by its inner content. In such a situation, if the child also says I should have 100% height of my parent (through height:100%), it becomes a deadlock - parent height dependent on child, child height dependent on parent's. This deadlock cannot be resolved by the browser and so height:100% simply doesn't works...unless the parent as a resolvable height - one way to do which is by giving an explicit height to parent This PR fixes that by essentially switching the inner Panel element to use `flex-grow: 1` rather than `height: 100%` Resolves #619 --- CHANGELOG.md | 1 + lib/components/panel/Panel.tsx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8de0a5785..1b40e5651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [616](https://github.com/bvaughn/react-resizable-panels/pull/616): Replace `Separator` and `Panel` edge hit-area padding with a minimum size threshold based on [Apple's user interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility). Separators that are large enough will no longer be padded; separators that are too small (or panels without separators) will more or less function like before. This should not have much of a user-facing impact other than an increase in the click target area. (Previously I was not padding enough, as per Apple's guidelines.) - [615](https://github.com/bvaughn/react-resizable-panels/pull/615): Double-clicking on a `Separator` resets its associated `Panel` to its default-size (see video below); double-click will have no impact on panels without default sizes +- [622](https://github.com/bvaughn/react-resizable-panels/pull/622): Bugfix: Panels within vertical groups are now properly sized in Safari - [618](https://github.com/bvaughn/react-resizable-panels/pull/618): Bugfix: Don't override `adoptedStyleSheets` https://github.com/user-attachments/assets/f19f6c5e-d290-455e-9bad-20e5038c3508 diff --git a/lib/components/panel/Panel.tsx b/lib/components/panel/Panel.tsx index e5d4b9dbc..8814e39d8 100644 --- a/lib/components/panel/Panel.tsx +++ b/lib/components/panel/Panel.tsx @@ -124,6 +124,7 @@ export function Panel({ style={{ ...PROHIBITED_CSS_PROPERTIES, + display: "flex", flexBasis: 0, flexShrink: 1, @@ -136,8 +137,8 @@ export function Panel({
From ae4f0f508f6d96a2f97b18548b33fa4f9a7a0229 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 09:37:10 -0500 Subject: [PATCH 2/6] CHANGELOG tweak --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b40e5651..9fd8da930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ - [616](https://github.com/bvaughn/react-resizable-panels/pull/616): Replace `Separator` and `Panel` edge hit-area padding with a minimum size threshold based on [Apple's user interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility). Separators that are large enough will no longer be padded; separators that are too small (or panels without separators) will more or less function like before. This should not have much of a user-facing impact other than an increase in the click target area. (Previously I was not padding enough, as per Apple's guidelines.) - [615](https://github.com/bvaughn/react-resizable-panels/pull/615): Double-clicking on a `Separator` resets its associated `Panel` to its default-size (see video below); double-click will have no impact on panels without default sizes -- [622](https://github.com/bvaughn/react-resizable-panels/pull/622): Bugfix: Panels within vertical groups are now properly sized in Safari -- [618](https://github.com/bvaughn/react-resizable-panels/pull/618): Bugfix: Don't override `adoptedStyleSheets` +- [622](https://github.com/bvaughn/react-resizable-panels/pull/622): **Bugfix**: Panels within vertical groups are now properly sized in Safari +- [618](https://github.com/bvaughn/react-resizable-panels/pull/618): **Bugfix**: Don't override `adoptedStyleSheets` + +Demo of double-clicking on a separator: https://github.com/user-attachments/assets/f19f6c5e-d290-455e-9bad-20e5038c3508 From afa9982ad0d6d88be31064349ee967726f92d1a6 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 09:47:01 -0500 Subject: [PATCH 3/6] Double-click resets secondary Panel to default size if primary Panel does not specify a default (#623) Resolves #620 --- CHANGELOG.md | 2 +- .../tests/tests/pointer-interactions.spec.tsx | 30 ++++++++++++++++++- .../event-handlers/onDocumentDoubleClick.ts | 13 ++++---- src/routes/CustomStylesRoute.tsx | 2 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd8da930..ffd4fc3cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased - [616](https://github.com/bvaughn/react-resizable-panels/pull/616): Replace `Separator` and `Panel` edge hit-area padding with a minimum size threshold based on [Apple's user interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility). Separators that are large enough will no longer be padded; separators that are too small (or panels without separators) will more or less function like before. This should not have much of a user-facing impact other than an increase in the click target area. (Previously I was not padding enough, as per Apple's guidelines.) -- [615](https://github.com/bvaughn/react-resizable-panels/pull/615): Double-clicking on a `Separator` resets its associated `Panel` to its default-size (see video below); double-click will have no impact on panels without default sizes +- [615](https://github.com/bvaughn/react-resizable-panels/pull/615), [620](https://github.com/bvaughn/react-resizable-panels/pull/620): Double-clicking on a `Separator` resets its associated `Panel` to its default-size (see video below); double-click will have no impact on panels without default sizes - [622](https://github.com/bvaughn/react-resizable-panels/pull/622): **Bugfix**: Panels within vertical groups are now properly sized in Safari - [618](https://github.com/bvaughn/react-resizable-panels/pull/618): **Bugfix**: Don't override `adoptedStyleSheets` diff --git a/integrations/tests/tests/pointer-interactions.spec.tsx b/integrations/tests/tests/pointer-interactions.spec.tsx index f33129c07..ed8c7c678 100644 --- a/integrations/tests/tests/pointer-interactions.spec.tsx +++ b/integrations/tests/tests/pointer-interactions.spec.tsx @@ -366,7 +366,7 @@ test.describe("pointer interactions", () => { await expect(mainPage.getByText('"right": 33')).toBeVisible(); }); - test("double-clicking a separator resets panel to default size", async ({ + test("double-clicking a separator resets primary panel to default size", async ({ page: mainPage }) => { const page = await goToUrl( @@ -394,6 +394,34 @@ test.describe("pointer interactions", () => { await expect(mainPage.getByText('"left": 30')).toBeVisible(); }); + test("double-clicking a separator resets secondary panel to default size", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + , + { usePopUpWindow } + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 40')).toBeVisible(); + + const separator = page.getByTestId("separator"); + await separator.dblclick(); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); + test.describe("focus", () => { test("should update focus to the nearest separator", async ({ page: mainPage diff --git a/lib/global/event-handlers/onDocumentDoubleClick.ts b/lib/global/event-handlers/onDocumentDoubleClick.ts index ea590aee3..744fc682d 100644 --- a/lib/global/event-handlers/onDocumentDoubleClick.ts +++ b/lib/global/event-handlers/onDocumentDoubleClick.ts @@ -23,14 +23,17 @@ export function onDocumentDoubleClick(event: MouseEvent) { }); if (current.separator) { - const primaryPanel = current.panels[0]; - if (primaryPanel.panelConstraints.defaultSize !== undefined) { + const panelWithDefaultSize = current.panels.find( + (panel) => panel.panelConstraints.defaultSize !== undefined + ); + if (panelWithDefaultSize) { + const defaultSize = panelWithDefaultSize.panelConstraints.defaultSize; const api = getImperativePanelMethods({ groupId: current.group.id, - panelId: primaryPanel.id + panelId: panelWithDefaultSize.id }); - if (api) { - api.resize(primaryPanel.panelConstraints.defaultSize); + if (api && defaultSize !== undefined) { + api.resize(defaultSize); event.preventDefault(); } diff --git a/src/routes/CustomStylesRoute.tsx b/src/routes/CustomStylesRoute.tsx index f1556e9d7..70988e427 100644 --- a/src/routes/CustomStylesRoute.tsx +++ b/src/routes/CustomStylesRoute.tsx @@ -36,7 +36,7 @@ export default function CustomStylesRoute() { left - right + right ); From fb047a8f827f0cc3d746cebc17da09e2534ee3fb Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 09:47:32 -0500 Subject: [PATCH 4/6] 4.4.2 -> 4.5.0 --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffd4fc3cf..34a99a39c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 4.5.0 - [616](https://github.com/bvaughn/react-resizable-panels/pull/616): Replace `Separator` and `Panel` edge hit-area padding with a minimum size threshold based on [Apple's user interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility). Separators that are large enough will no longer be padded; separators that are too small (or panels without separators) will more or less function like before. This should not have much of a user-facing impact other than an increase in the click target area. (Previously I was not padding enough, as per Apple's guidelines.) - [615](https://github.com/bvaughn/react-resizable-panels/pull/615), [620](https://github.com/bvaughn/react-resizable-panels/pull/620): Double-clicking on a `Separator` resets its associated `Panel` to its default-size (see video below); double-click will have no impact on panels without default sizes diff --git a/package.json b/package.json index 4af327781..c3f89c2a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-resizable-panels", - "version": "4.4.2", + "version": "4.5.0", "type": "module", "author": "Brian Vaughn (https://github.com/bvaughn/)", "contributors": [ From 23538a3da89ce6f06410f19cbad17395c6b8f5d4 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 11:32:32 -0500 Subject: [PATCH 5/6] Fix Safari cursor styles (#624) Resolves #621 --- CHANGELOG.md | 10 + lib/global/cursor/getCursorStyle.test.ts | 348 ++++++++++++------ lib/global/cursor/getCursorStyle.ts | 71 ++-- .../cursor/supportsAdvancedCursorStyles.ts | 27 ++ lib/global/cursor/updateCursorStyle.ts | 2 +- 5 files changed, 324 insertions(+), 134 deletions(-) create mode 100644 lib/global/cursor/supportsAdvancedCursorStyles.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a99a39c..5f502b7ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 4.5.1 + +- [624](https://github.com/bvaughn/react-resizable-panels/pull/624): **Bugfix**: Fallback to alternate CSS cursor styles for Safari + +| Safari | Chrome, Firefox +| :--- | :--- +| `grab` | `move` +| `col-resize` | `ew-resize` +| `row-resize` | `ns-resize` + ## 4.5.0 - [616](https://github.com/bvaughn/react-resizable-panels/pull/616): Replace `Separator` and `Panel` edge hit-area padding with a minimum size threshold based on [Apple's user interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility). Separators that are large enough will no longer be padded; separators that are too small (or panels without separators) will more or less function like before. This should not have much of a user-facing impact other than an increase in the click target area. (Previously I was not padding enough, as per Apple's guidelines.) diff --git a/lib/global/cursor/getCursorStyle.test.ts b/lib/global/cursor/getCursorStyle.test.ts index 565e0204d..5a69e36d0 100644 --- a/lib/global/cursor/getCursorStyle.test.ts +++ b/lib/global/cursor/getCursorStyle.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from "vitest"; +import { beforeEach, describe, expect, test } from "vitest"; import { CURSOR_FLAG_HORIZONTAL_MAX, CURSOR_FLAG_HORIZONTAL_MIN, @@ -7,6 +7,7 @@ import { } from "../../constants"; import { mockGroup } from "../test/mockGroup"; import { getCursorStyle } from "./getCursorStyle"; +import { overrideSupportsAdvancedCursorStylesForTesting } from "./supportsAdvancedCursorStyles"; describe("getCursorStyle", () => { const horizontalGroup = mockGroup(new DOMRect(0, 0, 100, 50)); @@ -24,128 +25,265 @@ describe("getCursorStyle", () => { disabledGroup.addPanel(new DOMRect(0, 0, 50, 50)); disabledGroup.addPanel(new DOMRect(50, 0, 50, 50)); - describe("state: inactive", () => { - test("should return null", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [horizontalGroup, verticalGroup], - state: "inactive" - }) - ).toBeNull(); + describe("advanced cursor style support", () => { + beforeEach(() => { + overrideSupportsAdvancedCursorStylesForTesting(true); }); - }); - describe("state: hover", () => { - test("horizontal", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [horizontalGroup], - state: "hover" - }) - ).toBe("ew-resize"); + describe("state: inactive", () => { + test("should return null", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "inactive" + }) + ).toBeNull(); + }); }); - test("vertical", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [verticalGroup], - state: "hover" - }) - ).toBe("ns-resize"); - }); + describe("state: hover", () => { + test("horizontal", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup], + state: "hover" + }) + ).toBe("ew-resize"); + }); - test("horizontal and vertical", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [horizontalGroup, verticalGroup], - state: "hover" - }) - ).toBe("move"); - }); + test("vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [verticalGroup], + state: "hover" + }) + ).toBe("ns-resize"); + }); + + test("horizontal and vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "hover" + }) + ).toBe("move"); + }); + + test("cursor flags should be ignored", () => { + expect( + getCursorStyle({ + cursorFlags: CURSOR_FLAG_HORIZONTAL_MAX, + groups: [horizontalGroup], + state: "hover" + }) + ).toBe("ew-resize"); + }); - test("cursor flags should be ignored", () => { - expect( - getCursorStyle({ - cursorFlags: CURSOR_FLAG_HORIZONTAL_MAX, - groups: [horizontalGroup], - state: "hover" - }) - ).toBe("ew-resize"); + test("disabled groups", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [disabledGroup], + state: "hover" + }) + ).toBeNull(); + }); }); - test("disabled groups", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [disabledGroup], - state: "hover" - }) - ).toBeNull(); + describe("state: active", () => { + test("horizontal", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup], + state: "active" + }) + ).toBe("ew-resize"); + }); + + test("vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [verticalGroup], + state: "active" + }) + ).toBe("ns-resize"); + }); + + test("horizontal and vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "active" + }) + ).toBe("move"); + }); + + test.each([ + [CURSOR_FLAG_HORIZONTAL_MIN, "e-resize"], + [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MIN, "se-resize"], + [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MAX, "ne-resize"], + [CURSOR_FLAG_HORIZONTAL_MAX, "w-resize"], + [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MIN, "sw-resize"], + [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MAX, "nw-resize"], + [CURSOR_FLAG_VERTICAL_MIN, "s-resize"], + [CURSOR_FLAG_VERTICAL_MAX, "n-resize"] + ])("cursor flags: %i -> %s", (cursorFlags, expected) => { + expect( + getCursorStyle({ + cursorFlags, + groups: [horizontalGroup, verticalGroup], + state: "active" + }) + ).toBe(expected); + }); + + test("disabled groups", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [disabledGroup], + state: "active" + }) + ).toBeNull(); + }); }); }); - describe("state: active", () => { - test("horizontal", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [horizontalGroup], - state: "active" - }) - ).toBe("ew-resize"); + describe("basic cursor style support", () => { + beforeEach(() => { + overrideSupportsAdvancedCursorStylesForTesting(false); }); - test("vertical", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [verticalGroup], - state: "active" - }) - ).toBe("ns-resize"); + describe("state: inactive", () => { + test("should return null", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "inactive" + }) + ).toBeNull(); + }); }); - test("horizontal and vertical", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [horizontalGroup, verticalGroup], - state: "active" - }) - ).toBe("move"); - }); + describe("state: hover", () => { + test("horizontal", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup], + state: "hover" + }) + ).toBe("col-resize"); + }); + + test("vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [verticalGroup], + state: "hover" + }) + ).toBe("row-resize"); + }); + + test("horizontal and vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "hover" + }) + ).toBe("grab"); + }); + + test("cursor flags should be ignored", () => { + expect( + getCursorStyle({ + cursorFlags: CURSOR_FLAG_HORIZONTAL_MAX, + groups: [horizontalGroup], + state: "hover" + }) + ).toBe("col-resize"); + }); - test.each([ - [CURSOR_FLAG_HORIZONTAL_MIN, "e-resize"], - [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MIN, "se-resize"], - [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MAX, "ne-resize"], - [CURSOR_FLAG_HORIZONTAL_MAX, "w-resize"], - [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MIN, "sw-resize"], - [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MAX, "nw-resize"], - [CURSOR_FLAG_VERTICAL_MIN, "s-resize"], - [CURSOR_FLAG_VERTICAL_MAX, "n-resize"] - ])("cursor flags: %i -> %s", (cursorFlags, expected) => { - expect( - getCursorStyle({ - cursorFlags, - groups: [horizontalGroup, verticalGroup], - state: "active" - }) - ).toBe(expected); + test("disabled groups", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [disabledGroup], + state: "hover" + }) + ).toBeNull(); + }); }); - test("disabled groups", () => { - expect( - getCursorStyle({ - cursorFlags: 0, - groups: [disabledGroup], - state: "active" - }) - ).toBeNull(); + describe("state: active", () => { + test("horizontal", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup], + state: "active" + }) + ).toBe("col-resize"); + }); + + test("vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [verticalGroup], + state: "active" + }) + ).toBe("row-resize"); + }); + + test("horizontal and vertical", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [horizontalGroup, verticalGroup], + state: "active" + }) + ).toBe("grab"); + }); + + test.each([ + [CURSOR_FLAG_HORIZONTAL_MIN, "grab"], + [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MIN, "grab"], + [CURSOR_FLAG_HORIZONTAL_MIN | CURSOR_FLAG_VERTICAL_MAX, "grab"], + [CURSOR_FLAG_HORIZONTAL_MAX, "grab"], + [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MIN, "grab"], + [CURSOR_FLAG_HORIZONTAL_MAX | CURSOR_FLAG_VERTICAL_MAX, "grab"], + [CURSOR_FLAG_VERTICAL_MIN, "grab"], + [CURSOR_FLAG_VERTICAL_MAX, "grab"] + ])("cursor flags: %i -> %s", (cursorFlags, expected) => { + expect( + getCursorStyle({ + cursorFlags, + groups: [horizontalGroup, verticalGroup], + state: "active" + }) + ).toBe(expected); + }); + + test("disabled groups", () => { + expect( + getCursorStyle({ + cursorFlags: 0, + groups: [disabledGroup], + state: "active" + }) + ).toBeNull(); + }); }); }); }); diff --git a/lib/global/cursor/getCursorStyle.ts b/lib/global/cursor/getCursorStyle.ts index f7db51169..d7ecfce23 100644 --- a/lib/global/cursor/getCursorStyle.ts +++ b/lib/global/cursor/getCursorStyle.ts @@ -7,6 +7,7 @@ import { CURSOR_FLAG_VERTICAL_MIN } from "../../constants"; import type { InteractionState } from "../types"; +import { supportsAdvancedCursorStyles } from "./supportsAdvancedCursorStyles"; export function getCursorStyle({ cursorFlags, @@ -48,43 +49,57 @@ export function getCursorStyle({ switch (state) { case "active": { - const horizontalMin = (cursorFlags & CURSOR_FLAG_HORIZONTAL_MIN) !== 0; - const horizontalMax = (cursorFlags & CURSOR_FLAG_HORIZONTAL_MAX) !== 0; - const verticalMin = (cursorFlags & CURSOR_FLAG_VERTICAL_MIN) !== 0; - const verticalMax = (cursorFlags & CURSOR_FLAG_VERTICAL_MAX) !== 0; - if (cursorFlags) { - if (horizontalMin) { - if (verticalMin) { - return "se-resize"; - } else if (verticalMax) { - return "ne-resize"; - } else { - return "e-resize"; - } - } else if (horizontalMax) { - if (verticalMin) { - return "sw-resize"; + if (supportsAdvancedCursorStyles()) { + const horizontalMin = + (cursorFlags & CURSOR_FLAG_HORIZONTAL_MIN) !== 0; + const horizontalMax = + (cursorFlags & CURSOR_FLAG_HORIZONTAL_MAX) !== 0; + const verticalMin = (cursorFlags & CURSOR_FLAG_VERTICAL_MIN) !== 0; + const verticalMax = (cursorFlags & CURSOR_FLAG_VERTICAL_MAX) !== 0; + + if (horizontalMin) { + if (verticalMin) { + return "se-resize"; + } else if (verticalMax) { + return "ne-resize"; + } else { + return "e-resize"; + } + } else if (horizontalMax) { + if (verticalMin) { + return "sw-resize"; + } else if (verticalMax) { + return "nw-resize"; + } else { + return "w-resize"; + } + } else if (verticalMin) { + return "s-resize"; } else if (verticalMax) { - return "nw-resize"; - } else { - return "w-resize"; + return "n-resize"; } - } else if (verticalMin) { - return "s-resize"; - } else if (verticalMax) { - return "n-resize"; } } break; } } - if (horizontalCount > 0 && verticalCount > 0) { - return "move"; - } else if (horizontalCount > 0) { - return "ew-resize"; + if (supportsAdvancedCursorStyles()) { + if (horizontalCount > 0 && verticalCount > 0) { + return "move"; + } else if (horizontalCount > 0) { + return "ew-resize"; + } else { + return "ns-resize"; + } } else { - return "ns-resize"; + if (horizontalCount > 0 && verticalCount > 0) { + return "grab"; + } else if (horizontalCount > 0) { + return "col-resize"; + } else { + return "row-resize"; + } } } diff --git a/lib/global/cursor/supportsAdvancedCursorStyles.ts b/lib/global/cursor/supportsAdvancedCursorStyles.ts new file mode 100644 index 000000000..09c779e42 --- /dev/null +++ b/lib/global/cursor/supportsAdvancedCursorStyles.ts @@ -0,0 +1,27 @@ +let cached: boolean | undefined = undefined; + +export function overrideSupportsAdvancedCursorStylesForTesting( + override: boolean +) { + cached = override; +} + +/** + * Caches and returns if advanced cursor CSS styles are supported. + */ +export function supportsAdvancedCursorStyles(): boolean { + if (cached === undefined) { + cached = false; + + if (typeof window !== "undefined") { + if ( + window.navigator.userAgent.includes("Chrome") || + window.navigator.userAgent.includes("Firefox") + ) { + cached = true; + } + } + } + + return cached; +} diff --git a/lib/global/cursor/updateCursorStyle.ts b/lib/global/cursor/updateCursorStyle.ts index 84c8fc7d8..0c9671b13 100644 --- a/lib/global/cursor/updateCursorStyle.ts +++ b/lib/global/cursor/updateCursorStyle.ts @@ -38,7 +38,7 @@ export function updateCursorStyle(ownerDocument: Document) { state: interactionState.state }); - const nextStyle = `*{cursor: ${cursorStyle} !important; ${interactionState.state === "active" ? "touch-action: none;" : ""} }`; + const nextStyle = `*, *:hover {cursor: ${cursorStyle} !important; ${interactionState.state === "active" ? "touch-action: none;" : ""} }`; if (prevStyle === nextStyle) { return; } From d1d2ddf667ff392c69f8a91963e81937a6a0e30d Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 24 Jan 2026 11:32:58 -0500 Subject: [PATCH 6/6] 4.5.0 -> 4.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c3f89c2a8..a7e4295b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-resizable-panels", - "version": "4.5.0", + "version": "4.5.1", "type": "module", "author": "Brian Vaughn (https://github.com/bvaughn/)", "contributors": [