|
6 | 6 | export let width: number | null = null; |
7 | 7 | export let left: number | null = null; |
8 | 8 | export let className: { root?: string; handle?: string; handleE?: string; handleW?: string }; |
9 | | - export let isResizing: boolean | null = null; |
| 9 | + let moveableRef: HTMLDivElement; |
| 10 | + let isResizing: boolean | null = null; |
10 | 11 | let direction: Direction | null = null; |
11 | | - let resizableRef: HTMLDivElement; |
12 | 12 | let mousePositionWhenResizingStart: number | null = null; |
13 | 13 | let trimmerRectWhenResizingStart: DOMRect | null = null; |
| 14 | + let isDragging = false; |
| 15 | + let moveablePositionWhenDragStart: { top: number; left: number } | null = null; |
14 | 16 | let dispatcher = createEventDispatcher(); |
15 | 17 |
|
16 | 18 | function handleResizeStart( |
|
19 | 21 | }, |
20 | 22 | dir: Direction |
21 | 23 | ) { |
22 | | - trimmerRectWhenResizingStart = resizableRef.getBoundingClientRect(); |
| 24 | + trimmerRectWhenResizingStart = moveableRef.getBoundingClientRect(); |
23 | 25 | mousePositionWhenResizingStart = e.pageX; |
24 | 26 | isResizing = true; |
25 | 27 | direction = dir; |
|
36 | 38 | return; |
37 | 39 | } |
38 | 40 |
|
39 | | - const trimmerRect = resizableRef.getBoundingClientRect(); |
| 41 | + const trimmerRect = moveableRef.getBoundingClientRect(); |
40 | 42 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
41 | | - const constrains = resizableRef.parentElement!.getBoundingClientRect(); |
| 43 | + const constrains = moveableRef.parentElement!.getBoundingClientRect(); |
42 | 44 |
|
43 | 45 | if (direction === 'right') { |
44 | 46 | const delta = e.pageX - mousePositionWhenResizingStart; |
|
53 | 55 | if (!width) { |
54 | 56 | const widthInPercentage = (interalWidth * 100) / constrains.width; |
55 | 57 |
|
56 | | - resizableRef.style.setProperty('width', widthInPercentage.toFixed(1) + '%'); |
| 58 | + moveableRef.style.setProperty('width', widthInPercentage.toFixed(1) + '%'); |
57 | 59 | } |
58 | 60 |
|
59 | 61 | dispatcher('resize', { |
60 | 62 | direction, |
61 | 63 | delta: deltaWidth, |
62 | | - refToElement: resizableRef |
| 64 | + refToElement: moveableRef |
63 | 65 | }); |
64 | 66 | } else if (direction === 'left') { |
65 | 67 | const delta = mousePositionWhenResizingStart - e.pageX; |
|
76 | 78 | const widthInPercentage = (internalWidth * 100) / constrains.width; |
77 | 79 | const leftInPercentage = Math.max(0, (internalLeft * 100) / constrains.width); |
78 | 80 |
|
79 | | - resizableRef.style.setProperty('width', widthInPercentage.toFixed(1) + '%'); |
80 | | - resizableRef.style.setProperty('left', leftInPercentage.toFixed(1) + '%'); |
| 81 | + moveableRef.style.setProperty('width', widthInPercentage.toFixed(1) + '%'); |
| 82 | + moveableRef.style.setProperty('left', leftInPercentage.toFixed(1) + '%'); |
81 | 83 | } |
82 | 84 |
|
83 | 85 | dispatcher('resize', { |
84 | 86 | direction, |
85 | 87 | delta: deltaWidth, |
86 | | - refToElement: resizableRef |
| 88 | + refToElement: moveableRef |
87 | 89 | }); |
88 | 90 | } |
89 | 91 | } |
90 | 92 |
|
91 | | - function handleResizeEnd() { |
| 93 | + function handleDragStart(e: MouseEvent) { |
| 94 | + isDragging = true; |
| 95 | + moveablePositionWhenDragStart = { |
| 96 | + top: e.clientY - moveableRef.offsetTop, |
| 97 | + left: e.clientX - moveableRef.offsetLeft |
| 98 | + }; |
| 99 | + dispatcher('dragStart'); |
| 100 | + } |
| 101 | +
|
| 102 | + function handleDrag(e: MouseEvent) { |
| 103 | + if (!isDragging) return; |
| 104 | + if (!moveablePositionWhenDragStart) return; |
| 105 | +
|
| 106 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
| 107 | + const left = e.clientX - moveablePositionWhenDragStart.left; |
| 108 | +
|
| 109 | + if (!left) { |
| 110 | + moveableRef.style.setProperty('left', left.toFixed(1) + 'px'); |
| 111 | + } |
| 112 | +
|
| 113 | + dispatcher('drag', { |
| 114 | + left, |
| 115 | + refToElement: moveableRef |
| 116 | + }); |
| 117 | + } |
| 118 | +
|
| 119 | + function handleEvents(e: MouseEvent) { |
| 120 | + if (isResizing) { |
| 121 | + handleResize(e); |
| 122 | + } else if (isDragging) { |
| 123 | + handleDrag(e); |
| 124 | + } |
| 125 | + } |
| 126 | +
|
| 127 | + function handleEnd() { |
92 | 128 | isResizing = false; |
| 129 | + isDragging = false; |
93 | 130 | direction = null; |
94 | 131 | dispatcher('resizeEnd'); |
| 132 | + dispatcher('dragEnd'); |
95 | 133 | } |
96 | 134 |
|
97 | 135 | onMount(() => { |
98 | | - document.addEventListener('mousemove', handleResize); |
99 | | - document.addEventListener('mouseup', handleResizeEnd); |
| 136 | + document.addEventListener('mousemove', handleEvents); |
| 137 | + document.addEventListener('mouseup', handleEnd); |
100 | 138 |
|
101 | 139 | return () => { |
102 | | - document.removeEventListener('mousemove', handleResize); |
103 | | - document.removeEventListener('mouseup', handleResizeEnd); |
| 140 | + document.removeEventListener('mousemove', handleEvents); |
| 141 | + document.removeEventListener('mouseup', handleEnd); |
104 | 142 | }; |
105 | 143 | }); |
106 | 144 | </script> |
|
119 | 157 | <div |
120 | 158 | class={className.root} |
121 | 159 | style="width: {width ?? 100}%; left: {left ?? 0}%;" |
122 | | - bind:this={resizableRef} |
| 160 | + bind:this={moveableRef} |
| 161 | + on:mousedown={handleDragStart} |
123 | 162 | on:mouseenter={(e) => dispatcher('mouseenter', { e })} |
124 | 163 | on:mouseleave={(e) => dispatcher('mouseleave', { e })} |
125 | 164 | > |
126 | 165 | <div class="w-full h-full relative"> |
127 | 166 | <button |
128 | 167 | class="{className.handle ?? |
129 | 168 | 'min-w-5 h-full cursor-ew-resize absolute top-0 left-0'} {className.handleW ?? ''}" |
130 | | - on:mousedown={(e) => handleResizeStart(e, 'left')} |
| 169 | + on:mousedown|stopPropagation={(e) => handleResizeStart(e, 'left')} |
131 | 170 | > |
132 | 171 | <slot name="w" /> |
133 | 172 | </button> |
134 | 173 | <button |
135 | 174 | class="{className.handle ?? |
136 | 175 | 'min-w-5 h-full cursor-ew-resize absolute top-0 right-0'} {className.handleE ?? ''}" |
137 | | - on:mousedown={(e) => handleResizeStart(e, 'right')} |
| 176 | + on:mousedown|stopPropagation={(e) => handleResizeStart(e, 'right')} |
138 | 177 | > |
139 | 178 | <slot name="e" /> |
140 | 179 | </button> |
|
0 commit comments