11---
2+ import { classNames } from ' @tutorialkit/components-react' ;
3+
24export type Type = ' horizontal' | ' vertical' ;
35export type Priority = ' min' | ' max' ;
46
@@ -8,11 +10,25 @@ interface Props {
810 pos? : string ;
911 min? : string ;
1012 max? : string ;
11- priority? : ' min' | ' max' ;
12- classList? : string ;
13+ class? : string ;
14+ sidePanelClass? : string ;
15+ }
16+
17+ export interface IResizablePanel {
18+ mainPanel(): HTMLElement ;
19+ sidePanel(): HTMLElement | undefined ;
20+ divider(): HTMLElement | undefined ;
1321}
1422
15- let { id, type = ' horizontal' , min = ' 0%' , pos = ' 50%' , max = ' 100%' , priority = ' min' , classList } = Astro .props ;
23+ let {
24+ id,
25+ type = ' horizontal' ,
26+ min = ' 0%' ,
27+ pos = ' 50%' ,
28+ max = ' 100%' ,
29+ class : className = ' ' ,
30+ sidePanelClass = ' ' ,
31+ } = Astro .props ;
1632
1733// check if there is a `slot` defined with name `b`
1834const hasSidePanel = Astro .slots .has (' b' );
@@ -23,18 +39,19 @@ if (!hasSidePanel) {
2339 min = ' 100%' ;
2440 max = ' 100%' ;
2541}
42+
43+ const panelClass = classNames (' overflow-hidden' , { ' h-full' : type === ' horizontal' });
2644---
2745
28- <resizable-panel
29- class ={ classList ?? ' ' }
30- data-id ={ id }
31- data-type ={ type }
32- data-pos ={ pos }
33- data-min ={ min }
34- data-priority ={ priority }
35- data-max ={ max }
36- >
37- <div class =`container max-w-full ${type }` style =`--pos: ${pos }` >
46+ <resizable-panel class ={ className } data-id ={ id } data-type ={ type } data-pos ={ pos } data-min ={ min } data-max ={ max } >
47+ <div
48+ data-id =" container"
49+ class ={ classNames (' sm:grid relative w-full h-full max-w-full' , {
50+ ' sm:grid-cols-[var(--pos)_1fr]' : type === ' horizontal' ,
51+ ' sm:grid-rows-[var(--pos)_1fr]' : type !== ' horizontal' ,
52+ })}
53+ style =`--pos: ${pos }`
54+ >
3855 <!-- It's important to keep the inline script here because it restores the position and blocks rendering to avoid flickering -->
3956 <script is:inline define:vars ={ { id , hasSidePanel }} >
4057 if (!hasSidePanel) {
@@ -44,36 +61,41 @@ if (!hasSidePanel) {
4461
4562 const sessionStorageKey = `tk_resizable_panel_${id}`;
4663
47- const $container = document.querySelector(`resizable-panel[data-id="${id}"] > .container `);
64+ const $container = document.querySelector(`resizable-panel[data-id="${id}"] > div `);
4865 const pos = sessionStorage.getItem(sessionStorageKey);
4966
5067 if (pos) {
5168 $container.style.setProperty('--pos', pos);
5269 }
5370 </script >
54- <div class =" panel" >
71+ <div class ={ panelClass } data-id = " main- panel" >
5572 <slot name =" a" />
5673 </div >
5774 {
5875 hasSidePanel && (
5976 <>
60- <div class = " panel" >
77+ <div class = { ` ${ panelClass } ${ sidePanelClass } ` } data-id = " side- panel" >
6178 <slot name = " b" />
6279 </div >
63- <div class = " divider" />
80+ <div
81+ data-id = " divider"
82+ class = { classNames (' absolute z-90 transition-colors hover:bg-gray-500/13' , {
83+ ' w-0 h-full left-[var(--pos)] cursor-ew-resize p-0 px-1.5 -translate-x-1/2' : type === ' horizontal' ,
84+ ' h-0 w-full top-[var(--pos)] cursor-ns-resize p-0 py-2 -translate-y-1/2' : type !== ' horizontal' ,
85+ })}
86+ />
6487 </>
6588 )
6689 }
6790 </div >
6891</resizable-panel>
6992
7093<script >
71- import type { Priority, Type } from './ResizablePanel.astro';
94+ import type { Type, IResizablePanel } from './ResizablePanel.astro';
7295
73- class ResizablePanel extends HTMLElement {
96+ class ResizablePanel extends HTMLElement implements IResizablePanel {
7497 readonly #id = this.dataset.id as string;
7598 readonly #type = this.dataset.type as Type;
76- readonly #priority = this.dataset.min as Priority;
7799 readonly #min = this.dataset.min as string;
78100 readonly #max = this.dataset.max as string;
79101
@@ -83,13 +105,17 @@ if (!hasSidePanel) {
83105 #dragging = false;
84106
85107 #container: HTMLElement;
108+ #mainPanel: HTMLElement;
109+ #sidePanel: HTMLElement | undefined;
86110 #divider: HTMLElement | undefined;
87111
88112 constructor() {
89113 super();
90114
91- this.#container = this.querySelector(':scope > .container') as HTMLElement;
92- this.#divider = this.#container.querySelector(':scope > .divider') as HTMLElement | undefined;
115+ this.#container = this.querySelector(':scope > [data-id="container"]') as HTMLElement;
116+ this.#mainPanel = this.#container.querySelector(':scope > [data-id="main-panel"]') as HTMLElement;
117+ this.#sidePanel = this.#container.querySelector(':scope > [data-id="side-panel"]') as HTMLElement | undefined;
118+ this.#divider = this.#container.querySelector(':scope > [data-id="divider"]') as HTMLElement | undefined;
93119
94120 this.#width = this.#container.clientWidth;
95121 this.#height = this.#container.clientHeight;
@@ -111,6 +137,18 @@ if (!hasSidePanel) {
111137 }
112138 }
113139
140+ mainPanel(): HTMLElement {
141+ return this.#mainPanel;
142+ }
143+
144+ sidePanel(): HTMLElement | undefined {
145+ return this.#sidePanel;
146+ }
147+
148+ divider(): HTMLElement | undefined {
149+ return this.#divider;
150+ }
151+
114152 connectedCallback() {
115153 this.#divider?.addEventListener('mousedown', this.#onMouseDown.bind(this));
116154 this.#divider?.addEventListener('touchstart', this.#onMouseDown.bind(this), { passive: true });
@@ -179,7 +217,6 @@ if (!hasSidePanel) {
179217 }
180218
181219 #setPosition(pos: string) {
182- const priority = this.#priority;
183220 const size = this.#type === 'horizontal' ? this.#width : this.#height;
184221
185222 let minPx = parseFloat(this.#min);
@@ -198,7 +235,7 @@ if (!hasSidePanel) {
198235 maxPx += size;
199236 }
200237
201- posPx = priority === 'min' ? Math.max(minPx, Math.min(maxPx, posPx)) : Math.min(maxPx, Math.max(minPx , posPx));
238+ posPx = Math.max(minPx, Math.min(maxPx, posPx));
202239
203240 this.#pos = `${(100 * posPx) / size}%`;
204241
@@ -208,89 +245,3 @@ if (!hasSidePanel) {
208245
209246 customElements.define('resizable-panel', ResizablePanel);
210247</script >
211-
212- <style >
213- .container {
214- --divider-thickness: var(--thickness, 10px);
215- --divider-after-size: 10px;
216-
217- display: grid;
218- position: relative;
219- width: 100%;
220- height: 100%;
221- }
222-
223- .container .panel {
224- overflow: hidden;
225- }
226-
227- .container > .divider:hover {
228- background-color: #8882;
229- }
230-
231- .container.horizontal > .panel {
232- height: 100%;
233- }
234-
235- .container.vertical {
236- grid-template-rows: var(--pos) 1fr;
237- }
238-
239- .container.horizontal {
240- grid-template-columns: var(--pos) 1fr;
241- }
242-
243- .divider {
244- position: absolute;
245- z-index: 999;
246- }
247-
248- .divider::after {
249- content: '';
250- position: absolute;
251- background-color: transparent;
252- transition: background-color 0.2s ease;
253- }
254-
255- .horizontal > .divider {
256- width: 0;
257- height: 100%;
258- left: var(--pos);
259- cursor: ew-resize;
260- padding: 0 calc(0.5 * var(--divider-thickness));
261- transform: translate(calc(-0.5 * var(--divider-thickness)), 0);
262- }
263-
264- .vertical > .divider {
265- width: 100%;
266- height: 0;
267- top: var(--pos);
268- cursor: ns-resize;
269- padding: calc(0.5 * var(--divider-thickness)) 0;
270- transform: translate(0, calc(-0.5 * var(--divider-thickness)));
271- }
272-
273- .horizontal > .divider::after {
274- left: 50%;
275- top: 0;
276- width: 0px;
277- height: 100%;
278- }
279-
280- .vertical > .divider::after {
281- top: 50%;
282- left: 0;
283- width: 100%;
284- height: 1px;
285- }
286-
287- .horizontal > .divider:hover:after {
288- left: calc(50% - var(--divider-after-size) / 2);
289- width: var(--divider-after-size);
290- }
291-
292- .vertical > .divider:hover:after {
293- top: calc(50% - var(--divider-after-size) / 2);
294- height: var(--divider-after-size);
295- }
296- </style >
0 commit comments