1
1
---
2
+ import { classNames } from ' @tutorialkit/components-react' ;
3
+
2
4
export type Type = ' horizontal' | ' vertical' ;
3
5
export type Priority = ' min' | ' max' ;
4
6
@@ -8,11 +10,25 @@ interface Props {
8
10
pos? : string ;
9
11
min? : string ;
10
12
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 ;
13
21
}
14
22
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 ;
16
32
17
33
// check if there is a `slot` defined with name `b`
18
34
const hasSidePanel = Astro .slots .has (' b' );
@@ -23,18 +39,19 @@ if (!hasSidePanel) {
23
39
min = ' 100%' ;
24
40
max = ' 100%' ;
25
41
}
42
+
43
+ const panelClass = classNames (' overflow-hidden' , { ' h-full' : type === ' horizontal' });
26
44
---
27
45
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
+ >
38
55
<!-- It's important to keep the inline script here because it restores the position and blocks rendering to avoid flickering -->
39
56
<script is:inline define:vars ={ { id , hasSidePanel }} >
40
57
if (!hasSidePanel) {
@@ -44,36 +61,41 @@ if (!hasSidePanel) {
44
61
45
62
const sessionStorageKey = `tk_resizable_panel_${id}`;
46
63
47
- const $container = document.querySelector(`resizable-panel[data-id="${id}"] > .container `);
64
+ const $container = document.querySelector(`resizable-panel[data-id="${id}"] > div `);
48
65
const pos = sessionStorage.getItem(sessionStorageKey);
49
66
50
67
if (pos) {
51
68
$container.style.setProperty('--pos', pos);
52
69
}
53
70
</script >
54
- <div class =" panel" >
71
+ <div class ={ panelClass } data-id = " main- panel" >
55
72
<slot name =" a" />
56
73
</div >
57
74
{
58
75
hasSidePanel && (
59
76
<>
60
- <div class = " panel" >
77
+ <div class = { ` ${ panelClass } ${ sidePanelClass } ` } data-id = " side- panel" >
61
78
<slot name = " b" />
62
79
</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
+ />
64
87
</>
65
88
)
66
89
}
67
90
</div >
68
91
</resizable-panel>
69
92
70
93
<script >
71
- import type { Priority, Type } from './ResizablePanel.astro';
94
+ import type { Type, IResizablePanel } from './ResizablePanel.astro';
72
95
73
- class ResizablePanel extends HTMLElement {
96
+ class ResizablePanel extends HTMLElement implements IResizablePanel {
74
97
readonly #id = this.dataset.id as string;
75
98
readonly #type = this.dataset.type as Type;
76
- readonly #priority = this.dataset.min as Priority;
77
99
readonly #min = this.dataset.min as string;
78
100
readonly #max = this.dataset.max as string;
79
101
@@ -83,13 +105,17 @@ if (!hasSidePanel) {
83
105
#dragging = false;
84
106
85
107
#container: HTMLElement;
108
+ #mainPanel: HTMLElement;
109
+ #sidePanel: HTMLElement | undefined;
86
110
#divider: HTMLElement | undefined;
87
111
88
112
constructor() {
89
113
super();
90
114
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;
93
119
94
120
this.#width = this.#container.clientWidth;
95
121
this.#height = this.#container.clientHeight;
@@ -111,6 +137,18 @@ if (!hasSidePanel) {
111
137
}
112
138
}
113
139
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
+
114
152
connectedCallback() {
115
153
this.#divider?.addEventListener('mousedown', this.#onMouseDown.bind(this));
116
154
this.#divider?.addEventListener('touchstart', this.#onMouseDown.bind(this), { passive: true });
@@ -179,7 +217,6 @@ if (!hasSidePanel) {
179
217
}
180
218
181
219
#setPosition(pos: string) {
182
- const priority = this.#priority;
183
220
const size = this.#type === 'horizontal' ? this.#width : this.#height;
184
221
185
222
let minPx = parseFloat(this.#min);
@@ -198,7 +235,7 @@ if (!hasSidePanel) {
198
235
maxPx += size;
199
236
}
200
237
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));
202
239
203
240
this.#pos = `${(100 * posPx) / size}%`;
204
241
@@ -208,89 +245,3 @@ if (!hasSidePanel) {
208
245
209
246
customElements.define('resizable-panel', ResizablePanel);
210
247
</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