diff --git a/CHANGELOG.md b/CHANGELOG.md index c7c57ab6c..b253330fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- [646](https://github.com/bvaughn/react-resizable-panels/pull/646): Re-enable the collapsible `Panel` from 4.5.3 that was disabled in 4.5.6 + ## 4.5.6 - [644](https://github.com/bvaughn/react-resizable-panels/pull/644): Disabled the change to collapsible panel behavior that was originally made in [635](https://github.com/bvaughn/react-resizable-panels/pull/635). diff --git a/lib/global/utils/adjustLayoutByDelta.test.ts b/lib/global/utils/adjustLayoutByDelta.test.ts index 79557a3b5..5f1563598 100644 --- a/lib/global/utils/adjustLayoutByDelta.test.ts +++ b/lib/global/utils/adjustLayoutByDelta.test.ts @@ -1939,7 +1939,7 @@ describe("adjustLayoutByDelta", () => { // Edge case issues/210 and issues/629 describe("collapsible panel thresholds", () => { ["left", "right"].forEach((panelId) => { - describe(`${panelId} panel`, () => { + describe(`${panelId} panel with collapsedSize:0`, () => { const collapsiblePanelConstraints = { collapsedSize: 0, collapsible: true, @@ -1953,96 +1953,104 @@ describe("adjustLayoutByDelta", () => { ? c([collapsiblePanelConstraints, {}]) : c([{}, collapsiblePanelConstraints]); - test.each([ - [ - "remain open if delta is less than minimum threshold", - { - initialLayout: open, - prevLayout: open, + test("remain open if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -4 : 4, - expectedLayout: open - } - ], - [ - "close if delta is greater than minimum threshold", - { initialLayout: open, + panelConstraints, prevLayout: open, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("close if delta is greater than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -6 : 6, - expectedLayout: closed - } - ], - [ - "re-open if delta is less than minimum threshold", - { initialLayout: open, - prevLayout: closed, + panelConstraints, + prevLayout: open, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("re-open if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -4 : 4, - expectedLayout: open - } - ], - [ - "remain closed if delta is more than minimum threshold", - { initialLayout: open, + panelConstraints, prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("remain closed if delta is more than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -6 : 6, - expectedLayout: closed - } - ], - [ - "remain closed if delta is less than minimum threshold", - { + initialLayout: open, + panelConstraints, + prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("remain closed if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ + delta: panelId === "left" ? 4 : -4, initialLayout: closed, + panelConstraints, prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("open if delta is greater than minimum threshold", () => { + expect( + adjustLayoutByDelta({ + delta: panelId === "left" ? 6 : -6, + initialLayout: closed, + panelConstraints, + prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("close if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? 4 : -4, - expectedLayout: closed - } - ], - // TODO Re-enable if/when this behavior change is re-enabled - // [ - // "open if delta is greater than minimum threshold", - // { - // initialLayout: closed, - // prevLayout: closed, - // delta: panelId === "left" ? 6 : -6, - // expectedLayout: open - // } - // ], - // [ - // "close if delta is less than minimum threshold", - // { - // initialLayout: closed, - // prevLayout: open, - // delta: panelId === "left" ? 4 : -4, - // expectedLayout: closed - // } - // ], - [ - "remain open if delta is more than minimum threshold", - { initialLayout: closed, + panelConstraints, prevLayout: open, - delta: panelId === "left" ? 6 : -6, - expectedLayout: open - } - ] - ])("%s", (_, { initialLayout, prevLayout, delta, expectedLayout }) => { + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("remain open if delta is more than minimum threshold", () => { expect( adjustLayoutByDelta({ - delta, - initialLayout, + delta: panelId === "left" ? 6 : -6, + initialLayout: closed, panelConstraints, - prevLayout, + prevLayout: open, trigger: "mouse-or-touch" }) - ).toEqual(expectedLayout); + ).toEqual(open); }); }); - }); - ["left", "right"].forEach((panelId) => { - describe(`${panelId} panel`, () => { + describe(`${panelId} panel with collapsedSize:5`, () => { const collapsiblePanelConstraints = { collapsedSize: 5, collapsible: true, @@ -2056,90 +2064,100 @@ describe("adjustLayoutByDelta", () => { ? c([collapsiblePanelConstraints, {}]) : c([{}, collapsiblePanelConstraints]); - test.each([ - [ - "remain open if delta is less than minimum threshold", - { - initialLayout: open, - prevLayout: open, + test("remain open if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -4 : 4, - expectedLayout: open - } - ], - [ - "close if delta is greater than minimum threshold", - { initialLayout: open, + panelConstraints, prevLayout: open, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("close if delta is greater than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -6 : 6, - expectedLayout: closed - } - ], - [ - "re-open if delta is less than minimum threshold", - { initialLayout: open, - prevLayout: closed, + panelConstraints, + prevLayout: open, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("re-open if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -4 : 4, - expectedLayout: open - } - ], - [ - "remain closed if delta is more than minimum threshold", - { initialLayout: open, + panelConstraints, prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("remain closed if delta is more than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? -6 : 6, - expectedLayout: closed - } - ], - [ - "remain closed if delta is less than minimum threshold", - { + initialLayout: open, + panelConstraints, + prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("remain closed if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ + delta: panelId === "left" ? 4 : -4, + initialLayout: closed, + panelConstraints, + prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("open if delta is greater than minimum threshold", () => { + expect( + adjustLayoutByDelta({ + delta: panelId === "left" ? 6 : -6, initialLayout: closed, + panelConstraints, prevLayout: closed, + trigger: "mouse-or-touch" + }) + ).toEqual(open); + }); + + test("close if delta is less than minimum threshold", () => { + expect( + adjustLayoutByDelta({ delta: panelId === "left" ? 4 : -4, - expectedLayout: closed - } - ], - // TODO Re-enable if/when this behavior change is re-enabled - // [ - // "open if delta is greater than minimum threshold", - // { - // initialLayout: closed, - // prevLayout: closed, - // delta: panelId === "left" ? 6 : -6, - // expectedLayout: open - // } - // ], - // [ - // "close if delta is less than minimum threshold", - // { - // initialLayout: closed, - // prevLayout: open, - // delta: panelId === "left" ? 4 : -4, - // expectedLayout: closed - // } - // ], - [ - "remain open if delta is more than minimum threshold", - { initialLayout: closed, + panelConstraints, prevLayout: open, - delta: panelId === "left" ? 6 : -6, - expectedLayout: open - } - ] - ])("%s", (_, { initialLayout, prevLayout, delta, expectedLayout }) => { + trigger: "mouse-or-touch" + }) + ).toEqual(closed); + }); + + test("remain open if delta is more than minimum threshold", () => { expect( adjustLayoutByDelta({ - delta, - initialLayout, + delta: panelId === "left" ? 6 : -6, + initialLayout: closed, panelConstraints, - prevLayout, + prevLayout: open, trigger: "mouse-or-touch" }) - ).toEqual(expectedLayout); + ).toEqual(open); }); }); }); @@ -2151,54 +2169,136 @@ describe("adjustLayoutByDelta", () => { minSize: 15 }; - test.each([ - [ - "expand left when there are multiple panels", - { + test("expand left when there are multiple panels", () => { + expect( + adjustLayoutByDelta({ delta: -70, initialLayout: l([25, 50, 25]), panelConstraints: c([{}, {}, collapsibleConstraints]), prevLayout: l([25, 50, 25]), - expectedLayout: l([5, 0, 95]), - pivotIndices: [1, 2] - } - ], - [ - "expand right when there are multiple panels", - { + pivotIndices: [1, 2], + trigger: "mouse-or-touch" + }) + ).toEqual(l([5, 0, 95])); + }); + + test("expand right when there are multiple panels", () => { + expect( + adjustLayoutByDelta({ delta: 70, initialLayout: l([25, 50, 25]), panelConstraints: c([collapsibleConstraints, {}, {}]), prevLayout: l([25, 50, 25]), - expectedLayout: l([95, 0, 5]), - pivotIndices: [0, 1] - } - ] - ])( - "%s", + pivotIndices: [0, 1], + trigger: "mouse-or-touch" + }) + ).toEqual(l([95, 0, 5])); + }); + + test("edge case issues/639", () => { ( - _, - { - delta, - initialLayout, - panelConstraints, - prevLayout, - pivotIndices, - expectedLayout - } - ) => { + [ + [-10, l([20, 40, 40])], + [-20, l([20, 30, 50])], + [-30, l([20, 20, 60])], + [-40, l([10, 20, 70])], + [-50, l([0, 20, 80])] + ] satisfies [number, Layout][] + ).forEach(([delta, expectedLayout]) => { expect( adjustLayoutByDelta({ delta, - initialLayout, - panelConstraints, - pivotIndices, - prevLayout, + initialLayout: l([20, 50, 30]), + panelConstraints: c([ + { + collapsedSize: 0, + collapsible: true, + defaultSize: 20, + minSize: 10 + }, + { + defaultSize: 50, + minSize: 20 + }, + { + collapsedSize: 0, + collapsible: true, + defaultSize: 30, + minSize: 10 + } + ]), + prevLayout: l([20, 50, 30]), + pivotIndices: [1, 2], + trigger: "mouse-or-touch" + }) + ).toEqual(expectedLayout); + }); + }); + + test("edge case discussions/643", () => { + ( + [ + [4, l([10, 90])], + [6, l([20, 80])], + [10, l([20, 80])], + [15, l([25, 75])], + [25, l([35, 65])], + [40, l([50, 50])], + [50, l([50, 50])] + ] satisfies [number, Layout][] + ).forEach(([delta, expectedLayout]) => { + expect( + adjustLayoutByDelta({ + delta, + initialLayout: l([10, 90]), + panelConstraints: c([ + { + collapsedSize: 10, + collapsible: true, + defaultSize: 10, + maxSize: 50, + minSize: 20 + }, + {} + ]), + prevLayout: l([10, 90]), + trigger: "mouse-or-touch" + }) + ).toEqual(expectedLayout); + }); + + // 2nd panel variation of the above + ( + [ + [-4, l([90, 10])], + [-6, l([80, 20])], + [-10, l([80, 20])], + [-15, l([75, 25])], + [-25, l([65, 35])], + [-40, l([50, 50])], + [-50, l([50, 50])] + ] satisfies [number, Layout][] + ).forEach(([delta, expectedLayout]) => { + expect( + adjustLayoutByDelta({ + delta, + initialLayout: l([90, 10]), + panelConstraints: c([ + {}, + { + collapsedSize: 10, + collapsible: true, + defaultSize: 10, + maxSize: 50, + minSize: 20 + } + ]), + prevLayout: l([10, 90]), trigger: "mouse-or-touch" }) ).toEqual(expectedLayout); - } - ); + }); + }); }); }); }); diff --git a/lib/global/utils/adjustLayoutByDelta.ts b/lib/global/utils/adjustLayoutByDelta.ts index d07e764f3..8f9a8010b 100644 --- a/lib/global/utils/adjustLayoutByDelta.ts +++ b/lib/global/utils/adjustLayoutByDelta.ts @@ -131,69 +131,57 @@ export function adjustLayoutByDelta({ } break; } - // TODO Re-enable this once the Firefox behavior has been corrected - // See github.com/bvaughn/react-resizable-panels/discussions/643 - // default: { - // // If we're starting from a collapsed state, dragging past the halfway point should cause the panel to expand - // // This can happen for positive or negative drags, and panels on either side of the separator can be collapsible - // // The easiest way to support this is to detect this scenario and pre-adjust the delta before applying the rest of the layout algorithm - // // DEBUG.push(`edge case check 3: collapsible panels`); - - // const index = delta < 0 ? secondPivotIndex : firstPivotIndex; - // const panelConstraints = panelConstraintsArray[index]; - // assert( - // panelConstraints, - // `Panel constraints not found for index ${index}` - // ); - - // const { collapsible, collapsedSize, minSize } = panelConstraints; - // if (collapsible) { - // const isSecondPanel = index === secondPivotIndex; - - // // DEBUG.push(` -> collapsible ${isSecondPanel ? "2nd" : "1st"} panel`); - // if (delta > 0) { - // const gapSize = minSize - collapsedSize; - // const halfwayPoint = gapSize / 2; - // // DEBUG.push(` -> halfway point: ${halfwayPoint}`); - // // DEBUG.push(` -> between collapsed: ${collapsedSize}`); - // // DEBUG.push(` -> and min: ${minSize}`); - - // if (compareLayoutNumbers(delta, minSize) < 0) { - // // DEBUG.push(` -> adjusting delta from: ${delta}`); - // delta = - // compareLayoutNumbers(delta, halfwayPoint) <= 0 ? 0 : gapSize; - // // DEBUG.push(` -> adjusting delta to: ${delta}`); - // } - // } else { - // const gapSize = minSize - collapsedSize; - // const halfwayPoint = 100 - gapSize / 2; - // // DEBUG.push(` -> halfway point: ${halfwayPoint}`); - // // DEBUG.push(` -> between collapsed: ${100 - collapsedSize}`); - // // DEBUG.push(` -> and min: ${100 - minSize}`); - - // if (isSecondPanel) { - // if (compareLayoutNumbers(Math.abs(delta), minSize) < 0) { - // // DEBUG.push(` -> adjusting delta from: ${delta}`); - // delta = - // compareLayoutNumbers(100 + delta, halfwayPoint) > 0 - // ? 0 - // : -gapSize; - // // DEBUG.push(` -> adjusting delta to: ${delta}`); - // } - // } else { - // if (compareLayoutNumbers(100 + delta, minSize) < 0) { - // // DEBUG.push(` -> adjusting delta from: ${delta}`); - // delta = - // compareLayoutNumbers(100 + delta, halfwayPoint) > 0 - // ? 0 - // : -gapSize; - // // DEBUG.push(` -> adjusting delta to: ${delta}`); - // } - // } - // } - // } - // break; - // } + default: { + // If we're starting from a collapsed state, dragging past the halfway point should cause the panel to expand + // This can happen for positive or negative drags, and panels on either side of the separator can be collapsible + // The easiest way to support this is to detect this scenario and pre-adjust the delta before applying the rest of the layout algorithm + // DEBUG.push(`edge case check 3: collapsible panels`); + + const index = delta < 0 ? secondPivotIndex : firstPivotIndex; + const panelConstraints = panelConstraintsArray[index]; + assert( + panelConstraints, + `Panel constraints not found for index ${index}` + ); + + const { collapsible, collapsedSize, minSize } = panelConstraints; + if (collapsible) { + // DEBUG.push(` -> collapsible ${isSecondPanel ? "2nd" : "1st"} panel`); + if (delta > 0) { + const gapSize = minSize - collapsedSize; + const halfwayPoint = gapSize / 2; + // DEBUG.push(` -> halfway point: ${halfwayPoint}`); + // DEBUG.push(` between collapsed: ${collapsedSize}`); + // DEBUG.push(` and min: ${minSize}`); + + if (compareLayoutNumbers(delta, gapSize) < 0) { + // DEBUG.push(" -> adjusting delta"); + // DEBUG.push(` from: ${delta}`); + delta = + compareLayoutNumbers(delta, halfwayPoint) <= 0 ? 0 : gapSize; + // DEBUG.push(` to: ${delta}`); + } + } else { + const gapSize = minSize - collapsedSize; + const halfwayPoint = 100 - gapSize / 2; + // DEBUG.push(` -> halfway point: ${halfwayPoint}`); + // DEBUG.push(` between collapsed: ${100 - collapsedSize}`); + // DEBUG.push(` and min: ${100 - minSize}`); + + //if (isSecondPanel) { + if (compareLayoutNumbers(Math.abs(delta), gapSize) < 0) { + // DEBUG.push(" -> adjusting delta"); + // DEBUG.push(` from: ${delta}`); + delta = + compareLayoutNumbers(100 + delta, halfwayPoint) > 0 + ? 0 + : -gapSize; + // DEBUG.push(` to: ${delta}`); + } + } + } + break; + } } // DEBUG.push(""); }