@@ -38,12 +38,19 @@ export interface SplitPanelProps {
38
38
39
39
/** CSS transition timing function for the collapse transition */
40
40
transitionTimingFunctionCollapse? : string ;
41
+
42
+ /** What size values the divider should snap to */
43
+ snapPoints? : number [];
44
+
45
+ /** How close to the snap point the size should be before the snapping occurs */
46
+ snapThreshold? : number ;
41
47
}
42
48
</script >
43
49
44
50
<script lang="ts" setup>
45
51
import { clamp , useDraggable , useElementSize , useResizeObserver } from ' @vueuse/core' ;
46
52
import { computed , onMounted , ref , useTemplateRef , watch } from ' vue' ;
53
+ import { closestNumber } from ' ./utils/closest-number' ;
47
54
import { percentageToPixels } from ' ./utils/percentage-to-pixels' ;
48
55
import { pixelsToPercentage } from ' ./utils/pixels-to-percentage' ;
49
56
@@ -58,6 +65,8 @@ const props = withDefaults(defineProps<SplitPanelProps>(), {
58
65
transitionDuration: ' 0' ,
59
66
transitionTimingFunctionCollapse: ' cubic-bezier(0.4, 0, 0.6, 1)' ,
60
67
transitionTimingFunctionExpand: ' cubic-bezier(0, 0, 0.2, 1)' ,
68
+ snapPoints : () => [],
69
+ snapThreshold: 12 ,
61
70
});
62
71
63
72
const panelEl = useTemplateRef (' split-panel' );
@@ -123,6 +132,11 @@ const maxSizePercentage = computed(() => {
123
132
return pixelsToPercentage (componentSize .value , props .maxSize );
124
133
});
125
134
135
+ const snapPixels = computed (() => {
136
+ if (props .sizeUnit === ' px' ) return props .snapPoints ;
137
+ return props .snapPoints .map ((snapPercentage ) => percentageToPixels (componentSize .value , snapPercentage ));
138
+ });
139
+
126
140
let expandedSizePercentage = 0 ;
127
141
128
142
/** Whether the primary column is collapsed or not */
@@ -177,6 +191,19 @@ watch([dividerX, dividerY], ([newX, newY]) => {
177
191
}
178
192
}
179
193
194
+ for (let snapPoint of snapPixels .value ) {
195
+ if (props .direction === ' rtl' && props .orientation === ' horizontal' ) {
196
+ snapPoint = componentSize .value - snapPoint ;
197
+ }
198
+
199
+ if (
200
+ newPositionInPixels >= snapPoint - props .snapThreshold
201
+ && newPositionInPixels <= snapPoint + props .snapThreshold
202
+ ) {
203
+ newPositionInPixels = snapPoint ;
204
+ }
205
+ }
206
+
180
207
sizePercentage .value = clamp (pixelsToPercentage (componentSize .value , newPositionInPixels ), 0 , 100 );
181
208
});
182
209
@@ -237,6 +264,14 @@ const handleKeydown = (event: KeyboardEvent) => {
237
264
}
238
265
};
239
266
267
+ const handleDblClick = () => {
268
+ const closest = closestNumber (snapPixels .value , sizePixels .value );
269
+
270
+ if (closest !== undefined ) {
271
+ sizePixels .value = closest ;
272
+ }
273
+ };
274
+
240
275
const gridTemplate = computed (() => {
241
276
let primary: string ;
242
277
@@ -293,6 +328,7 @@ defineExpose({ collapse, expand, toggle });
293
328
aria-valuemax =" 100"
294
329
aria-label =" Resize"
295
330
@keydown.prevent =" handleKeydown"
331
+ @dblclick =" handleDblClick"
296
332
>
297
333
<slot name =" divider" >
298
334
<div />
0 commit comments