panel-splitjs 0.0.1a0__tar.gz → 0.2.0__tar.gz

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 (45) hide show
  1. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.github/workflows/build.yml +1 -1
  2. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.github/workflows/test.yml +41 -3
  3. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.github/workflows/update-lockfiles.yml +3 -3
  4. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/PKG-INFO +116 -41
  5. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/README.md +114 -39
  6. panel_splitjs-0.2.0/examples/apps/multi.py +21 -0
  7. panel_splitjs-0.2.0/examples/apps/simple.py +8 -0
  8. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/pixi.lock +4080 -4237
  9. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/pixi.toml +5 -7
  10. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/pyproject.toml +1 -1
  11. panel_splitjs-0.2.0/src/panel_splitjs/__init__.py +4 -0
  12. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/_version.py +2 -2
  13. panel_splitjs-0.2.0/src/panel_splitjs/base.py +164 -0
  14. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/splitjs.css +54 -17
  15. panel_splitjs-0.2.0/src/panel_splitjs/dist/panel-splitjs.bundle.js +1 -0
  16. panel_splitjs-0.2.0/src/panel_splitjs/models/multi_split.js +102 -0
  17. panel_splitjs-0.2.0/src/panel_splitjs/models/split.js +190 -0
  18. panel_splitjs-0.2.0/tests/conftest.py +18 -0
  19. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/tests/test_base.py +11 -10
  20. panel_splitjs-0.2.0/tests/test_ui.py +138 -0
  21. panel_splitjs-0.0.1a0/examples/apps/simple.py +0 -7
  22. panel_splitjs-0.0.1a0/src/panel_splitjs/__init__.py +0 -4
  23. panel_splitjs-0.0.1a0/src/panel_splitjs/base.py +0 -139
  24. panel_splitjs-0.0.1a0/src/panel_splitjs/dist/panel-splitjs.bundle.js +0 -1
  25. panel_splitjs-0.0.1a0/src/panel_splitjs/models/splitjs.js +0 -215
  26. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.copier-answers.yml +0 -0
  27. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.gitattributes +0 -0
  28. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.github/CODEOWNERS +0 -0
  29. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.github/dependabot.yml +0 -0
  30. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.gitignore +0 -0
  31. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.pre-commit-config.yaml +0 -0
  32. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/.prettierrc +0 -0
  33. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/MANIFEST.in +0 -0
  34. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/docs/assets/logo.svg +0 -0
  35. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/docs/index.md +0 -0
  36. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/docs/reference/panel_splitjs.md +0 -0
  37. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/hatch_build.py +0 -0
  38. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/mkdocs.yml +0 -0
  39. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/__version.py +0 -0
  40. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/arrow_down.svg +0 -0
  41. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/arrow_left.svg +0 -0
  42. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/arrow_right.svg +0 -0
  43. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/arrow_up.svg +0 -0
  44. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/handle.svg +0 -0
  45. {panel_splitjs-0.0.1a0 → panel_splitjs-0.2.0}/src/panel_splitjs/dist/css/handle_vertical.svg +0 -0
@@ -14,7 +14,7 @@ jobs:
14
14
  build:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v5
17
+ - uses: actions/checkout@v5.0.0
18
18
  with:
19
19
  fetch-depth: 0
20
20
  - name: Set up pixi
@@ -42,7 +42,7 @@ jobs:
42
42
  needs: [setup]
43
43
  runs-on: "ubuntu-latest"
44
44
  steps:
45
- - uses: actions/checkout@v5
45
+ - uses: actions/checkout@v5.0.0
46
46
  - name: Set up pixi
47
47
  uses: prefix-dev/setup-pixi@v0.9.1
48
48
  - uses: holoviz-dev/holoviz_tasks/pre-commit@v0
@@ -50,7 +50,7 @@ jobs:
50
50
  if: needs.setup.outputs.img_change == 'true'
51
51
  with:
52
52
  extra_args: -a --hook-stage manual oxipng || true --
53
- - uses: stefanzweifel/git-auto-commit-action@v6
53
+ - uses: stefanzweifel/git-auto-commit-action@v7
54
54
  if: needs.setup.outputs.img_change == 'true'
55
55
  with:
56
56
  commit_message: "Optimize PNG images (lossless)"
@@ -64,7 +64,7 @@ jobs:
64
64
  code_change: ${{ steps.filter.outputs.code }}
65
65
  matrix: ${{ env.MATRIX }}
66
66
  steps:
67
- - uses: actions/checkout@v5
67
+ - uses: actions/checkout@v5.0.0
68
68
  if: github.event_name != 'pull_request'
69
69
  - name: Check for code changes
70
70
  uses: dorny/paths-filter@v3
@@ -142,3 +142,41 @@ jobs:
142
142
  with:
143
143
  token: ${{ secrets.CODECOV_TOKEN }}
144
144
  slug: panel-extensions/panel-splitjs
145
+ ui_test:
146
+ name: ui:${{ matrix.environment }}:${{ matrix.os }}
147
+ needs: [pre_commit, setup, pixi_lock]
148
+ runs-on: ${{ matrix.os }}
149
+ if: needs.setup.outputs.code_change == 'true'
150
+ strategy:
151
+ fail-fast: false
152
+ matrix:
153
+ os: ["ubuntu-latest", "macos-latest", "windows-latest"]
154
+ environment: ["test-ui"]
155
+ timeout-minutes: 60
156
+ env:
157
+ PANEL_LOG_LEVEL: info
158
+ steps:
159
+ - uses: holoviz-dev/holoviz_tasks/pixi_install@v0
160
+ with:
161
+ environments: ${{ matrix.environment }}
162
+ install: false
163
+ id: install
164
+ - name: Test UI
165
+ run: |
166
+ # Create a .uicoveragerc file to set the concurrency library to greenlet
167
+ # https://github.com/microsoft/playwright-python/issues/313
168
+ echo "[run]\nconcurrency = greenlet" > .uicoveragerc
169
+ FAIL="--screenshot only-on-failure --full-page-screenshot --output ui_screenshots --tracing retain-on-failure"
170
+ pixi run -e ${{ matrix.environment }} test-ui $COV --cov-config=.uicoveragerc $FAIL
171
+ - name: Upload UI Screenshots
172
+ uses: actions/upload-artifact@v4
173
+ if: always()
174
+ with:
175
+ name: ui_screenshots_${{ runner.os }}
176
+ path: ./ui_screenshots
177
+ if-no-files-found: ignore
178
+ - name: Upload coverage reports to Codecov
179
+ uses: codecov/codecov-action@v5
180
+ with:
181
+ token: ${{ secrets.CODECOV_TOKEN }}
182
+ slug: panel-extensions/panel-splitjs
@@ -12,16 +12,16 @@ jobs:
12
12
  pixi-update:
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
15
+ - uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
16
16
  - name: Set up pixi
17
- uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
17
+ uses: prefix-dev/setup-pixi@194d461b21b6c5717c722ffc597fa91ed2ff29fa # v0.9.1
18
18
  with:
19
19
  run-install: false
20
20
  - name: Update lockfiles
21
21
  run: |
22
22
  pixi update --json --no-install | pixi exec pixi-diff-to-markdown >> diff.md
23
23
  - name: Create pull request
24
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
24
+ uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
25
25
  with:
26
26
  token: ${{ secrets.GITHUB_TOKEN }}
27
27
  commit-message: Update pixi lockfile
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: panel-splitjs
3
- Version: 0.0.1a0
3
+ Version: 0.2.0
4
4
  Summary: Provides split.js components for Panel.
5
5
  Project-URL: Homepage, https://github.com/panel-extensions/panel-splitjs
6
6
  Project-URL: Source, https://github.com/panel-extensions/panel-splitjs
@@ -22,7 +22,7 @@ Classifier: Topic :: Scientific/Engineering
22
22
  Classifier: Topic :: Software Development :: Libraries
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: packaging
25
- Requires-Dist: panel>=1.8.0
25
+ Requires-Dist: panel>=1.8.3
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: mkdocs-material; extra == 'dev'
28
28
  Requires-Dist: mkdocstrings[python]; extra == 'dev'
@@ -48,13 +48,13 @@ A responsive, draggable split panel component for [Panel](https://panel.holoviz.
48
48
 
49
49
  ## Features
50
50
 
51
- - **Draggable divider** - Resize panels by dragging the divider between them
52
- - **Collapsible panels** - Toggle panels open/closed with optional buttons
51
+ - **Draggable dividers** - Resize panels by dragging the divider between them
52
+ - **Collapsible panels** - Collapse individual panels with toggle buttons
53
53
  - **Flexible orientation** - Support for both horizontal and vertical splits
54
- - **Minimum size constraints** - Enforce minimum panel sizes to prevent over-collapse
55
- - **Smooth animations** - Beautiful transitions when toggling panels
54
+ - **Size constraints** - Enforce minimum and maximum panel sizes
55
+ - **Snap behavior** - Smart snapping to minimum sizes for better UX
56
56
  - **Customizable sizes** - Control initial and expanded panel sizes
57
- - **Invertible layout** - Swap panel positions and button locations
57
+ - **Multi-panel support** - Create layouts with 2+ panels using `MultiSplit`
58
58
 
59
59
  ## Installation
60
60
 
@@ -82,7 +82,8 @@ pn.extension()
82
82
  split = Split(
83
83
  pn.pane.Markdown("## Left Panel\nContent here"),
84
84
  pn.pane.Markdown("## Right Panel\nMore content"),
85
- sizes=(50, 50), # Equal sizing
85
+ sizes=(50, 50), # Equal sizing initially
86
+ min_size=100, # Minimum 100px for each panel
86
87
  show_buttons=True
87
88
  )
88
89
 
@@ -114,6 +115,7 @@ split = HSplit(
114
115
  left_panel,
115
116
  right_panel,
116
117
  sizes=(70, 30), # 70% left, 30% right
118
+ min_size=200, # Minimum 200px for each panel
117
119
  show_buttons=True
118
120
  )
119
121
 
@@ -135,7 +137,7 @@ split = VSplit(
135
137
  top_panel,
136
138
  bottom_panel,
137
139
  sizes=(60, 40),
138
- orientation="vertical"
140
+ min_size=150
139
141
  )
140
142
 
141
143
  split.servable()
@@ -149,72 +151,90 @@ from panel_splitjs import Split
149
151
 
150
152
  pn.extension()
151
153
 
152
- # Start with sidebar collapsed
154
+ # Start with right panel collapsed
153
155
  split = Split(
154
156
  pn.pane.Markdown("## Main Content"),
155
157
  pn.pane.Markdown("## Collapsible Sidebar"),
156
- collapsed=True,
158
+ collapsed=1, # 0 for first panel, 1 for second panel, None for not collapsed
157
159
  expanded_sizes=(65, 35), # When expanded, 65% main, 35% sidebar
158
160
  show_buttons=True,
159
- min_sizes=(200, 200) # Minimum 200px for each panel
161
+ min_size=(200, 200) # Minimum 200px for each panel
160
162
  )
161
163
 
162
164
  # Toggle collapse programmatically
163
165
  button = pn.widgets.Button(name="Toggle Sidebar")
164
- button.on_click(lambda e: setattr(split, 'collapsed', not split.collapsed))
166
+ def toggle(event):
167
+ split.collapsed = None if split.collapsed == 1 else 1
168
+ button.on_click(toggle)
165
169
 
166
170
  pn.Column(button, split).servable()
167
171
  ```
168
172
 
169
- ### Inverted Layout
173
+ ### Multi-Panel Split
170
174
 
171
175
  ```python
172
176
  import panel as pn
173
- from panel_splitjs import Split
177
+ from panel_splitjs import MultiSplit
174
178
 
175
179
  pn.extension()
176
180
 
177
- # Inverted: right panel collapses, button on right side
178
- split = Split(
179
- pn.pane.Markdown("## Secondary Panel"),
180
- pn.pane.Markdown("## Main Content"),
181
- invert=True, # Swap layout and button position
182
- collapsed=True,
183
- expanded_sizes=(35, 65),
184
- show_buttons=True
181
+ # Create a layout with three panels
182
+ multi = MultiSplit(
183
+ pn.pane.Markdown("## Panel 1"),
184
+ pn.pane.Markdown("## Panel 2"),
185
+ pn.pane.Markdown("## Panel 3"),
186
+ sizes=(30, 40, 30), # Three panels with custom sizing
187
+ min_size=100, # Minimum 100px for each panel
188
+ orientation="horizontal"
185
189
  )
186
190
 
187
- split.servable()
191
+ multi.servable()
188
192
  ```
189
193
 
190
194
  ## API Reference
191
195
 
192
196
  ### Split
193
197
 
194
- The main split panel component with full customization options.
198
+ The main split panel component for creating two-panel layouts with collapsible functionality.
195
199
 
196
200
  **Parameters:**
197
201
 
198
202
  - `objects` (list): Two Panel components to display in the split panels
199
- - `collapsed` (bool, default=False): Whether the secondary panel is collapsed
200
- - `expanded_sizes` (tuple, default=(50, 50)): Percentage sizes when expanded (must sum to 100)
201
- - `invert` (bool, default=False): Swap panel positions and button locations (constant after init)
202
- - `min_sizes` (tuple, default=(0, 0)): Minimum sizes in pixels for each panel
203
- - `orientation` (str, default="horizontal"): Either "horizontal" or "vertical"
204
- - `show_buttons` (bool, default=False): Show collapse/expand toggle buttons
205
- - `sizes` (tuple, default=(100, 0)): Initial percentage sizes (must sum to 100)
203
+ - `collapsed` (int | None, default=None): Which panel is collapsed - `0` for first panel, `1` for second panel, `None` for not collapsed
204
+ - `expanded_sizes` (tuple, default=(50, 50)): Percentage sizes when both panels are expanded
205
+ - `max_size` (int | tuple, default=None): Maximum sizes in pixels - single value applies to both panels, tuple for individual sizes
206
+ - `min_size` (int | tuple, default=0): Minimum sizes in pixels - single value applies to both panels, tuple for individual sizes
207
+ - `orientation` (str, default="horizontal"): Either `"horizontal"` or `"vertical"`
208
+ - `show_buttons` (bool, default=True): Show collapse/expand toggle buttons on the divider
209
+ - `sizes` (tuple, default=(50, 50)): Initial percentage sizes of the panels
210
+ - `snap_size` (int, default=30): Snap to minimum size at this offset in pixels
211
+ - `step_size` (int, default=1): Step size in pixels at which panel sizes can be changed
206
212
 
207
213
  ### HSplit
208
214
 
209
215
  Horizontal split panel (convenience class).
210
216
 
211
- Same parameters as `Split` but `orientation` is locked to "horizontal".
217
+ Same parameters as `Split` but `orientation` is locked to `"horizontal"`.
212
218
 
213
219
  ### VSplit
214
220
 
215
221
  Vertical split panel (convenience class).
216
222
 
217
- Same parameters as `Split` but `orientation` is locked to "vertical".
223
+ Same parameters as `Split` but `orientation` is locked to `"vertical"`.
224
+
225
+ ### MultiSplit
226
+
227
+ Multi-panel split component for creating layouts with three or more panels.
228
+
229
+ **Parameters:**
230
+
231
+ - `objects` (list): List of Panel components to display (3 or more)
232
+ - `max_size` (int | tuple, default=None): Maximum sizes in pixels - single value applies to all panels, tuple for individual sizes
233
+ - `min_size` (int | tuple, default=100): Minimum sizes in pixels - single value applies to all panels, tuple for individual sizes
234
+ - `orientation` (str, default="horizontal"): Either `"horizontal"` or `"vertical"`
235
+ - `sizes` (tuple, default=None): Initial percentage sizes of the panels (length must match number of objects)
236
+ - `snap_size` (int, default=30): Snap to minimum size at this offset in pixels
237
+ - `step_size` (int, default=1): Step size in pixels at which panel sizes can be changed
218
238
 
219
239
  ## Common Use Cases
220
240
 
@@ -232,10 +252,10 @@ output = pn.Column("# Output Area")
232
252
  split = Split(
233
253
  chat,
234
254
  output,
235
- collapsed=False,
255
+ collapsed=None, # Both panels visible
236
256
  expanded_sizes=(50, 50),
237
257
  show_buttons=True,
238
- min_sizes=(300, 300)
258
+ min_size=(300, 300) # Minimum 300px for each panel
239
259
  )
240
260
 
241
261
  split.servable()
@@ -260,16 +280,16 @@ visualization = pn.pane.Markdown("## Main Visualization Area")
260
280
  split = Split(
261
281
  controls,
262
282
  visualization,
263
- collapsed=True,
283
+ collapsed=0, # Start with controls collapsed
264
284
  expanded_sizes=(25, 75),
265
285
  show_buttons=True,
266
- min_sizes=(250, 400)
286
+ min_size=(250, 400) # Minimum sizes for each panel
267
287
  )
268
288
 
269
289
  split.servable()
270
290
  ```
271
291
 
272
- ### Responsive Layout
292
+ ### Responsive Layout with Size Constraints
273
293
 
274
294
  ```python
275
295
  import panel as pn
@@ -277,18 +297,73 @@ from panel_splitjs import Split
277
297
 
278
298
  pn.extension()
279
299
 
280
- # Automatically adjust to available space
281
300
  split = Split(
282
301
  pn.pane.Markdown("## Panel 1\nResponsive content"),
283
302
  pn.pane.Markdown("## Panel 2\nMore responsive content"),
284
303
  sizes=(50, 50),
285
- min_sizes=(200, 200), # Prevent panels from getting too small
304
+ min_size=200, # Minimum 200px per panel
305
+ max_size=800, # Maximum 800px per panel
306
+ snap_size=50, # Snap to min size when within 50px
286
307
  show_buttons=True
287
308
  )
288
309
 
289
310
  split.servable()
290
311
  ```
291
312
 
313
+ ### Complex Multi-Panel Layout
314
+
315
+ ```python
316
+ import panel as pn
317
+ from panel_splitjs import MultiSplit
318
+
319
+ pn.extension()
320
+
321
+ # Create a four-panel layout
322
+ sidebar = pn.Column("## Sidebar", pn.widgets.Select(options=["A", "B", "C"]))
323
+ main = pn.pane.Markdown("## Main Content Area")
324
+ detail = pn.pane.Markdown("## Detail Panel")
325
+ console = pn.pane.Markdown("## Console Output")
326
+
327
+ multi = MultiSplit(
328
+ sidebar,
329
+ main,
330
+ detail,
331
+ console,
332
+ sizes=(15, 40, 25, 20), # Custom sizing for each panel
333
+ min_size=(150, 300, 200, 150), # Individual minimums
334
+ orientation="horizontal"
335
+ )
336
+
337
+ multi.servable()
338
+ ```
339
+
340
+ ### Nested Splits
341
+
342
+ ```python
343
+ import panel as pn
344
+ from panel_splitjs import HSplit, VSplit
345
+
346
+ pn.extension()
347
+
348
+ # Create a nested layout: horizontal split with vertical split on right
349
+ left = pn.pane.Markdown("## Left Panel")
350
+
351
+ # Right side has a vertical split
352
+ top_right = pn.pane.Markdown("## Top Right")
353
+ bottom_right = pn.pane.Markdown("## Bottom Right")
354
+ right = VSplit(top_right, bottom_right, sizes=(60, 40))
355
+
356
+ # Main horizontal split
357
+ layout = HSplit(
358
+ left,
359
+ right,
360
+ sizes=(30, 70),
361
+ min_size=200
362
+ )
363
+
364
+ layout.servable()
365
+ ```
366
+
292
367
  ## Development
293
368
 
294
369
  This project is managed by [pixi](https://pixi.sh).
@@ -9,13 +9,13 @@ A responsive, draggable split panel component for [Panel](https://panel.holoviz.
9
9
 
10
10
  ## Features
11
11
 
12
- - **Draggable divider** - Resize panels by dragging the divider between them
13
- - **Collapsible panels** - Toggle panels open/closed with optional buttons
12
+ - **Draggable dividers** - Resize panels by dragging the divider between them
13
+ - **Collapsible panels** - Collapse individual panels with toggle buttons
14
14
  - **Flexible orientation** - Support for both horizontal and vertical splits
15
- - **Minimum size constraints** - Enforce minimum panel sizes to prevent over-collapse
16
- - **Smooth animations** - Beautiful transitions when toggling panels
15
+ - **Size constraints** - Enforce minimum and maximum panel sizes
16
+ - **Snap behavior** - Smart snapping to minimum sizes for better UX
17
17
  - **Customizable sizes** - Control initial and expanded panel sizes
18
- - **Invertible layout** - Swap panel positions and button locations
18
+ - **Multi-panel support** - Create layouts with 2+ panels using `MultiSplit`
19
19
 
20
20
  ## Installation
21
21
 
@@ -43,7 +43,8 @@ pn.extension()
43
43
  split = Split(
44
44
  pn.pane.Markdown("## Left Panel\nContent here"),
45
45
  pn.pane.Markdown("## Right Panel\nMore content"),
46
- sizes=(50, 50), # Equal sizing
46
+ sizes=(50, 50), # Equal sizing initially
47
+ min_size=100, # Minimum 100px for each panel
47
48
  show_buttons=True
48
49
  )
49
50
 
@@ -75,6 +76,7 @@ split = HSplit(
75
76
  left_panel,
76
77
  right_panel,
77
78
  sizes=(70, 30), # 70% left, 30% right
79
+ min_size=200, # Minimum 200px for each panel
78
80
  show_buttons=True
79
81
  )
80
82
 
@@ -96,7 +98,7 @@ split = VSplit(
96
98
  top_panel,
97
99
  bottom_panel,
98
100
  sizes=(60, 40),
99
- orientation="vertical"
101
+ min_size=150
100
102
  )
101
103
 
102
104
  split.servable()
@@ -110,72 +112,90 @@ from panel_splitjs import Split
110
112
 
111
113
  pn.extension()
112
114
 
113
- # Start with sidebar collapsed
115
+ # Start with right panel collapsed
114
116
  split = Split(
115
117
  pn.pane.Markdown("## Main Content"),
116
118
  pn.pane.Markdown("## Collapsible Sidebar"),
117
- collapsed=True,
119
+ collapsed=1, # 0 for first panel, 1 for second panel, None for not collapsed
118
120
  expanded_sizes=(65, 35), # When expanded, 65% main, 35% sidebar
119
121
  show_buttons=True,
120
- min_sizes=(200, 200) # Minimum 200px for each panel
122
+ min_size=(200, 200) # Minimum 200px for each panel
121
123
  )
122
124
 
123
125
  # Toggle collapse programmatically
124
126
  button = pn.widgets.Button(name="Toggle Sidebar")
125
- button.on_click(lambda e: setattr(split, 'collapsed', not split.collapsed))
127
+ def toggle(event):
128
+ split.collapsed = None if split.collapsed == 1 else 1
129
+ button.on_click(toggle)
126
130
 
127
131
  pn.Column(button, split).servable()
128
132
  ```
129
133
 
130
- ### Inverted Layout
134
+ ### Multi-Panel Split
131
135
 
132
136
  ```python
133
137
  import panel as pn
134
- from panel_splitjs import Split
138
+ from panel_splitjs import MultiSplit
135
139
 
136
140
  pn.extension()
137
141
 
138
- # Inverted: right panel collapses, button on right side
139
- split = Split(
140
- pn.pane.Markdown("## Secondary Panel"),
141
- pn.pane.Markdown("## Main Content"),
142
- invert=True, # Swap layout and button position
143
- collapsed=True,
144
- expanded_sizes=(35, 65),
145
- show_buttons=True
142
+ # Create a layout with three panels
143
+ multi = MultiSplit(
144
+ pn.pane.Markdown("## Panel 1"),
145
+ pn.pane.Markdown("## Panel 2"),
146
+ pn.pane.Markdown("## Panel 3"),
147
+ sizes=(30, 40, 30), # Three panels with custom sizing
148
+ min_size=100, # Minimum 100px for each panel
149
+ orientation="horizontal"
146
150
  )
147
151
 
148
- split.servable()
152
+ multi.servable()
149
153
  ```
150
154
 
151
155
  ## API Reference
152
156
 
153
157
  ### Split
154
158
 
155
- The main split panel component with full customization options.
159
+ The main split panel component for creating two-panel layouts with collapsible functionality.
156
160
 
157
161
  **Parameters:**
158
162
 
159
163
  - `objects` (list): Two Panel components to display in the split panels
160
- - `collapsed` (bool, default=False): Whether the secondary panel is collapsed
161
- - `expanded_sizes` (tuple, default=(50, 50)): Percentage sizes when expanded (must sum to 100)
162
- - `invert` (bool, default=False): Swap panel positions and button locations (constant after init)
163
- - `min_sizes` (tuple, default=(0, 0)): Minimum sizes in pixels for each panel
164
- - `orientation` (str, default="horizontal"): Either "horizontal" or "vertical"
165
- - `show_buttons` (bool, default=False): Show collapse/expand toggle buttons
166
- - `sizes` (tuple, default=(100, 0)): Initial percentage sizes (must sum to 100)
164
+ - `collapsed` (int | None, default=None): Which panel is collapsed - `0` for first panel, `1` for second panel, `None` for not collapsed
165
+ - `expanded_sizes` (tuple, default=(50, 50)): Percentage sizes when both panels are expanded
166
+ - `max_size` (int | tuple, default=None): Maximum sizes in pixels - single value applies to both panels, tuple for individual sizes
167
+ - `min_size` (int | tuple, default=0): Minimum sizes in pixels - single value applies to both panels, tuple for individual sizes
168
+ - `orientation` (str, default="horizontal"): Either `"horizontal"` or `"vertical"`
169
+ - `show_buttons` (bool, default=True): Show collapse/expand toggle buttons on the divider
170
+ - `sizes` (tuple, default=(50, 50)): Initial percentage sizes of the panels
171
+ - `snap_size` (int, default=30): Snap to minimum size at this offset in pixels
172
+ - `step_size` (int, default=1): Step size in pixels at which panel sizes can be changed
167
173
 
168
174
  ### HSplit
169
175
 
170
176
  Horizontal split panel (convenience class).
171
177
 
172
- Same parameters as `Split` but `orientation` is locked to "horizontal".
178
+ Same parameters as `Split` but `orientation` is locked to `"horizontal"`.
173
179
 
174
180
  ### VSplit
175
181
 
176
182
  Vertical split panel (convenience class).
177
183
 
178
- Same parameters as `Split` but `orientation` is locked to "vertical".
184
+ Same parameters as `Split` but `orientation` is locked to `"vertical"`.
185
+
186
+ ### MultiSplit
187
+
188
+ Multi-panel split component for creating layouts with three or more panels.
189
+
190
+ **Parameters:**
191
+
192
+ - `objects` (list): List of Panel components to display (3 or more)
193
+ - `max_size` (int | tuple, default=None): Maximum sizes in pixels - single value applies to all panels, tuple for individual sizes
194
+ - `min_size` (int | tuple, default=100): Minimum sizes in pixels - single value applies to all panels, tuple for individual sizes
195
+ - `orientation` (str, default="horizontal"): Either `"horizontal"` or `"vertical"`
196
+ - `sizes` (tuple, default=None): Initial percentage sizes of the panels (length must match number of objects)
197
+ - `snap_size` (int, default=30): Snap to minimum size at this offset in pixels
198
+ - `step_size` (int, default=1): Step size in pixels at which panel sizes can be changed
179
199
 
180
200
  ## Common Use Cases
181
201
 
@@ -193,10 +213,10 @@ output = pn.Column("# Output Area")
193
213
  split = Split(
194
214
  chat,
195
215
  output,
196
- collapsed=False,
216
+ collapsed=None, # Both panels visible
197
217
  expanded_sizes=(50, 50),
198
218
  show_buttons=True,
199
- min_sizes=(300, 300)
219
+ min_size=(300, 300) # Minimum 300px for each panel
200
220
  )
201
221
 
202
222
  split.servable()
@@ -221,16 +241,16 @@ visualization = pn.pane.Markdown("## Main Visualization Area")
221
241
  split = Split(
222
242
  controls,
223
243
  visualization,
224
- collapsed=True,
244
+ collapsed=0, # Start with controls collapsed
225
245
  expanded_sizes=(25, 75),
226
246
  show_buttons=True,
227
- min_sizes=(250, 400)
247
+ min_size=(250, 400) # Minimum sizes for each panel
228
248
  )
229
249
 
230
250
  split.servable()
231
251
  ```
232
252
 
233
- ### Responsive Layout
253
+ ### Responsive Layout with Size Constraints
234
254
 
235
255
  ```python
236
256
  import panel as pn
@@ -238,18 +258,73 @@ from panel_splitjs import Split
238
258
 
239
259
  pn.extension()
240
260
 
241
- # Automatically adjust to available space
242
261
  split = Split(
243
262
  pn.pane.Markdown("## Panel 1\nResponsive content"),
244
263
  pn.pane.Markdown("## Panel 2\nMore responsive content"),
245
264
  sizes=(50, 50),
246
- min_sizes=(200, 200), # Prevent panels from getting too small
265
+ min_size=200, # Minimum 200px per panel
266
+ max_size=800, # Maximum 800px per panel
267
+ snap_size=50, # Snap to min size when within 50px
247
268
  show_buttons=True
248
269
  )
249
270
 
250
271
  split.servable()
251
272
  ```
252
273
 
274
+ ### Complex Multi-Panel Layout
275
+
276
+ ```python
277
+ import panel as pn
278
+ from panel_splitjs import MultiSplit
279
+
280
+ pn.extension()
281
+
282
+ # Create a four-panel layout
283
+ sidebar = pn.Column("## Sidebar", pn.widgets.Select(options=["A", "B", "C"]))
284
+ main = pn.pane.Markdown("## Main Content Area")
285
+ detail = pn.pane.Markdown("## Detail Panel")
286
+ console = pn.pane.Markdown("## Console Output")
287
+
288
+ multi = MultiSplit(
289
+ sidebar,
290
+ main,
291
+ detail,
292
+ console,
293
+ sizes=(15, 40, 25, 20), # Custom sizing for each panel
294
+ min_size=(150, 300, 200, 150), # Individual minimums
295
+ orientation="horizontal"
296
+ )
297
+
298
+ multi.servable()
299
+ ```
300
+
301
+ ### Nested Splits
302
+
303
+ ```python
304
+ import panel as pn
305
+ from panel_splitjs import HSplit, VSplit
306
+
307
+ pn.extension()
308
+
309
+ # Create a nested layout: horizontal split with vertical split on right
310
+ left = pn.pane.Markdown("## Left Panel")
311
+
312
+ # Right side has a vertical split
313
+ top_right = pn.pane.Markdown("## Top Right")
314
+ bottom_right = pn.pane.Markdown("## Bottom Right")
315
+ right = VSplit(top_right, bottom_right, sizes=(60, 40))
316
+
317
+ # Main horizontal split
318
+ layout = HSplit(
319
+ left,
320
+ right,
321
+ sizes=(30, 70),
322
+ min_size=200
323
+ )
324
+
325
+ layout.servable()
326
+ ```
327
+
253
328
  ## Development
254
329
 
255
330
  This project is managed by [pixi](https://pixi.sh).
@@ -0,0 +1,21 @@
1
+ from panel_splitjs import MultiSplit
2
+ from panel_material_ui import Button, Column, Paper, Row
3
+
4
+ paper_opts = dict(elevation=3, margin=10, sizing_mode="stretch_both")
5
+
6
+ add = Button(label="Add item", on_click=lambda _: ms.append(Paper("Added", **paper_opts)))
7
+ insert = Button(label="Insert item", on_click=lambda _: ms.insert(2, Paper("Inserted", **paper_opts)))
8
+
9
+ ms = MultiSplit(
10
+ Paper("Foo", **paper_opts),
11
+ Paper("Bar", **paper_opts),
12
+ Paper("Baz", **paper_opts),
13
+ Paper("Qux", **paper_opts),
14
+ Paper("Quux", **paper_opts),
15
+ sizes=(20, 30, 20, 10, 20),
16
+ min_size=100,
17
+ sizing_mode="stretch_width",
18
+ height=400
19
+ )
20
+
21
+ Column(Row(add, insert), ms).servable()