From 43486a6c119a3ea0047b4919bcd330f4a14685b3 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 3 Feb 2026 17:24:24 +0100 Subject: [PATCH] Various fixes to ensure robustness of size initialization --- .github/workflows/test.yml | 2 +- pixi.toml | 1 - src/panel_splitjs/base.py | 6 +++--- src/panel_splitjs/models/multi_split.js | 9 +++++++-- src/panel_splitjs/models/split.js | 15 +++++++++------ 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3b1236..751fd9d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -155,6 +155,7 @@ jobs: timeout-minutes: 60 env: PANEL_LOG_LEVEL: info + FAIL: "--screenshot only-on-failure --full-page-screenshot --output ui_screenshots --tracing retain-on-failure" steps: - uses: holoviz-dev/holoviz_tasks/pixi_install@v0 with: @@ -166,7 +167,6 @@ jobs: # Create a .uicoveragerc file to set the concurrency library to greenlet # https://github.com/microsoft/playwright-python/issues/313 echo "[run]\nconcurrency = greenlet" > .uicoveragerc - FAIL="--screenshot only-on-failure --full-page-screenshot --output ui_screenshots --tracing retain-on-failure" pixi run -e ${{ matrix.environment }} test-ui $COV --cov-config=.uicoveragerc $FAIL - name: Upload UI Screenshots uses: actions/upload-artifact@v5 diff --git a/pixi.toml b/pixi.toml index 34975d4..539a231 100644 --- a/pixi.toml +++ b/pixi.toml @@ -52,7 +52,6 @@ pytest-cov = "*" pytest-rerunfailures = "<16" pytest-xdist = "*" mypy = "*" -plotly = "*" [feature.test-unit-task.tasks] _install = "pip install --no-deps --disable-pip-version-check -e ." diff --git a/src/panel_splitjs/base.py b/src/panel_splitjs/base.py index f340bbe..bc93f81 100644 --- a/src/panel_splitjs/base.py +++ b/src/panel_splitjs/base.py @@ -13,7 +13,7 @@ BASE_PATH = Path(__file__).parent DIST_PATH = BASE_PATH / 'dist' CDN_BASE = f"https://cdn.holoviz.org/panel-splitjs/v{base_version(__version__)}" -CDN_DIST = f"{CDN_BASE}/panel-material-ui.bundle.js" +CDN_DIST = f"{CDN_BASE}/panel-splitjs.bundle.js" extension_dirs['panel-splitjs'] = DIST_PATH EXTENSION_CDN[DIST_PATH] = CDN_BASE @@ -97,7 +97,7 @@ class Split(SplitBase): collapsed = param.Integer(default=None, doc=""" Whether the first or second panel is collapsed. 0 for first panel, 1 for second panel, None for not collapsed.""") - expanded_sizes = param.NumericTuple(default=(50, 50), length=2, doc=""" + expanded_sizes = Size(default=(50, 50), allow_None=True, length=2, doc=""" The sizes of the two panels when expanded (as percentages). Default is (50, 50) . When invert=True, these percentages are automatically swapped.""") @@ -116,7 +116,7 @@ class Split(SplitBase): Whether to show the toggle buttons on the divider. When False, the buttons are hidden and panels can only be resized by dragging.""") - sizes = param.NumericTuple(default=(50, 50), length=2, doc=""" + sizes = Size(default=(50, 50), allow_None=True, length=2, doc=""" The initial sizes of the two panels (as percentages). Default is (50, 50) which means the left panel takes up 50% of the space and the right panel is not visible.""") diff --git a/src/panel_splitjs/models/multi_split.js b/src/panel_splitjs/models/multi_split.js index 79bb272..5550521 100644 --- a/src/panel_splitjs/models/multi_split.js +++ b/src/panel_splitjs/models/multi_split.js @@ -7,6 +7,7 @@ export function render({ model, el }) { split_div.classList.add("loading") let split = null + let initialized = false function reconcileChildren(parent, desiredChildren) { // Ensure each desired child is at the correct index @@ -88,15 +89,19 @@ export function render({ model, el }) { return } sizes = model.sizes - split.setSizes(sizes) + if (initialized && split) { + split.setSizes(sizes) + } }) - let initialized = false model.on("after_layout", () => { if (!initialized) { initialized = true split_div.style.visibility = "" split_div.classList.remove("loading") + if (split) { + split.setSizes(sizes) + } } }) diff --git a/src/panel_splitjs/models/split.js b/src/panel_splitjs/models/split.js index cb8a4c3..5782c1a 100644 --- a/src/panel_splitjs/models/split.js +++ b/src/panel_splitjs/models/split.js @@ -13,6 +13,8 @@ export function render({ model, el }) { const [left_min, right_min] = Array.isArray(model.min_size) ? model.min_size : [model.min_size, model.min_size] + let initialized = false + if (model.orientation === "horizontal") { split_div.style.minWidth = `${left_min + right_min + model.gutter_size}px` } else { @@ -117,7 +119,7 @@ export function render({ model, el }) { let is_collapsed = model.collapsed let sizes = model.sizes - const init_sizes = is_collapsed ? [100, 0] : model.sizes + const init_sizes = is_collapsed == null ? model.sizes : (is_collapsed ? [100, 0] : [0, 100]) const split_instance = Split([split0, split1], { sizes: init_sizes, minSize: model.min_size, @@ -168,11 +170,13 @@ export function render({ model, el }) { } else { left_content_wrapper.className = "content-wrapper" } - if (resize) { + if (resize && initialized) { split_instance.setSizes([ls, rs]) sizes = [ls, rs] window.dispatchEvent(new Event('resize')) requestAnimationFrame(() => { model.sizes = split_instance.getSizes() }) + } else if (resize) { + sizes = [ls, rs] } } @@ -182,7 +186,7 @@ export function render({ model, el }) { } sizes = model.sizes model.collapsed = (1-sizes[0]) >= 0 ? 0 : (1-sizes[1]) >= 0 ? 1 : null - sync_ui(sizes, true) + sync_ui(sizes, initialized) }) model.on("collapsed", () => { @@ -191,10 +195,9 @@ export function render({ model, el }) { } is_collapsed = model.collapsed const new_sizes = is_collapsed === 0 ? [0, 100] : (is_collapsed === 1 ? [100, 0] : model.expanded_sizes) - sync_ui(new_sizes, true) + sync_ui(new_sizes, initialized) }) - let initialized = false model.on("after_layout", () => { if (initialized) { return @@ -211,9 +214,9 @@ export function render({ model, el }) { right_arrow_button.classList.remove("animated") }, 1500) } - window.dispatchEvent(new Event('resize')) split_div.style.visibility = "" split_div.classList.remove("loading") + sync_ui(is_collapsed == null ? sizes : (is_collapsed ? [100, 0] : [0, 100]), true) }) model.on("remove", () => split_instance.destroy())