ninegrid2 6.862.0 → 6.863.0
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.
- package/dist/bundle.cjs.js +162 -31
- package/dist/bundle.esm.js +162 -31
- package/dist/nx/nxSplitter.js +173 -31
- package/package.json +1 -1
- package/src/nx/nxSplitter.js +173 -31
package/dist/bundle.cjs.js
CHANGED
|
@@ -121167,17 +121167,15 @@ class nxSplitter extends HTMLElement {
|
|
|
121167
121167
|
this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
|
|
121168
121168
|
};
|
|
121169
121169
|
|
|
121170
|
-
// 이전 #startDrag_BAK 함수는 삭제하거나 주석 처리합니다.
|
|
121171
|
-
// #startDrag_BAK = (...) => { ... }
|
|
121172
|
-
|
|
121173
121170
|
#startDrag = (e) => {
|
|
121174
|
-
e.preventDefault();
|
|
121171
|
+
e.preventDefault();
|
|
121175
121172
|
e.stopPropagation();
|
|
121176
121173
|
|
|
121177
121174
|
const splitterRect = this.getBoundingClientRect();
|
|
121178
121175
|
const isHorizontal = this.#mode === "h";
|
|
121179
121176
|
|
|
121180
|
-
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
121177
|
+
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
121178
|
+
// 이 값은 최종 패널 사이즈 계산에 사용되며, 드래그 바 초기 위치와는 직접 관련 없음
|
|
121181
121179
|
isHorizontal
|
|
121182
121180
|
? e.clientX - splitterRect.left
|
|
121183
121181
|
: e.clientY - splitterRect.top;
|
|
@@ -121205,15 +121203,14 @@ class nxSplitter extends HTMLElement {
|
|
|
121205
121203
|
return;
|
|
121206
121204
|
}
|
|
121207
121205
|
|
|
121208
|
-
//
|
|
121206
|
+
// dragBar를 먼저 DOM에 추가하여 offsetParent를 확보
|
|
121209
121207
|
(parent.shadowRoot || parent).appendChild(dragBar);
|
|
121210
121208
|
|
|
121211
|
-
// dragBar의 실제 offsetParent (position: absolute의 기준이 되는 요소)
|
|
121212
121209
|
const dragBarOffsetParent = dragBar.offsetParent;
|
|
121213
121210
|
|
|
121214
121211
|
if (!dragBarOffsetParent) {
|
|
121215
121212
|
console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
|
|
121216
|
-
dragBar.remove();
|
|
121213
|
+
dragBar.remove();
|
|
121217
121214
|
return;
|
|
121218
121215
|
}
|
|
121219
121216
|
|
|
@@ -121221,7 +121218,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121221
121218
|
const prevRect = prev.getBoundingClientRect(); // 뷰포트 기준
|
|
121222
121219
|
const nextRect = next.getBoundingClientRect(); // 뷰포트 기준
|
|
121223
121220
|
|
|
121224
|
-
|
|
121225
121221
|
// 뷰포트 기준 클릭 위치를 dragBarOffsetParent 기준으로 변환하여 초기 dragBar 위치 설정
|
|
121226
121222
|
let initialPosInOffsetParent;
|
|
121227
121223
|
if (isHorizontal) {
|
|
@@ -121249,12 +121245,10 @@ class nxSplitter extends HTMLElement {
|
|
|
121249
121245
|
|
|
121250
121246
|
const onMove = moveEvent => {
|
|
121251
121247
|
const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
|
121252
|
-
// 뷰포트 기준 클릭 위치를 offsetParent 기준으로 변환
|
|
121253
121248
|
const currentPosInOffsetParent = isHorizontal
|
|
121254
121249
|
? clientPos - dragBarOffsetParentRect.left
|
|
121255
121250
|
: clientPos - dragBarOffsetParentRect.top;
|
|
121256
121251
|
|
|
121257
|
-
// 드래그 바가 최소/최대 범위 내에 있도록 클램프
|
|
121258
121252
|
const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
|
|
121259
121253
|
|
|
121260
121254
|
if (isHorizontal) {
|
|
@@ -121262,8 +121256,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121262
121256
|
} else {
|
|
121263
121257
|
dragBar.style.top = `${clampedPos}px`;
|
|
121264
121258
|
}
|
|
121265
|
-
|
|
121266
|
-
console.log(clampedPos);
|
|
121267
121259
|
};
|
|
121268
121260
|
|
|
121269
121261
|
const onUp = (upEvent) => {
|
|
@@ -121271,34 +121263,176 @@ class nxSplitter extends HTMLElement {
|
|
|
121271
121263
|
window.removeEventListener("mouseup", onUp);
|
|
121272
121264
|
dragBar.remove();
|
|
121273
121265
|
|
|
121274
|
-
// 최종 드래그 바의 위치를 offsetParent 기준으로 가져옴
|
|
121275
121266
|
const finalDragBarPosInOffsetParent = isHorizontal
|
|
121276
121267
|
? parseFloat(dragBar.style.left)
|
|
121277
121268
|
: parseFloat(dragBar.style.top);
|
|
121278
121269
|
|
|
121279
|
-
//
|
|
121280
|
-
const
|
|
121270
|
+
// ⭐⭐ prev와 next의 padding을 고려한 최종 사이즈 계산 ⭐⭐
|
|
121271
|
+
const prevComputedStyle = getComputedStyle(prev);
|
|
121272
|
+
const nextComputedStyle = getComputedStyle(next);
|
|
121281
121273
|
|
|
121282
|
-
|
|
121283
|
-
|
|
121284
|
-
|
|
121285
|
-
|
|
121286
|
-
|
|
121274
|
+
if (isHorizontal) {
|
|
121275
|
+
parseFloat(prevComputedStyle.paddingLeft) || 0;
|
|
121276
|
+
parseFloat(nextComputedStyle.paddingRight) || 0;
|
|
121277
|
+
} else {
|
|
121278
|
+
parseFloat(prevComputedStyle.paddingTop) || 0;
|
|
121279
|
+
parseFloat(nextComputedStyle.paddingBottom) || 0;
|
|
121280
|
+
}
|
|
121281
|
+
|
|
121282
|
+
// prev의 새로운 크기: dragBar의 위치 (offsetParent 기준) - prev의 padding-left/top
|
|
121283
|
+
// (prev의 content 시작점까지)
|
|
121284
|
+
finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121285
|
+
|
|
121286
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - next의 padding-right/bottom
|
|
121287
|
+
(dragBarOffsetParentRect[isHorizontal ? 'width' : 'height'] - finalDragBarPosInOffsetParent);
|
|
121288
|
+
|
|
121289
|
+
// `box-sizing`에 따라 `width`/`height` 설정 방식이 달라짐.
|
|
121290
|
+
// 대부분 `border-box`를 사용하므로, `width`를 설정하면 `padding`이 포함됨.
|
|
121291
|
+
// 따라서 `newPrevInnerSize`와 `newNextInnerSize`는 `padding`을 제외한 `content` 크기가 아니라
|
|
121292
|
+
// 최종적으로 `width`/`height`에 적용될 값이어야 함.
|
|
121293
|
+
// 즉, `padding`이 포함된 전체 `width`/`height`로 계산해야 함.
|
|
121294
|
+
|
|
121295
|
+
// 다시 생각해보면, finalDragBarPosInOffsetParent가 바로 prev의 새로운 width/height가 되어야 합니다.
|
|
121296
|
+
// 왜냐하면 dragBar의 위치가 prev의 끝이기 때문입니다.
|
|
121297
|
+
// 문제는 prev/next에 padding이 있을 때 이 크기를 어떻게 `style.width`/`height`에 반영할지입니다.
|
|
121298
|
+
|
|
121299
|
+
// 가장 일반적인 시나리오: flex-grow 사용 (flex 컨테이너에서 권장)
|
|
121300
|
+
// `prev`와 `next`에 `flex-grow`를 설정하고 `flex-basis: 0;` (혹은 `auto`)를 사용하면,
|
|
121301
|
+
// `flex-grow` 비율로 공간을 나눕니다.
|
|
121302
|
+
// 드래그는 이 `flex-grow` 비율을 조정하는 방식으로 구현하는 것이 가장 깔끔합니다.
|
|
121303
|
+
|
|
121304
|
+
// 현재 prev.style.flex = "none"; next.style.flex = "none"; 방식으로 직접 width/height를 조절하고 있으므로
|
|
121305
|
+
// 이 방식에 맞춰 padding을 고려합니다.
|
|
121306
|
+
// 즉, finalDragBarPosInOffsetParent는 offsetParent의 왼쪽/위쪽 경계부터 dragBar까지의 거리
|
|
121307
|
+
// 이는 곧 prev 요소의 새로운 overall (width/height)가 됩니다.
|
|
121308
|
+
|
|
121309
|
+
// 여기서 오차가 난다면, `prev`와 `next`의 `box-sizing`이 무엇인지 확인해야 합니다.
|
|
121310
|
+
// 만약 `box-sizing: content-box;`라면 `width = content + padding + border`.
|
|
121311
|
+
// 만약 `box-sizing: border-box;`라면 `width = content`.
|
|
121312
|
+
// 일반적으로 CSS Reset이나 프레임워크는 `border-box`를 사용하므로, 이 가정을 따릅니다.
|
|
121313
|
+
|
|
121314
|
+
// `border-box` 가정 시: finalDragBarPosInOffsetParent가 `prev`의 새로운 `width`가 됩니다.
|
|
121315
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 `width`가 됩니다.
|
|
121316
|
+
// `padding`은 이 `width` 안에 포함됩니다.
|
|
121317
|
+
// 따라서 별도로 `padding` 값을 빼거나 더할 필요가 없습니다.
|
|
121318
|
+
// 만약 오차가 있다면, `prev` 또는 `next`에 `margin`이 적용되어 있거나,
|
|
121319
|
+
// `parent`에 `gap` 속성 등이 적용되어 있는 것은 아닌지 확인해봐야 합니다.
|
|
121320
|
+
|
|
121321
|
+
// 하지만 사용자께서 padding에서 오차가 난다고 하셨으므로,
|
|
121322
|
+
// `prev`/`next`의 `padding`이 `width`/`height` 계산에 영향을 주는 경우를 상정합니다.
|
|
121323
|
+
// 이 경우 `box-sizing: content-box;` 이거나,
|
|
121324
|
+
// `width`/`height`를 `content` 크기로 설정하고 싶을 때 발생합니다.
|
|
121325
|
+
|
|
121326
|
+
// **새로운 delta 계산 (prev의 최종 너비)**
|
|
121327
|
+
// dragBar의 최종 위치가 offsetParent 기준.
|
|
121328
|
+
// prev.getBoundingClientRect().left - dragBarOffsetParentRect.left : prev의 offsetParent 기준 왼쪽 시작점
|
|
121329
|
+
// clickOffset: 마우스 클릭 지점과 스플리터 바 왼쪽 경계의 차이.
|
|
121330
|
+
// 이 값은 드래그 바의 '컨텐츠' 시작점을 기준으로 한 오프셋으로 간주.
|
|
121331
|
+
|
|
121332
|
+
// 가장 간단한 해결책은 `getBoundingClientRect()`로 얻은 `prevRect.width`와 `nextRect.width`의 합을
|
|
121333
|
+
// `totalSize`로 사용하는 것이 아니라, `parent`의 실제 사용 가능한 공간을 기준으로 삼고,
|
|
121334
|
+
// 드래그 된 `dragBar`의 위치가 해당 공간의 몇 퍼센트를 차지하는지로 `prev`와 `next`의 새로운 `flex-grow`
|
|
121335
|
+
// 비율을 계산하는 것입니다.
|
|
121336
|
+
|
|
121337
|
+
// 현재처럼 직접 width/height를 조절하는 방식에서는
|
|
121338
|
+
// `finalDragBarPosInOffsetParent`가 prev의 새로운 `width`가 되는 것이 맞고,
|
|
121339
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 next의 새로운 `width`가 되는 것이 맞습니다.
|
|
121340
|
+
// 만약 padding 때문에 오차가 있다면, 이는 `box-sizing`의 문제이거나,
|
|
121341
|
+
// `getBoundingClientRect()`가 `margin`을 포함하지 않는다는 점 때문일 수 있습니다.
|
|
121342
|
+
|
|
121343
|
+
// **다시 한번 문제의 핵심을 짚어봅시다.**
|
|
121344
|
+
// "dragBar가 그만큼 왼쪽으로 그려져 padding 지우면 정상이야."
|
|
121345
|
+
// 이 문제는 **드래그 바의 초기 위치 문제**입니다. 이 부분은 이미 `dragBar.offsetParent`를 사용하여 해결했습니다.
|
|
121346
|
+
|
|
121347
|
+
// "prev, next가 padding 을 가졌을때는 오차가 있어."
|
|
121348
|
+
// 이 문제는 **최종 `width`/`height`를 `prev`와 `next`에 적용할 때의 오차**입니다.
|
|
121349
|
+
// 즉, `finalPrevSize`와 `finalNextSize`를 계산하는 방식이 문제가 됩니다.
|
|
121350
|
+
|
|
121351
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 왼쪽/상단 끝에서 `dragBar`까지의 거리입니다.
|
|
121352
|
+
// 이 값이 `prev`의 새로운 너비/높이가 되어야 합니다.
|
|
121353
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 너비/높이가 되어야 합니다.
|
|
121354
|
+
|
|
121355
|
+
// 만약 `prev`/`next`가 `padding`을 가질 때 오차가 발생한다면,
|
|
121356
|
+
// 이는 `prev.style.width = \`${finalPrevSize}px\`;`를 적용할 때,
|
|
121357
|
+
// `finalPrevSize`가 `prev`의 `content + padding + border`를 포함하는 `box-sizing: border-box;`일 경우의 크기여야 하는데,
|
|
121358
|
+
// 실제 `prev` 요소의 `box-sizing`이 `content-box`인 경우 `padding`만큼 더 커지기 때문일 수 있습니다.
|
|
121359
|
+
|
|
121360
|
+
// CSS에서 `box-sizing: border-box;`를 모든 요소에 적용하는 것을 강력히 권장합니다.
|
|
121361
|
+
// 예: `* { box-sizing: border-box; }`
|
|
121362
|
+
|
|
121363
|
+
// 만약 `border-box`를 이미 사용하고 있고 여전히 오차가 있다면,
|
|
121364
|
+
// `dragBar`의 `width` (수평 스플리터의 경우) 또는 `height` (수직 스플리터의 경우)를 고려해야 합니다.
|
|
121365
|
+
// 스플리터 자체의 두께만큼 `prev`와 `next` 사이의 공간이 발생하기 때문입니다.
|
|
121366
|
+
|
|
121367
|
+
const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height; // 스플리터 바의 두께
|
|
121368
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - (스플리터 두께의 절반)
|
|
121369
|
+
offsetParentTotalSize - finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
121370
|
+
|
|
121371
|
+
// 스플리터 두께만큼 중앙에서 나누어주는 로직
|
|
121372
|
+
// prev의 끝점은 dragBar의 중앙이 되어야 함
|
|
121373
|
+
// 즉, finalDragBarPosInOffsetParent는 dragBar의 좌측 끝점.
|
|
121374
|
+
// prev의 새로운 크기는 이 dragBar 좌측 끝점에서 prev의 시작점까지의 거리.
|
|
121375
|
+
// 근데 prev의 실제 content box 끝은 dragBar의 중앙까지 와야 한다면,
|
|
121376
|
+
// prev의 끝점에서 dragBar 중앙까지의 거리를 계산.
|
|
121377
|
+
|
|
121378
|
+
// 다시, 드래그 위치 `relative`가 `parent` 기준 스플리터 바의 왼쪽/상단 경계라면
|
|
121379
|
+
// `prev`의 새로운 크기는 이 `relative` 값과 동일해야 합니다.
|
|
121380
|
+
// 그리고 `next`의 새로운 크기는 `parentTotalSize - relative - splitterThickness`가 되어야 합니다.
|
|
121381
|
+
// 여기서 `relative`는 `onMove`에서 계산된 `dragBar.style.left` 또는 `top` 값입니다.
|
|
121382
|
+
// `finalDragBarPosInOffsetParent`는 `relative`와 같은 의미.
|
|
121383
|
+
|
|
121384
|
+
// ⭐ `totalSize` (두 패널의 합계) 대신 `parent`의 사용 가능한 전체 공간을 기준으로
|
|
121385
|
+
// `finalPrevSize`와 `finalNextSize`를 다시 계산합니다.
|
|
121386
|
+
// 이 방식이 `padding`이나 `margin` 같은 외부 요소에 덜 민감합니다.
|
|
121387
|
+
|
|
121388
|
+
// `prev.style.width = Xpx`는 prev의 `content + padding + border`가 Xpx가 된다는 가정 (border-box)
|
|
121389
|
+
// `prev`의 시작점 (offsetParent 기준)
|
|
121390
|
+
const prevStartPosInOffsetParent = isHorizontal
|
|
121391
|
+
? prevRect.left - dragBarOffsetParentRect.left
|
|
121392
|
+
: prevRect.top - dragBarOffsetParentRect.top;
|
|
121393
|
+
|
|
121394
|
+
// `prev`의 새로운 크기 (스플리터 바의 위치까지)
|
|
121395
|
+
finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
|
|
121396
|
+
// `next`의 새로운 크기 (스플리터 바 이후부터 `offsetParent` 끝까지)
|
|
121397
|
+
// `offsetParentTotalSize`는 `offsetParent`의 전체 너비/높이
|
|
121398
|
+
finalNextSize = offsetParentTotalSize - (finalDragBarPosInOffsetParent + splitterThickness) + (prevStartPosInOffsetParent + finalPrevSize - nextRect[isHorizontal ? 'left' : 'top'] + dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121399
|
+
// 이 복잡한 계산은 prev와 next 사이의 간극 (스플리터 두께)을 정확히 반영하기 위함입니다.
|
|
121400
|
+
// `finalPrevSize + finalNextSize + splitterThickness`가 `offsetParentTotalSize`와 일치해야 합니다.
|
|
121401
|
+
|
|
121402
|
+
// ⭐ 최종적으로 계산된 크기:
|
|
121403
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 시작점에서 `dragBar`의 시작점까지의 거리.
|
|
121404
|
+
// `prev`의 새 너비는 `finalDragBarPosInOffsetParent` 그 자체가 아니라,
|
|
121405
|
+
// `dragBar`가 위치한 곳까지의 `prev`의 너비여야 합니다.
|
|
121406
|
+
// 즉, `finalDragBarPosInOffsetParent`는 `prev`의 끝점이 되는 위치.
|
|
121407
|
+
|
|
121408
|
+
// 다시 한번, `finalDragBarPosInOffsetParent`는 `offsetParent`의 0 지점에서 `dragBar`의 0 지점까지의 거리입니다.
|
|
121409
|
+
// `prev` 요소의 최종 너비는 이 `finalDragBarPosInOffsetParent`에서
|
|
121410
|
+
// `prev` 요소의 `offsetParent` 기준 시작 지점 (`prevRect.left - dragBarOffsetParentRect.left`)을 뺀 값입니다.
|
|
121411
|
+
// 즉, `prev`의 새로운 너비 = `dragBar`의 왼쪽 위치 - `prev`의 왼쪽 위치
|
|
121412
|
+
|
|
121413
|
+
// 그리고 `next`의 새로운 너비 = `next`의 오른쪽 위치 - `dragBar`의 오른쪽 위치
|
|
121414
|
+
|
|
121415
|
+
const newPrevWidthCalculated = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121416
|
+
const newNextWidthCalculated = (nextRect[isHorizontal ? 'right' : 'bottom'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']) - (finalDragBarPosInOffsetParent + splitterThickness);
|
|
121417
|
+
|
|
121418
|
+
|
|
121419
|
+
// 최소 크기 제약
|
|
121420
|
+
const MIN_PANEL_SIZE_PX = 1; // 1px 미만으로 작아지지 않도록
|
|
121421
|
+
finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, newPrevWidthCalculated);
|
|
121422
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, newNextWidthCalculated);
|
|
121423
|
+
|
|
121424
|
+
// 만약 `prev + next + splitter`의 합이 부모의 `content` 사이즈와 일치해야 한다면
|
|
121425
|
+
// `finalNextSize`를 `offsetParentTotalSize - finalPrevSize - splitterThickness`로 계산하는 것이 더 정확할 수 있습니다.
|
|
121426
|
+
// 이렇게 하면 오차가 마지막 패널에 몰아지지만, 합은 정확하게 유지됩니다.
|
|
121427
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
|
|
121287
121428
|
|
|
121288
|
-
const MIN_PANEL_SIZE = 1; // 패널 최소 크기
|
|
121289
|
-
let finalPrevSize = Math.max(MIN_PANEL_SIZE, newPrevSize);
|
|
121290
|
-
let finalNextSize = Math.max(MIN_PANEL_SIZE, newNextSize);
|
|
121291
121429
|
|
|
121292
|
-
// 기존 flex 값을 저장하고 복원
|
|
121293
121430
|
const originalPrevFlex = prev.style.flex;
|
|
121294
121431
|
const originalNextFlex = next.style.flex;
|
|
121295
121432
|
|
|
121296
|
-
// 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
|
|
121297
121433
|
prev.style.flex = "none";
|
|
121298
121434
|
next.style.flex = "none";
|
|
121299
121435
|
|
|
121300
|
-
console.log(finalPrevSize, finalNextSize);
|
|
121301
|
-
|
|
121302
121436
|
if (isHorizontal) {
|
|
121303
121437
|
prev.style.width = `${finalPrevSize}px`;
|
|
121304
121438
|
next.style.width = `${finalNextSize}px`;
|
|
@@ -121307,9 +121441,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121307
121441
|
next.style.height = `${finalNextSize}px`;
|
|
121308
121442
|
}
|
|
121309
121443
|
|
|
121310
|
-
// 작업 완료 후 원래 flex 값으로 복원
|
|
121311
|
-
// 주의: width/height가 설정된 상태에서 flex를 복원하면 충돌할 수 있음.
|
|
121312
|
-
// flex-basis나 flex-grow/shrink를 적절히 조절하는 것이 더 견고한 방법.
|
|
121313
121444
|
prev.style.flex = originalPrevFlex;
|
|
121314
121445
|
next.style.flex = originalNextFlex;
|
|
121315
121446
|
};
|
package/dist/bundle.esm.js
CHANGED
|
@@ -121163,17 +121163,15 @@ class nxSplitter extends HTMLElement {
|
|
|
121163
121163
|
this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
|
|
121164
121164
|
};
|
|
121165
121165
|
|
|
121166
|
-
// 이전 #startDrag_BAK 함수는 삭제하거나 주석 처리합니다.
|
|
121167
|
-
// #startDrag_BAK = (...) => { ... }
|
|
121168
|
-
|
|
121169
121166
|
#startDrag = (e) => {
|
|
121170
|
-
e.preventDefault();
|
|
121167
|
+
e.preventDefault();
|
|
121171
121168
|
e.stopPropagation();
|
|
121172
121169
|
|
|
121173
121170
|
const splitterRect = this.getBoundingClientRect();
|
|
121174
121171
|
const isHorizontal = this.#mode === "h";
|
|
121175
121172
|
|
|
121176
|
-
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
121173
|
+
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
121174
|
+
// 이 값은 최종 패널 사이즈 계산에 사용되며, 드래그 바 초기 위치와는 직접 관련 없음
|
|
121177
121175
|
isHorizontal
|
|
121178
121176
|
? e.clientX - splitterRect.left
|
|
121179
121177
|
: e.clientY - splitterRect.top;
|
|
@@ -121201,15 +121199,14 @@ class nxSplitter extends HTMLElement {
|
|
|
121201
121199
|
return;
|
|
121202
121200
|
}
|
|
121203
121201
|
|
|
121204
|
-
//
|
|
121202
|
+
// dragBar를 먼저 DOM에 추가하여 offsetParent를 확보
|
|
121205
121203
|
(parent.shadowRoot || parent).appendChild(dragBar);
|
|
121206
121204
|
|
|
121207
|
-
// dragBar의 실제 offsetParent (position: absolute의 기준이 되는 요소)
|
|
121208
121205
|
const dragBarOffsetParent = dragBar.offsetParent;
|
|
121209
121206
|
|
|
121210
121207
|
if (!dragBarOffsetParent) {
|
|
121211
121208
|
console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
|
|
121212
|
-
dragBar.remove();
|
|
121209
|
+
dragBar.remove();
|
|
121213
121210
|
return;
|
|
121214
121211
|
}
|
|
121215
121212
|
|
|
@@ -121217,7 +121214,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121217
121214
|
const prevRect = prev.getBoundingClientRect(); // 뷰포트 기준
|
|
121218
121215
|
const nextRect = next.getBoundingClientRect(); // 뷰포트 기준
|
|
121219
121216
|
|
|
121220
|
-
|
|
121221
121217
|
// 뷰포트 기준 클릭 위치를 dragBarOffsetParent 기준으로 변환하여 초기 dragBar 위치 설정
|
|
121222
121218
|
let initialPosInOffsetParent;
|
|
121223
121219
|
if (isHorizontal) {
|
|
@@ -121245,12 +121241,10 @@ class nxSplitter extends HTMLElement {
|
|
|
121245
121241
|
|
|
121246
121242
|
const onMove = moveEvent => {
|
|
121247
121243
|
const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
|
121248
|
-
// 뷰포트 기준 클릭 위치를 offsetParent 기준으로 변환
|
|
121249
121244
|
const currentPosInOffsetParent = isHorizontal
|
|
121250
121245
|
? clientPos - dragBarOffsetParentRect.left
|
|
121251
121246
|
: clientPos - dragBarOffsetParentRect.top;
|
|
121252
121247
|
|
|
121253
|
-
// 드래그 바가 최소/최대 범위 내에 있도록 클램프
|
|
121254
121248
|
const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
|
|
121255
121249
|
|
|
121256
121250
|
if (isHorizontal) {
|
|
@@ -121258,8 +121252,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121258
121252
|
} else {
|
|
121259
121253
|
dragBar.style.top = `${clampedPos}px`;
|
|
121260
121254
|
}
|
|
121261
|
-
|
|
121262
|
-
console.log(clampedPos);
|
|
121263
121255
|
};
|
|
121264
121256
|
|
|
121265
121257
|
const onUp = (upEvent) => {
|
|
@@ -121267,34 +121259,176 @@ class nxSplitter extends HTMLElement {
|
|
|
121267
121259
|
window.removeEventListener("mouseup", onUp);
|
|
121268
121260
|
dragBar.remove();
|
|
121269
121261
|
|
|
121270
|
-
// 최종 드래그 바의 위치를 offsetParent 기준으로 가져옴
|
|
121271
121262
|
const finalDragBarPosInOffsetParent = isHorizontal
|
|
121272
121263
|
? parseFloat(dragBar.style.left)
|
|
121273
121264
|
: parseFloat(dragBar.style.top);
|
|
121274
121265
|
|
|
121275
|
-
//
|
|
121276
|
-
const
|
|
121266
|
+
// ⭐⭐ prev와 next의 padding을 고려한 최종 사이즈 계산 ⭐⭐
|
|
121267
|
+
const prevComputedStyle = getComputedStyle(prev);
|
|
121268
|
+
const nextComputedStyle = getComputedStyle(next);
|
|
121277
121269
|
|
|
121278
|
-
|
|
121279
|
-
|
|
121280
|
-
|
|
121281
|
-
|
|
121282
|
-
|
|
121270
|
+
if (isHorizontal) {
|
|
121271
|
+
parseFloat(prevComputedStyle.paddingLeft) || 0;
|
|
121272
|
+
parseFloat(nextComputedStyle.paddingRight) || 0;
|
|
121273
|
+
} else {
|
|
121274
|
+
parseFloat(prevComputedStyle.paddingTop) || 0;
|
|
121275
|
+
parseFloat(nextComputedStyle.paddingBottom) || 0;
|
|
121276
|
+
}
|
|
121277
|
+
|
|
121278
|
+
// prev의 새로운 크기: dragBar의 위치 (offsetParent 기준) - prev의 padding-left/top
|
|
121279
|
+
// (prev의 content 시작점까지)
|
|
121280
|
+
finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121281
|
+
|
|
121282
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - next의 padding-right/bottom
|
|
121283
|
+
(dragBarOffsetParentRect[isHorizontal ? 'width' : 'height'] - finalDragBarPosInOffsetParent);
|
|
121284
|
+
|
|
121285
|
+
// `box-sizing`에 따라 `width`/`height` 설정 방식이 달라짐.
|
|
121286
|
+
// 대부분 `border-box`를 사용하므로, `width`를 설정하면 `padding`이 포함됨.
|
|
121287
|
+
// 따라서 `newPrevInnerSize`와 `newNextInnerSize`는 `padding`을 제외한 `content` 크기가 아니라
|
|
121288
|
+
// 최종적으로 `width`/`height`에 적용될 값이어야 함.
|
|
121289
|
+
// 즉, `padding`이 포함된 전체 `width`/`height`로 계산해야 함.
|
|
121290
|
+
|
|
121291
|
+
// 다시 생각해보면, finalDragBarPosInOffsetParent가 바로 prev의 새로운 width/height가 되어야 합니다.
|
|
121292
|
+
// 왜냐하면 dragBar의 위치가 prev의 끝이기 때문입니다.
|
|
121293
|
+
// 문제는 prev/next에 padding이 있을 때 이 크기를 어떻게 `style.width`/`height`에 반영할지입니다.
|
|
121294
|
+
|
|
121295
|
+
// 가장 일반적인 시나리오: flex-grow 사용 (flex 컨테이너에서 권장)
|
|
121296
|
+
// `prev`와 `next`에 `flex-grow`를 설정하고 `flex-basis: 0;` (혹은 `auto`)를 사용하면,
|
|
121297
|
+
// `flex-grow` 비율로 공간을 나눕니다.
|
|
121298
|
+
// 드래그는 이 `flex-grow` 비율을 조정하는 방식으로 구현하는 것이 가장 깔끔합니다.
|
|
121299
|
+
|
|
121300
|
+
// 현재 prev.style.flex = "none"; next.style.flex = "none"; 방식으로 직접 width/height를 조절하고 있으므로
|
|
121301
|
+
// 이 방식에 맞춰 padding을 고려합니다.
|
|
121302
|
+
// 즉, finalDragBarPosInOffsetParent는 offsetParent의 왼쪽/위쪽 경계부터 dragBar까지의 거리
|
|
121303
|
+
// 이는 곧 prev 요소의 새로운 overall (width/height)가 됩니다.
|
|
121304
|
+
|
|
121305
|
+
// 여기서 오차가 난다면, `prev`와 `next`의 `box-sizing`이 무엇인지 확인해야 합니다.
|
|
121306
|
+
// 만약 `box-sizing: content-box;`라면 `width = content + padding + border`.
|
|
121307
|
+
// 만약 `box-sizing: border-box;`라면 `width = content`.
|
|
121308
|
+
// 일반적으로 CSS Reset이나 프레임워크는 `border-box`를 사용하므로, 이 가정을 따릅니다.
|
|
121309
|
+
|
|
121310
|
+
// `border-box` 가정 시: finalDragBarPosInOffsetParent가 `prev`의 새로운 `width`가 됩니다.
|
|
121311
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 `width`가 됩니다.
|
|
121312
|
+
// `padding`은 이 `width` 안에 포함됩니다.
|
|
121313
|
+
// 따라서 별도로 `padding` 값을 빼거나 더할 필요가 없습니다.
|
|
121314
|
+
// 만약 오차가 있다면, `prev` 또는 `next`에 `margin`이 적용되어 있거나,
|
|
121315
|
+
// `parent`에 `gap` 속성 등이 적용되어 있는 것은 아닌지 확인해봐야 합니다.
|
|
121316
|
+
|
|
121317
|
+
// 하지만 사용자께서 padding에서 오차가 난다고 하셨으므로,
|
|
121318
|
+
// `prev`/`next`의 `padding`이 `width`/`height` 계산에 영향을 주는 경우를 상정합니다.
|
|
121319
|
+
// 이 경우 `box-sizing: content-box;` 이거나,
|
|
121320
|
+
// `width`/`height`를 `content` 크기로 설정하고 싶을 때 발생합니다.
|
|
121321
|
+
|
|
121322
|
+
// **새로운 delta 계산 (prev의 최종 너비)**
|
|
121323
|
+
// dragBar의 최종 위치가 offsetParent 기준.
|
|
121324
|
+
// prev.getBoundingClientRect().left - dragBarOffsetParentRect.left : prev의 offsetParent 기준 왼쪽 시작점
|
|
121325
|
+
// clickOffset: 마우스 클릭 지점과 스플리터 바 왼쪽 경계의 차이.
|
|
121326
|
+
// 이 값은 드래그 바의 '컨텐츠' 시작점을 기준으로 한 오프셋으로 간주.
|
|
121327
|
+
|
|
121328
|
+
// 가장 간단한 해결책은 `getBoundingClientRect()`로 얻은 `prevRect.width`와 `nextRect.width`의 합을
|
|
121329
|
+
// `totalSize`로 사용하는 것이 아니라, `parent`의 실제 사용 가능한 공간을 기준으로 삼고,
|
|
121330
|
+
// 드래그 된 `dragBar`의 위치가 해당 공간의 몇 퍼센트를 차지하는지로 `prev`와 `next`의 새로운 `flex-grow`
|
|
121331
|
+
// 비율을 계산하는 것입니다.
|
|
121332
|
+
|
|
121333
|
+
// 현재처럼 직접 width/height를 조절하는 방식에서는
|
|
121334
|
+
// `finalDragBarPosInOffsetParent`가 prev의 새로운 `width`가 되는 것이 맞고,
|
|
121335
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 next의 새로운 `width`가 되는 것이 맞습니다.
|
|
121336
|
+
// 만약 padding 때문에 오차가 있다면, 이는 `box-sizing`의 문제이거나,
|
|
121337
|
+
// `getBoundingClientRect()`가 `margin`을 포함하지 않는다는 점 때문일 수 있습니다.
|
|
121338
|
+
|
|
121339
|
+
// **다시 한번 문제의 핵심을 짚어봅시다.**
|
|
121340
|
+
// "dragBar가 그만큼 왼쪽으로 그려져 padding 지우면 정상이야."
|
|
121341
|
+
// 이 문제는 **드래그 바의 초기 위치 문제**입니다. 이 부분은 이미 `dragBar.offsetParent`를 사용하여 해결했습니다.
|
|
121342
|
+
|
|
121343
|
+
// "prev, next가 padding 을 가졌을때는 오차가 있어."
|
|
121344
|
+
// 이 문제는 **최종 `width`/`height`를 `prev`와 `next`에 적용할 때의 오차**입니다.
|
|
121345
|
+
// 즉, `finalPrevSize`와 `finalNextSize`를 계산하는 방식이 문제가 됩니다.
|
|
121346
|
+
|
|
121347
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 왼쪽/상단 끝에서 `dragBar`까지의 거리입니다.
|
|
121348
|
+
// 이 값이 `prev`의 새로운 너비/높이가 되어야 합니다.
|
|
121349
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 너비/높이가 되어야 합니다.
|
|
121350
|
+
|
|
121351
|
+
// 만약 `prev`/`next`가 `padding`을 가질 때 오차가 발생한다면,
|
|
121352
|
+
// 이는 `prev.style.width = \`${finalPrevSize}px\`;`를 적용할 때,
|
|
121353
|
+
// `finalPrevSize`가 `prev`의 `content + padding + border`를 포함하는 `box-sizing: border-box;`일 경우의 크기여야 하는데,
|
|
121354
|
+
// 실제 `prev` 요소의 `box-sizing`이 `content-box`인 경우 `padding`만큼 더 커지기 때문일 수 있습니다.
|
|
121355
|
+
|
|
121356
|
+
// CSS에서 `box-sizing: border-box;`를 모든 요소에 적용하는 것을 강력히 권장합니다.
|
|
121357
|
+
// 예: `* { box-sizing: border-box; }`
|
|
121358
|
+
|
|
121359
|
+
// 만약 `border-box`를 이미 사용하고 있고 여전히 오차가 있다면,
|
|
121360
|
+
// `dragBar`의 `width` (수평 스플리터의 경우) 또는 `height` (수직 스플리터의 경우)를 고려해야 합니다.
|
|
121361
|
+
// 스플리터 자체의 두께만큼 `prev`와 `next` 사이의 공간이 발생하기 때문입니다.
|
|
121362
|
+
|
|
121363
|
+
const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height; // 스플리터 바의 두께
|
|
121364
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - (스플리터 두께의 절반)
|
|
121365
|
+
offsetParentTotalSize - finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
121366
|
+
|
|
121367
|
+
// 스플리터 두께만큼 중앙에서 나누어주는 로직
|
|
121368
|
+
// prev의 끝점은 dragBar의 중앙이 되어야 함
|
|
121369
|
+
// 즉, finalDragBarPosInOffsetParent는 dragBar의 좌측 끝점.
|
|
121370
|
+
// prev의 새로운 크기는 이 dragBar 좌측 끝점에서 prev의 시작점까지의 거리.
|
|
121371
|
+
// 근데 prev의 실제 content box 끝은 dragBar의 중앙까지 와야 한다면,
|
|
121372
|
+
// prev의 끝점에서 dragBar 중앙까지의 거리를 계산.
|
|
121373
|
+
|
|
121374
|
+
// 다시, 드래그 위치 `relative`가 `parent` 기준 스플리터 바의 왼쪽/상단 경계라면
|
|
121375
|
+
// `prev`의 새로운 크기는 이 `relative` 값과 동일해야 합니다.
|
|
121376
|
+
// 그리고 `next`의 새로운 크기는 `parentTotalSize - relative - splitterThickness`가 되어야 합니다.
|
|
121377
|
+
// 여기서 `relative`는 `onMove`에서 계산된 `dragBar.style.left` 또는 `top` 값입니다.
|
|
121378
|
+
// `finalDragBarPosInOffsetParent`는 `relative`와 같은 의미.
|
|
121379
|
+
|
|
121380
|
+
// ⭐ `totalSize` (두 패널의 합계) 대신 `parent`의 사용 가능한 전체 공간을 기준으로
|
|
121381
|
+
// `finalPrevSize`와 `finalNextSize`를 다시 계산합니다.
|
|
121382
|
+
// 이 방식이 `padding`이나 `margin` 같은 외부 요소에 덜 민감합니다.
|
|
121383
|
+
|
|
121384
|
+
// `prev.style.width = Xpx`는 prev의 `content + padding + border`가 Xpx가 된다는 가정 (border-box)
|
|
121385
|
+
// `prev`의 시작점 (offsetParent 기준)
|
|
121386
|
+
const prevStartPosInOffsetParent = isHorizontal
|
|
121387
|
+
? prevRect.left - dragBarOffsetParentRect.left
|
|
121388
|
+
: prevRect.top - dragBarOffsetParentRect.top;
|
|
121389
|
+
|
|
121390
|
+
// `prev`의 새로운 크기 (스플리터 바의 위치까지)
|
|
121391
|
+
finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
|
|
121392
|
+
// `next`의 새로운 크기 (스플리터 바 이후부터 `offsetParent` 끝까지)
|
|
121393
|
+
// `offsetParentTotalSize`는 `offsetParent`의 전체 너비/높이
|
|
121394
|
+
finalNextSize = offsetParentTotalSize - (finalDragBarPosInOffsetParent + splitterThickness) + (prevStartPosInOffsetParent + finalPrevSize - nextRect[isHorizontal ? 'left' : 'top'] + dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121395
|
+
// 이 복잡한 계산은 prev와 next 사이의 간극 (스플리터 두께)을 정확히 반영하기 위함입니다.
|
|
121396
|
+
// `finalPrevSize + finalNextSize + splitterThickness`가 `offsetParentTotalSize`와 일치해야 합니다.
|
|
121397
|
+
|
|
121398
|
+
// ⭐ 최종적으로 계산된 크기:
|
|
121399
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 시작점에서 `dragBar`의 시작점까지의 거리.
|
|
121400
|
+
// `prev`의 새 너비는 `finalDragBarPosInOffsetParent` 그 자체가 아니라,
|
|
121401
|
+
// `dragBar`가 위치한 곳까지의 `prev`의 너비여야 합니다.
|
|
121402
|
+
// 즉, `finalDragBarPosInOffsetParent`는 `prev`의 끝점이 되는 위치.
|
|
121403
|
+
|
|
121404
|
+
// 다시 한번, `finalDragBarPosInOffsetParent`는 `offsetParent`의 0 지점에서 `dragBar`의 0 지점까지의 거리입니다.
|
|
121405
|
+
// `prev` 요소의 최종 너비는 이 `finalDragBarPosInOffsetParent`에서
|
|
121406
|
+
// `prev` 요소의 `offsetParent` 기준 시작 지점 (`prevRect.left - dragBarOffsetParentRect.left`)을 뺀 값입니다.
|
|
121407
|
+
// 즉, `prev`의 새로운 너비 = `dragBar`의 왼쪽 위치 - `prev`의 왼쪽 위치
|
|
121408
|
+
|
|
121409
|
+
// 그리고 `next`의 새로운 너비 = `next`의 오른쪽 위치 - `dragBar`의 오른쪽 위치
|
|
121410
|
+
|
|
121411
|
+
const newPrevWidthCalculated = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
121412
|
+
const newNextWidthCalculated = (nextRect[isHorizontal ? 'right' : 'bottom'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']) - (finalDragBarPosInOffsetParent + splitterThickness);
|
|
121413
|
+
|
|
121414
|
+
|
|
121415
|
+
// 최소 크기 제약
|
|
121416
|
+
const MIN_PANEL_SIZE_PX = 1; // 1px 미만으로 작아지지 않도록
|
|
121417
|
+
finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, newPrevWidthCalculated);
|
|
121418
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, newNextWidthCalculated);
|
|
121419
|
+
|
|
121420
|
+
// 만약 `prev + next + splitter`의 합이 부모의 `content` 사이즈와 일치해야 한다면
|
|
121421
|
+
// `finalNextSize`를 `offsetParentTotalSize - finalPrevSize - splitterThickness`로 계산하는 것이 더 정확할 수 있습니다.
|
|
121422
|
+
// 이렇게 하면 오차가 마지막 패널에 몰아지지만, 합은 정확하게 유지됩니다.
|
|
121423
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
|
|
121283
121424
|
|
|
121284
|
-
const MIN_PANEL_SIZE = 1; // 패널 최소 크기
|
|
121285
|
-
let finalPrevSize = Math.max(MIN_PANEL_SIZE, newPrevSize);
|
|
121286
|
-
let finalNextSize = Math.max(MIN_PANEL_SIZE, newNextSize);
|
|
121287
121425
|
|
|
121288
|
-
// 기존 flex 값을 저장하고 복원
|
|
121289
121426
|
const originalPrevFlex = prev.style.flex;
|
|
121290
121427
|
const originalNextFlex = next.style.flex;
|
|
121291
121428
|
|
|
121292
|
-
// 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
|
|
121293
121429
|
prev.style.flex = "none";
|
|
121294
121430
|
next.style.flex = "none";
|
|
121295
121431
|
|
|
121296
|
-
console.log(finalPrevSize, finalNextSize);
|
|
121297
|
-
|
|
121298
121432
|
if (isHorizontal) {
|
|
121299
121433
|
prev.style.width = `${finalPrevSize}px`;
|
|
121300
121434
|
next.style.width = `${finalNextSize}px`;
|
|
@@ -121303,9 +121437,6 @@ class nxSplitter extends HTMLElement {
|
|
|
121303
121437
|
next.style.height = `${finalNextSize}px`;
|
|
121304
121438
|
}
|
|
121305
121439
|
|
|
121306
|
-
// 작업 완료 후 원래 flex 값으로 복원
|
|
121307
|
-
// 주의: width/height가 설정된 상태에서 flex를 복원하면 충돌할 수 있음.
|
|
121308
|
-
// flex-basis나 flex-grow/shrink를 적절히 조절하는 것이 더 견고한 방법.
|
|
121309
121440
|
prev.style.flex = originalPrevFlex;
|
|
121310
121441
|
next.style.flex = originalNextFlex;
|
|
121311
121442
|
};
|
package/dist/nx/nxSplitter.js
CHANGED
|
@@ -22,17 +22,15 @@ class nxSplitter extends HTMLElement {
|
|
|
22
22
|
this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// 이전 #startDrag_BAK 함수는 삭제하거나 주석 처리합니다.
|
|
26
|
-
// #startDrag_BAK = (...) => { ... }
|
|
27
|
-
|
|
28
25
|
#startDrag = (e) => {
|
|
29
|
-
e.preventDefault();
|
|
26
|
+
e.preventDefault();
|
|
30
27
|
e.stopPropagation();
|
|
31
28
|
|
|
32
29
|
const splitterRect = this.getBoundingClientRect();
|
|
33
30
|
const isHorizontal = this.#mode === "h";
|
|
34
31
|
|
|
35
|
-
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
32
|
+
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
33
|
+
// 이 값은 최종 패널 사이즈 계산에 사용되며, 드래그 바 초기 위치와는 직접 관련 없음
|
|
36
34
|
const clickOffset = isHorizontal
|
|
37
35
|
? e.clientX - splitterRect.left
|
|
38
36
|
: e.clientY - splitterRect.top;
|
|
@@ -60,15 +58,14 @@ class nxSplitter extends HTMLElement {
|
|
|
60
58
|
return;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
//
|
|
61
|
+
// dragBar를 먼저 DOM에 추가하여 offsetParent를 확보
|
|
64
62
|
(parent.shadowRoot || parent).appendChild(dragBar);
|
|
65
63
|
|
|
66
|
-
// dragBar의 실제 offsetParent (position: absolute의 기준이 되는 요소)
|
|
67
64
|
const dragBarOffsetParent = dragBar.offsetParent;
|
|
68
65
|
|
|
69
66
|
if (!dragBarOffsetParent) {
|
|
70
67
|
console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
|
|
71
|
-
dragBar.remove();
|
|
68
|
+
dragBar.remove();
|
|
72
69
|
return;
|
|
73
70
|
}
|
|
74
71
|
|
|
@@ -76,7 +73,6 @@ class nxSplitter extends HTMLElement {
|
|
|
76
73
|
const prevRect = prev.getBoundingClientRect(); // 뷰포트 기준
|
|
77
74
|
const nextRect = next.getBoundingClientRect(); // 뷰포트 기준
|
|
78
75
|
|
|
79
|
-
|
|
80
76
|
// 뷰포트 기준 클릭 위치를 dragBarOffsetParent 기준으로 변환하여 초기 dragBar 위치 설정
|
|
81
77
|
let initialPosInOffsetParent;
|
|
82
78
|
if (isHorizontal) {
|
|
@@ -104,12 +100,10 @@ class nxSplitter extends HTMLElement {
|
|
|
104
100
|
|
|
105
101
|
const onMove = moveEvent => {
|
|
106
102
|
const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
|
107
|
-
// 뷰포트 기준 클릭 위치를 offsetParent 기준으로 변환
|
|
108
103
|
const currentPosInOffsetParent = isHorizontal
|
|
109
104
|
? clientPos - dragBarOffsetParentRect.left
|
|
110
105
|
: clientPos - dragBarOffsetParentRect.top;
|
|
111
106
|
|
|
112
|
-
// 드래그 바가 최소/최대 범위 내에 있도록 클램프
|
|
113
107
|
const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
|
|
114
108
|
|
|
115
109
|
if (isHorizontal) {
|
|
@@ -117,8 +111,6 @@ class nxSplitter extends HTMLElement {
|
|
|
117
111
|
} else {
|
|
118
112
|
dragBar.style.top = `${clampedPos}px`;
|
|
119
113
|
}
|
|
120
|
-
|
|
121
|
-
console.log(clampedPos);
|
|
122
114
|
};
|
|
123
115
|
|
|
124
116
|
const onUp = (upEvent) => {
|
|
@@ -126,34 +118,187 @@ class nxSplitter extends HTMLElement {
|
|
|
126
118
|
window.removeEventListener("mouseup", onUp);
|
|
127
119
|
dragBar.remove();
|
|
128
120
|
|
|
129
|
-
// 최종 드래그 바의 위치를 offsetParent 기준으로 가져옴
|
|
130
121
|
const finalDragBarPosInOffsetParent = isHorizontal
|
|
131
122
|
? parseFloat(dragBar.style.left)
|
|
132
123
|
: parseFloat(dragBar.style.top);
|
|
133
124
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
125
|
+
// ⭐⭐ prev와 next의 padding을 고려한 최종 사이즈 계산 ⭐⭐
|
|
126
|
+
const prevComputedStyle = getComputedStyle(prev);
|
|
127
|
+
const nextComputedStyle = getComputedStyle(next);
|
|
128
|
+
|
|
129
|
+
let prevPaddingStart = 0; // padding-left 또는 padding-top
|
|
130
|
+
let nextPaddingEnd = 0; // padding-right 또는 padding-bottom
|
|
136
131
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
if (isHorizontal) {
|
|
133
|
+
prevPaddingStart = parseFloat(prevComputedStyle.paddingLeft) || 0;
|
|
134
|
+
nextPaddingEnd = parseFloat(nextComputedStyle.paddingRight) || 0;
|
|
135
|
+
} else {
|
|
136
|
+
prevPaddingStart = parseFloat(prevComputedStyle.paddingTop) || 0;
|
|
137
|
+
nextPaddingEnd = parseFloat(nextComputedStyle.paddingBottom) || 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// prev의 새로운 크기: dragBar의 위치 (offsetParent 기준) - prev의 padding-left/top
|
|
141
|
+
// (prev의 content 시작점까지)
|
|
142
|
+
let newPrevInnerSize = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
143
|
+
newPrevInnerSize -= prevPaddingStart; // prev의 padding-left/top을 제외
|
|
144
|
+
|
|
145
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - next의 padding-right/bottom
|
|
146
|
+
let newNextInnerSize = (dragBarOffsetParentRect[isHorizontal ? 'width' : 'height'] - finalDragBarPosInOffsetParent);
|
|
147
|
+
newNextInnerSize -= nextPaddingEnd; // next의 padding-right/bottom을 제외
|
|
148
|
+
|
|
149
|
+
// 최종 패널 크기에 MIN_PANEL_SIZE 적용 (padding이 적용된 후의 최소 크기)
|
|
150
|
+
const MIN_PANEL_SIZE = 1;
|
|
151
|
+
|
|
152
|
+
// `box-sizing`에 따라 `width`/`height` 설정 방식이 달라짐.
|
|
153
|
+
// 대부분 `border-box`를 사용하므로, `width`를 설정하면 `padding`이 포함됨.
|
|
154
|
+
// 따라서 `newPrevInnerSize`와 `newNextInnerSize`는 `padding`을 제외한 `content` 크기가 아니라
|
|
155
|
+
// 최종적으로 `width`/`height`에 적용될 값이어야 함.
|
|
156
|
+
// 즉, `padding`이 포함된 전체 `width`/`height`로 계산해야 함.
|
|
157
|
+
|
|
158
|
+
// 다시 생각해보면, finalDragBarPosInOffsetParent가 바로 prev의 새로운 width/height가 되어야 합니다.
|
|
159
|
+
// 왜냐하면 dragBar의 위치가 prev의 끝이기 때문입니다.
|
|
160
|
+
// 문제는 prev/next에 padding이 있을 때 이 크기를 어떻게 `style.width`/`height`에 반영할지입니다.
|
|
161
|
+
|
|
162
|
+
// 가장 일반적인 시나리오: flex-grow 사용 (flex 컨테이너에서 권장)
|
|
163
|
+
// `prev`와 `next`에 `flex-grow`를 설정하고 `flex-basis: 0;` (혹은 `auto`)를 사용하면,
|
|
164
|
+
// `flex-grow` 비율로 공간을 나눕니다.
|
|
165
|
+
// 드래그는 이 `flex-grow` 비율을 조정하는 방식으로 구현하는 것이 가장 깔끔합니다.
|
|
166
|
+
|
|
167
|
+
// 현재 prev.style.flex = "none"; next.style.flex = "none"; 방식으로 직접 width/height를 조절하고 있으므로
|
|
168
|
+
// 이 방식에 맞춰 padding을 고려합니다.
|
|
169
|
+
// 즉, finalDragBarPosInOffsetParent는 offsetParent의 왼쪽/위쪽 경계부터 dragBar까지의 거리
|
|
170
|
+
// 이는 곧 prev 요소의 새로운 overall (width/height)가 됩니다.
|
|
171
|
+
|
|
172
|
+
// 여기서 오차가 난다면, `prev`와 `next`의 `box-sizing`이 무엇인지 확인해야 합니다.
|
|
173
|
+
// 만약 `box-sizing: content-box;`라면 `width = content + padding + border`.
|
|
174
|
+
// 만약 `box-sizing: border-box;`라면 `width = content`.
|
|
175
|
+
// 일반적으로 CSS Reset이나 프레임워크는 `border-box`를 사용하므로, 이 가정을 따릅니다.
|
|
176
|
+
|
|
177
|
+
// `border-box` 가정 시: finalDragBarPosInOffsetParent가 `prev`의 새로운 `width`가 됩니다.
|
|
178
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 `width`가 됩니다.
|
|
179
|
+
// `padding`은 이 `width` 안에 포함됩니다.
|
|
180
|
+
// 따라서 별도로 `padding` 값을 빼거나 더할 필요가 없습니다.
|
|
181
|
+
// 만약 오차가 있다면, `prev` 또는 `next`에 `margin`이 적용되어 있거나,
|
|
182
|
+
// `parent`에 `gap` 속성 등이 적용되어 있는 것은 아닌지 확인해봐야 합니다.
|
|
183
|
+
|
|
184
|
+
// 하지만 사용자께서 padding에서 오차가 난다고 하셨으므로,
|
|
185
|
+
// `prev`/`next`의 `padding`이 `width`/`height` 계산에 영향을 주는 경우를 상정합니다.
|
|
186
|
+
// 이 경우 `box-sizing: content-box;` 이거나,
|
|
187
|
+
// `width`/`height`를 `content` 크기로 설정하고 싶을 때 발생합니다.
|
|
188
|
+
|
|
189
|
+
// **새로운 delta 계산 (prev의 최종 너비)**
|
|
190
|
+
// dragBar의 최종 위치가 offsetParent 기준.
|
|
191
|
+
// prev.getBoundingClientRect().left - dragBarOffsetParentRect.left : prev의 offsetParent 기준 왼쪽 시작점
|
|
192
|
+
// clickOffset: 마우스 클릭 지점과 스플리터 바 왼쪽 경계의 차이.
|
|
193
|
+
// 이 값은 드래그 바의 '컨텐츠' 시작점을 기준으로 한 오프셋으로 간주.
|
|
194
|
+
|
|
195
|
+
// 가장 간단한 해결책은 `getBoundingClientRect()`로 얻은 `prevRect.width`와 `nextRect.width`의 합을
|
|
196
|
+
// `totalSize`로 사용하는 것이 아니라, `parent`의 실제 사용 가능한 공간을 기준으로 삼고,
|
|
197
|
+
// 드래그 된 `dragBar`의 위치가 해당 공간의 몇 퍼센트를 차지하는지로 `prev`와 `next`의 새로운 `flex-grow`
|
|
198
|
+
// 비율을 계산하는 것입니다.
|
|
199
|
+
|
|
200
|
+
// 현재처럼 직접 width/height를 조절하는 방식에서는
|
|
201
|
+
// `finalDragBarPosInOffsetParent`가 prev의 새로운 `width`가 되는 것이 맞고,
|
|
202
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 next의 새로운 `width`가 되는 것이 맞습니다.
|
|
203
|
+
// 만약 padding 때문에 오차가 있다면, 이는 `box-sizing`의 문제이거나,
|
|
204
|
+
// `getBoundingClientRect()`가 `margin`을 포함하지 않는다는 점 때문일 수 있습니다.
|
|
205
|
+
|
|
206
|
+
// **다시 한번 문제의 핵심을 짚어봅시다.**
|
|
207
|
+
// "dragBar가 그만큼 왼쪽으로 그려져 padding 지우면 정상이야."
|
|
208
|
+
// 이 문제는 **드래그 바의 초기 위치 문제**입니다. 이 부분은 이미 `dragBar.offsetParent`를 사용하여 해결했습니다.
|
|
209
|
+
|
|
210
|
+
// "prev, next가 padding 을 가졌을때는 오차가 있어."
|
|
211
|
+
// 이 문제는 **최종 `width`/`height`를 `prev`와 `next`에 적용할 때의 오차**입니다.
|
|
212
|
+
// 즉, `finalPrevSize`와 `finalNextSize`를 계산하는 방식이 문제가 됩니다.
|
|
213
|
+
|
|
214
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 왼쪽/상단 끝에서 `dragBar`까지의 거리입니다.
|
|
215
|
+
// 이 값이 `prev`의 새로운 너비/높이가 되어야 합니다.
|
|
216
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 너비/높이가 되어야 합니다.
|
|
217
|
+
|
|
218
|
+
// 만약 `prev`/`next`가 `padding`을 가질 때 오차가 발생한다면,
|
|
219
|
+
// 이는 `prev.style.width = \`${finalPrevSize}px\`;`를 적용할 때,
|
|
220
|
+
// `finalPrevSize`가 `prev`의 `content + padding + border`를 포함하는 `box-sizing: border-box;`일 경우의 크기여야 하는데,
|
|
221
|
+
// 실제 `prev` 요소의 `box-sizing`이 `content-box`인 경우 `padding`만큼 더 커지기 때문일 수 있습니다.
|
|
222
|
+
|
|
223
|
+
// CSS에서 `box-sizing: border-box;`를 모든 요소에 적용하는 것을 강력히 권장합니다.
|
|
224
|
+
// 예: `* { box-sizing: border-box; }`
|
|
225
|
+
|
|
226
|
+
// 만약 `border-box`를 이미 사용하고 있고 여전히 오차가 있다면,
|
|
227
|
+
// `dragBar`의 `width` (수평 스플리터의 경우) 또는 `height` (수직 스플리터의 경우)를 고려해야 합니다.
|
|
228
|
+
// 스플리터 자체의 두께만큼 `prev`와 `next` 사이의 공간이 발생하기 때문입니다.
|
|
229
|
+
|
|
230
|
+
const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height; // 스플리터 바의 두께
|
|
231
|
+
|
|
232
|
+
// prev의 새로운 크기: dragBar 위치 - (스플리터 두께의 절반)
|
|
233
|
+
let newPrevWidth = finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
234
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - (스플리터 두께의 절반)
|
|
235
|
+
let newNextWidth = offsetParentTotalSize - finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
236
|
+
|
|
237
|
+
// 스플리터 두께만큼 중앙에서 나누어주는 로직
|
|
238
|
+
// prev의 끝점은 dragBar의 중앙이 되어야 함
|
|
239
|
+
// 즉, finalDragBarPosInOffsetParent는 dragBar의 좌측 끝점.
|
|
240
|
+
// prev의 새로운 크기는 이 dragBar 좌측 끝점에서 prev의 시작점까지의 거리.
|
|
241
|
+
// 근데 prev의 실제 content box 끝은 dragBar의 중앙까지 와야 한다면,
|
|
242
|
+
// prev의 끝점에서 dragBar 중앙까지의 거리를 계산.
|
|
243
|
+
|
|
244
|
+
// 다시, 드래그 위치 `relative`가 `parent` 기준 스플리터 바의 왼쪽/상단 경계라면
|
|
245
|
+
// `prev`의 새로운 크기는 이 `relative` 값과 동일해야 합니다.
|
|
246
|
+
// 그리고 `next`의 새로운 크기는 `parentTotalSize - relative - splitterThickness`가 되어야 합니다.
|
|
247
|
+
// 여기서 `relative`는 `onMove`에서 계산된 `dragBar.style.left` 또는 `top` 값입니다.
|
|
248
|
+
// `finalDragBarPosInOffsetParent`는 `relative`와 같은 의미.
|
|
249
|
+
|
|
250
|
+
// ⭐ `totalSize` (두 패널의 합계) 대신 `parent`의 사용 가능한 전체 공간을 기준으로
|
|
251
|
+
// `finalPrevSize`와 `finalNextSize`를 다시 계산합니다.
|
|
252
|
+
// 이 방식이 `padding`이나 `margin` 같은 외부 요소에 덜 민감합니다.
|
|
253
|
+
|
|
254
|
+
// `prev.style.width = Xpx`는 prev의 `content + padding + border`가 Xpx가 된다는 가정 (border-box)
|
|
255
|
+
// `prev`의 시작점 (offsetParent 기준)
|
|
256
|
+
const prevStartPosInOffsetParent = isHorizontal
|
|
257
|
+
? prevRect.left - dragBarOffsetParentRect.left
|
|
258
|
+
: prevRect.top - dragBarOffsetParentRect.top;
|
|
259
|
+
|
|
260
|
+
// `prev`의 새로운 크기 (스플리터 바의 위치까지)
|
|
261
|
+
finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
|
|
262
|
+
// `next`의 새로운 크기 (스플리터 바 이후부터 `offsetParent` 끝까지)
|
|
263
|
+
// `offsetParentTotalSize`는 `offsetParent`의 전체 너비/높이
|
|
264
|
+
finalNextSize = offsetParentTotalSize - (finalDragBarPosInOffsetParent + splitterThickness) + (prevStartPosInOffsetParent + finalPrevSize - nextRect[isHorizontal ? 'left' : 'top'] + dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
265
|
+
// 이 복잡한 계산은 prev와 next 사이의 간극 (스플리터 두께)을 정확히 반영하기 위함입니다.
|
|
266
|
+
// `finalPrevSize + finalNextSize + splitterThickness`가 `offsetParentTotalSize`와 일치해야 합니다.
|
|
267
|
+
|
|
268
|
+
// ⭐ 최종적으로 계산된 크기:
|
|
269
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 시작점에서 `dragBar`의 시작점까지의 거리.
|
|
270
|
+
// `prev`의 새 너비는 `finalDragBarPosInOffsetParent` 그 자체가 아니라,
|
|
271
|
+
// `dragBar`가 위치한 곳까지의 `prev`의 너비여야 합니다.
|
|
272
|
+
// 즉, `finalDragBarPosInOffsetParent`는 `prev`의 끝점이 되는 위치.
|
|
273
|
+
|
|
274
|
+
// 다시 한번, `finalDragBarPosInOffsetParent`는 `offsetParent`의 0 지점에서 `dragBar`의 0 지점까지의 거리입니다.
|
|
275
|
+
// `prev` 요소의 최종 너비는 이 `finalDragBarPosInOffsetParent`에서
|
|
276
|
+
// `prev` 요소의 `offsetParent` 기준 시작 지점 (`prevRect.left - dragBarOffsetParentRect.left`)을 뺀 값입니다.
|
|
277
|
+
// 즉, `prev`의 새로운 너비 = `dragBar`의 왼쪽 위치 - `prev`의 왼쪽 위치
|
|
278
|
+
|
|
279
|
+
// 그리고 `next`의 새로운 너비 = `next`의 오른쪽 위치 - `dragBar`의 오른쪽 위치
|
|
280
|
+
|
|
281
|
+
const newPrevWidthCalculated = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
282
|
+
const newNextWidthCalculated = (nextRect[isHorizontal ? 'right' : 'bottom'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']) - (finalDragBarPosInOffsetParent + splitterThickness);
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
// 최소 크기 제약
|
|
286
|
+
const MIN_PANEL_SIZE_PX = 1; // 1px 미만으로 작아지지 않도록
|
|
287
|
+
finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, newPrevWidthCalculated);
|
|
288
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, newNextWidthCalculated);
|
|
289
|
+
|
|
290
|
+
// 만약 `prev + next + splitter`의 합이 부모의 `content` 사이즈와 일치해야 한다면
|
|
291
|
+
// `finalNextSize`를 `offsetParentTotalSize - finalPrevSize - splitterThickness`로 계산하는 것이 더 정확할 수 있습니다.
|
|
292
|
+
// 이렇게 하면 오차가 마지막 패널에 몰아지지만, 합은 정확하게 유지됩니다.
|
|
293
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
|
|
142
294
|
|
|
143
|
-
const MIN_PANEL_SIZE = 1; // 패널 최소 크기
|
|
144
|
-
let finalPrevSize = Math.max(MIN_PANEL_SIZE, newPrevSize);
|
|
145
|
-
let finalNextSize = Math.max(MIN_PANEL_SIZE, newNextSize);
|
|
146
295
|
|
|
147
|
-
// 기존 flex 값을 저장하고 복원
|
|
148
296
|
const originalPrevFlex = prev.style.flex;
|
|
149
297
|
const originalNextFlex = next.style.flex;
|
|
150
298
|
|
|
151
|
-
// 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
|
|
152
299
|
prev.style.flex = "none";
|
|
153
300
|
next.style.flex = "none";
|
|
154
301
|
|
|
155
|
-
console.log(finalPrevSize, finalNextSize);
|
|
156
|
-
|
|
157
302
|
if (isHorizontal) {
|
|
158
303
|
prev.style.width = `${finalPrevSize}px`;
|
|
159
304
|
next.style.width = `${finalNextSize}px`;
|
|
@@ -162,9 +307,6 @@ class nxSplitter extends HTMLElement {
|
|
|
162
307
|
next.style.height = `${finalNextSize}px`;
|
|
163
308
|
}
|
|
164
309
|
|
|
165
|
-
// 작업 완료 후 원래 flex 값으로 복원
|
|
166
|
-
// 주의: width/height가 설정된 상태에서 flex를 복원하면 충돌할 수 있음.
|
|
167
|
-
// flex-basis나 flex-grow/shrink를 적절히 조절하는 것이 더 견고한 방법.
|
|
168
310
|
prev.style.flex = originalPrevFlex;
|
|
169
311
|
next.style.flex = originalNextFlex;
|
|
170
312
|
};
|
package/package.json
CHANGED
package/src/nx/nxSplitter.js
CHANGED
|
@@ -22,17 +22,15 @@ class nxSplitter extends HTMLElement {
|
|
|
22
22
|
this.#mode = (!prevRect || !nextRect) || (Math.abs(prevRect.top - nextRect.top) < 5) ? "h" : "v";
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// 이전 #startDrag_BAK 함수는 삭제하거나 주석 처리합니다.
|
|
26
|
-
// #startDrag_BAK = (...) => { ... }
|
|
27
|
-
|
|
28
25
|
#startDrag = (e) => {
|
|
29
|
-
e.preventDefault();
|
|
26
|
+
e.preventDefault();
|
|
30
27
|
e.stopPropagation();
|
|
31
28
|
|
|
32
29
|
const splitterRect = this.getBoundingClientRect();
|
|
33
30
|
const isHorizontal = this.#mode === "h";
|
|
34
31
|
|
|
35
|
-
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
32
|
+
// 클릭 지점에서 스플리터 경계까지의 오프셋
|
|
33
|
+
// 이 값은 최종 패널 사이즈 계산에 사용되며, 드래그 바 초기 위치와는 직접 관련 없음
|
|
36
34
|
const clickOffset = isHorizontal
|
|
37
35
|
? e.clientX - splitterRect.left
|
|
38
36
|
: e.clientY - splitterRect.top;
|
|
@@ -60,15 +58,14 @@ class nxSplitter extends HTMLElement {
|
|
|
60
58
|
return;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
//
|
|
61
|
+
// dragBar를 먼저 DOM에 추가하여 offsetParent를 확보
|
|
64
62
|
(parent.shadowRoot || parent).appendChild(dragBar);
|
|
65
63
|
|
|
66
|
-
// dragBar의 실제 offsetParent (position: absolute의 기준이 되는 요소)
|
|
67
64
|
const dragBarOffsetParent = dragBar.offsetParent;
|
|
68
65
|
|
|
69
66
|
if (!dragBarOffsetParent) {
|
|
70
67
|
console.error("dragBar's offsetParent could not be determined. Ensure parent or an ancestor has a position property (e.g., relative).");
|
|
71
|
-
dragBar.remove();
|
|
68
|
+
dragBar.remove();
|
|
72
69
|
return;
|
|
73
70
|
}
|
|
74
71
|
|
|
@@ -76,7 +73,6 @@ class nxSplitter extends HTMLElement {
|
|
|
76
73
|
const prevRect = prev.getBoundingClientRect(); // 뷰포트 기준
|
|
77
74
|
const nextRect = next.getBoundingClientRect(); // 뷰포트 기준
|
|
78
75
|
|
|
79
|
-
|
|
80
76
|
// 뷰포트 기준 클릭 위치를 dragBarOffsetParent 기준으로 변환하여 초기 dragBar 위치 설정
|
|
81
77
|
let initialPosInOffsetParent;
|
|
82
78
|
if (isHorizontal) {
|
|
@@ -104,12 +100,10 @@ class nxSplitter extends HTMLElement {
|
|
|
104
100
|
|
|
105
101
|
const onMove = moveEvent => {
|
|
106
102
|
const clientPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY;
|
|
107
|
-
// 뷰포트 기준 클릭 위치를 offsetParent 기준으로 변환
|
|
108
103
|
const currentPosInOffsetParent = isHorizontal
|
|
109
104
|
? clientPos - dragBarOffsetParentRect.left
|
|
110
105
|
: clientPos - dragBarOffsetParentRect.top;
|
|
111
106
|
|
|
112
|
-
// 드래그 바가 최소/최대 범위 내에 있도록 클램프
|
|
113
107
|
const clampedPos = Math.max(minLimit, Math.min(currentPosInOffsetParent, maxLimit));
|
|
114
108
|
|
|
115
109
|
if (isHorizontal) {
|
|
@@ -117,8 +111,6 @@ class nxSplitter extends HTMLElement {
|
|
|
117
111
|
} else {
|
|
118
112
|
dragBar.style.top = `${clampedPos}px`;
|
|
119
113
|
}
|
|
120
|
-
|
|
121
|
-
console.log(clampedPos);
|
|
122
114
|
};
|
|
123
115
|
|
|
124
116
|
const onUp = (upEvent) => {
|
|
@@ -126,34 +118,187 @@ class nxSplitter extends HTMLElement {
|
|
|
126
118
|
window.removeEventListener("mouseup", onUp);
|
|
127
119
|
dragBar.remove();
|
|
128
120
|
|
|
129
|
-
// 최종 드래그 바의 위치를 offsetParent 기준으로 가져옴
|
|
130
121
|
const finalDragBarPosInOffsetParent = isHorizontal
|
|
131
122
|
? parseFloat(dragBar.style.left)
|
|
132
123
|
: parseFloat(dragBar.style.top);
|
|
133
124
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
125
|
+
// ⭐⭐ prev와 next의 padding을 고려한 최종 사이즈 계산 ⭐⭐
|
|
126
|
+
const prevComputedStyle = getComputedStyle(prev);
|
|
127
|
+
const nextComputedStyle = getComputedStyle(next);
|
|
128
|
+
|
|
129
|
+
let prevPaddingStart = 0; // padding-left 또는 padding-top
|
|
130
|
+
let nextPaddingEnd = 0; // padding-right 또는 padding-bottom
|
|
136
131
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
if (isHorizontal) {
|
|
133
|
+
prevPaddingStart = parseFloat(prevComputedStyle.paddingLeft) || 0;
|
|
134
|
+
nextPaddingEnd = parseFloat(nextComputedStyle.paddingRight) || 0;
|
|
135
|
+
} else {
|
|
136
|
+
prevPaddingStart = parseFloat(prevComputedStyle.paddingTop) || 0;
|
|
137
|
+
nextPaddingEnd = parseFloat(nextComputedStyle.paddingBottom) || 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// prev의 새로운 크기: dragBar의 위치 (offsetParent 기준) - prev의 padding-left/top
|
|
141
|
+
// (prev의 content 시작점까지)
|
|
142
|
+
let newPrevInnerSize = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
143
|
+
newPrevInnerSize -= prevPaddingStart; // prev의 padding-left/top을 제외
|
|
144
|
+
|
|
145
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - next의 padding-right/bottom
|
|
146
|
+
let newNextInnerSize = (dragBarOffsetParentRect[isHorizontal ? 'width' : 'height'] - finalDragBarPosInOffsetParent);
|
|
147
|
+
newNextInnerSize -= nextPaddingEnd; // next의 padding-right/bottom을 제외
|
|
148
|
+
|
|
149
|
+
// 최종 패널 크기에 MIN_PANEL_SIZE 적용 (padding이 적용된 후의 최소 크기)
|
|
150
|
+
const MIN_PANEL_SIZE = 1;
|
|
151
|
+
|
|
152
|
+
// `box-sizing`에 따라 `width`/`height` 설정 방식이 달라짐.
|
|
153
|
+
// 대부분 `border-box`를 사용하므로, `width`를 설정하면 `padding`이 포함됨.
|
|
154
|
+
// 따라서 `newPrevInnerSize`와 `newNextInnerSize`는 `padding`을 제외한 `content` 크기가 아니라
|
|
155
|
+
// 최종적으로 `width`/`height`에 적용될 값이어야 함.
|
|
156
|
+
// 즉, `padding`이 포함된 전체 `width`/`height`로 계산해야 함.
|
|
157
|
+
|
|
158
|
+
// 다시 생각해보면, finalDragBarPosInOffsetParent가 바로 prev의 새로운 width/height가 되어야 합니다.
|
|
159
|
+
// 왜냐하면 dragBar의 위치가 prev의 끝이기 때문입니다.
|
|
160
|
+
// 문제는 prev/next에 padding이 있을 때 이 크기를 어떻게 `style.width`/`height`에 반영할지입니다.
|
|
161
|
+
|
|
162
|
+
// 가장 일반적인 시나리오: flex-grow 사용 (flex 컨테이너에서 권장)
|
|
163
|
+
// `prev`와 `next`에 `flex-grow`를 설정하고 `flex-basis: 0;` (혹은 `auto`)를 사용하면,
|
|
164
|
+
// `flex-grow` 비율로 공간을 나눕니다.
|
|
165
|
+
// 드래그는 이 `flex-grow` 비율을 조정하는 방식으로 구현하는 것이 가장 깔끔합니다.
|
|
166
|
+
|
|
167
|
+
// 현재 prev.style.flex = "none"; next.style.flex = "none"; 방식으로 직접 width/height를 조절하고 있으므로
|
|
168
|
+
// 이 방식에 맞춰 padding을 고려합니다.
|
|
169
|
+
// 즉, finalDragBarPosInOffsetParent는 offsetParent의 왼쪽/위쪽 경계부터 dragBar까지의 거리
|
|
170
|
+
// 이는 곧 prev 요소의 새로운 overall (width/height)가 됩니다.
|
|
171
|
+
|
|
172
|
+
// 여기서 오차가 난다면, `prev`와 `next`의 `box-sizing`이 무엇인지 확인해야 합니다.
|
|
173
|
+
// 만약 `box-sizing: content-box;`라면 `width = content + padding + border`.
|
|
174
|
+
// 만약 `box-sizing: border-box;`라면 `width = content`.
|
|
175
|
+
// 일반적으로 CSS Reset이나 프레임워크는 `border-box`를 사용하므로, 이 가정을 따릅니다.
|
|
176
|
+
|
|
177
|
+
// `border-box` 가정 시: finalDragBarPosInOffsetParent가 `prev`의 새로운 `width`가 됩니다.
|
|
178
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 `width`가 됩니다.
|
|
179
|
+
// `padding`은 이 `width` 안에 포함됩니다.
|
|
180
|
+
// 따라서 별도로 `padding` 값을 빼거나 더할 필요가 없습니다.
|
|
181
|
+
// 만약 오차가 있다면, `prev` 또는 `next`에 `margin`이 적용되어 있거나,
|
|
182
|
+
// `parent`에 `gap` 속성 등이 적용되어 있는 것은 아닌지 확인해봐야 합니다.
|
|
183
|
+
|
|
184
|
+
// 하지만 사용자께서 padding에서 오차가 난다고 하셨으므로,
|
|
185
|
+
// `prev`/`next`의 `padding`이 `width`/`height` 계산에 영향을 주는 경우를 상정합니다.
|
|
186
|
+
// 이 경우 `box-sizing: content-box;` 이거나,
|
|
187
|
+
// `width`/`height`를 `content` 크기로 설정하고 싶을 때 발생합니다.
|
|
188
|
+
|
|
189
|
+
// **새로운 delta 계산 (prev의 최종 너비)**
|
|
190
|
+
// dragBar의 최종 위치가 offsetParent 기준.
|
|
191
|
+
// prev.getBoundingClientRect().left - dragBarOffsetParentRect.left : prev의 offsetParent 기준 왼쪽 시작점
|
|
192
|
+
// clickOffset: 마우스 클릭 지점과 스플리터 바 왼쪽 경계의 차이.
|
|
193
|
+
// 이 값은 드래그 바의 '컨텐츠' 시작점을 기준으로 한 오프셋으로 간주.
|
|
194
|
+
|
|
195
|
+
// 가장 간단한 해결책은 `getBoundingClientRect()`로 얻은 `prevRect.width`와 `nextRect.width`의 합을
|
|
196
|
+
// `totalSize`로 사용하는 것이 아니라, `parent`의 실제 사용 가능한 공간을 기준으로 삼고,
|
|
197
|
+
// 드래그 된 `dragBar`의 위치가 해당 공간의 몇 퍼센트를 차지하는지로 `prev`와 `next`의 새로운 `flex-grow`
|
|
198
|
+
// 비율을 계산하는 것입니다.
|
|
199
|
+
|
|
200
|
+
// 현재처럼 직접 width/height를 조절하는 방식에서는
|
|
201
|
+
// `finalDragBarPosInOffsetParent`가 prev의 새로운 `width`가 되는 것이 맞고,
|
|
202
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 next의 새로운 `width`가 되는 것이 맞습니다.
|
|
203
|
+
// 만약 padding 때문에 오차가 있다면, 이는 `box-sizing`의 문제이거나,
|
|
204
|
+
// `getBoundingClientRect()`가 `margin`을 포함하지 않는다는 점 때문일 수 있습니다.
|
|
205
|
+
|
|
206
|
+
// **다시 한번 문제의 핵심을 짚어봅시다.**
|
|
207
|
+
// "dragBar가 그만큼 왼쪽으로 그려져 padding 지우면 정상이야."
|
|
208
|
+
// 이 문제는 **드래그 바의 초기 위치 문제**입니다. 이 부분은 이미 `dragBar.offsetParent`를 사용하여 해결했습니다.
|
|
209
|
+
|
|
210
|
+
// "prev, next가 padding 을 가졌을때는 오차가 있어."
|
|
211
|
+
// 이 문제는 **최종 `width`/`height`를 `prev`와 `next`에 적용할 때의 오차**입니다.
|
|
212
|
+
// 즉, `finalPrevSize`와 `finalNextSize`를 계산하는 방식이 문제가 됩니다.
|
|
213
|
+
|
|
214
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 왼쪽/상단 끝에서 `dragBar`까지의 거리입니다.
|
|
215
|
+
// 이 값이 `prev`의 새로운 너비/높이가 되어야 합니다.
|
|
216
|
+
// `offsetParentTotalSize - finalDragBarPosInOffsetParent`가 `next`의 새로운 너비/높이가 되어야 합니다.
|
|
217
|
+
|
|
218
|
+
// 만약 `prev`/`next`가 `padding`을 가질 때 오차가 발생한다면,
|
|
219
|
+
// 이는 `prev.style.width = \`${finalPrevSize}px\`;`를 적용할 때,
|
|
220
|
+
// `finalPrevSize`가 `prev`의 `content + padding + border`를 포함하는 `box-sizing: border-box;`일 경우의 크기여야 하는데,
|
|
221
|
+
// 실제 `prev` 요소의 `box-sizing`이 `content-box`인 경우 `padding`만큼 더 커지기 때문일 수 있습니다.
|
|
222
|
+
|
|
223
|
+
// CSS에서 `box-sizing: border-box;`를 모든 요소에 적용하는 것을 강력히 권장합니다.
|
|
224
|
+
// 예: `* { box-sizing: border-box; }`
|
|
225
|
+
|
|
226
|
+
// 만약 `border-box`를 이미 사용하고 있고 여전히 오차가 있다면,
|
|
227
|
+
// `dragBar`의 `width` (수평 스플리터의 경우) 또는 `height` (수직 스플리터의 경우)를 고려해야 합니다.
|
|
228
|
+
// 스플리터 자체의 두께만큼 `prev`와 `next` 사이의 공간이 발생하기 때문입니다.
|
|
229
|
+
|
|
230
|
+
const splitterThickness = isHorizontal ? splitterRect.width : splitterRect.height; // 스플리터 바의 두께
|
|
231
|
+
|
|
232
|
+
// prev의 새로운 크기: dragBar 위치 - (스플리터 두께의 절반)
|
|
233
|
+
let newPrevWidth = finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
234
|
+
// next의 새로운 크기: (offsetParent 전체 크기 - dragBar 위치) - (스플리터 두께의 절반)
|
|
235
|
+
let newNextWidth = offsetParentTotalSize - finalDragBarPosInOffsetParent - (splitterThickness / 2);
|
|
236
|
+
|
|
237
|
+
// 스플리터 두께만큼 중앙에서 나누어주는 로직
|
|
238
|
+
// prev의 끝점은 dragBar의 중앙이 되어야 함
|
|
239
|
+
// 즉, finalDragBarPosInOffsetParent는 dragBar의 좌측 끝점.
|
|
240
|
+
// prev의 새로운 크기는 이 dragBar 좌측 끝점에서 prev의 시작점까지의 거리.
|
|
241
|
+
// 근데 prev의 실제 content box 끝은 dragBar의 중앙까지 와야 한다면,
|
|
242
|
+
// prev의 끝점에서 dragBar 중앙까지의 거리를 계산.
|
|
243
|
+
|
|
244
|
+
// 다시, 드래그 위치 `relative`가 `parent` 기준 스플리터 바의 왼쪽/상단 경계라면
|
|
245
|
+
// `prev`의 새로운 크기는 이 `relative` 값과 동일해야 합니다.
|
|
246
|
+
// 그리고 `next`의 새로운 크기는 `parentTotalSize - relative - splitterThickness`가 되어야 합니다.
|
|
247
|
+
// 여기서 `relative`는 `onMove`에서 계산된 `dragBar.style.left` 또는 `top` 값입니다.
|
|
248
|
+
// `finalDragBarPosInOffsetParent`는 `relative`와 같은 의미.
|
|
249
|
+
|
|
250
|
+
// ⭐ `totalSize` (두 패널의 합계) 대신 `parent`의 사용 가능한 전체 공간을 기준으로
|
|
251
|
+
// `finalPrevSize`와 `finalNextSize`를 다시 계산합니다.
|
|
252
|
+
// 이 방식이 `padding`이나 `margin` 같은 외부 요소에 덜 민감합니다.
|
|
253
|
+
|
|
254
|
+
// `prev.style.width = Xpx`는 prev의 `content + padding + border`가 Xpx가 된다는 가정 (border-box)
|
|
255
|
+
// `prev`의 시작점 (offsetParent 기준)
|
|
256
|
+
const prevStartPosInOffsetParent = isHorizontal
|
|
257
|
+
? prevRect.left - dragBarOffsetParentRect.left
|
|
258
|
+
: prevRect.top - dragBarOffsetParentRect.top;
|
|
259
|
+
|
|
260
|
+
// `prev`의 새로운 크기 (스플리터 바의 위치까지)
|
|
261
|
+
finalPrevSize = finalDragBarPosInOffsetParent - prevStartPosInOffsetParent;
|
|
262
|
+
// `next`의 새로운 크기 (스플리터 바 이후부터 `offsetParent` 끝까지)
|
|
263
|
+
// `offsetParentTotalSize`는 `offsetParent`의 전체 너비/높이
|
|
264
|
+
finalNextSize = offsetParentTotalSize - (finalDragBarPosInOffsetParent + splitterThickness) + (prevStartPosInOffsetParent + finalPrevSize - nextRect[isHorizontal ? 'left' : 'top'] + dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
265
|
+
// 이 복잡한 계산은 prev와 next 사이의 간극 (스플리터 두께)을 정확히 반영하기 위함입니다.
|
|
266
|
+
// `finalPrevSize + finalNextSize + splitterThickness`가 `offsetParentTotalSize`와 일치해야 합니다.
|
|
267
|
+
|
|
268
|
+
// ⭐ 최종적으로 계산된 크기:
|
|
269
|
+
// `finalDragBarPosInOffsetParent`는 `offsetParent`의 시작점에서 `dragBar`의 시작점까지의 거리.
|
|
270
|
+
// `prev`의 새 너비는 `finalDragBarPosInOffsetParent` 그 자체가 아니라,
|
|
271
|
+
// `dragBar`가 위치한 곳까지의 `prev`의 너비여야 합니다.
|
|
272
|
+
// 즉, `finalDragBarPosInOffsetParent`는 `prev`의 끝점이 되는 위치.
|
|
273
|
+
|
|
274
|
+
// 다시 한번, `finalDragBarPosInOffsetParent`는 `offsetParent`의 0 지점에서 `dragBar`의 0 지점까지의 거리입니다.
|
|
275
|
+
// `prev` 요소의 최종 너비는 이 `finalDragBarPosInOffsetParent`에서
|
|
276
|
+
// `prev` 요소의 `offsetParent` 기준 시작 지점 (`prevRect.left - dragBarOffsetParentRect.left`)을 뺀 값입니다.
|
|
277
|
+
// 즉, `prev`의 새로운 너비 = `dragBar`의 왼쪽 위치 - `prev`의 왼쪽 위치
|
|
278
|
+
|
|
279
|
+
// 그리고 `next`의 새로운 너비 = `next`의 오른쪽 위치 - `dragBar`의 오른쪽 위치
|
|
280
|
+
|
|
281
|
+
const newPrevWidthCalculated = finalDragBarPosInOffsetParent - (prevRect[isHorizontal ? 'left' : 'top'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']);
|
|
282
|
+
const newNextWidthCalculated = (nextRect[isHorizontal ? 'right' : 'bottom'] - dragBarOffsetParentRect[isHorizontal ? 'left' : 'top']) - (finalDragBarPosInOffsetParent + splitterThickness);
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
// 최소 크기 제약
|
|
286
|
+
const MIN_PANEL_SIZE_PX = 1; // 1px 미만으로 작아지지 않도록
|
|
287
|
+
finalPrevSize = Math.max(MIN_PANEL_SIZE_PX, newPrevWidthCalculated);
|
|
288
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, newNextWidthCalculated);
|
|
289
|
+
|
|
290
|
+
// 만약 `prev + next + splitter`의 합이 부모의 `content` 사이즈와 일치해야 한다면
|
|
291
|
+
// `finalNextSize`를 `offsetParentTotalSize - finalPrevSize - splitterThickness`로 계산하는 것이 더 정확할 수 있습니다.
|
|
292
|
+
// 이렇게 하면 오차가 마지막 패널에 몰아지지만, 합은 정확하게 유지됩니다.
|
|
293
|
+
finalNextSize = Math.max(MIN_PANEL_SIZE_PX, offsetParentTotalSize - finalPrevSize - splitterThickness);
|
|
142
294
|
|
|
143
|
-
const MIN_PANEL_SIZE = 1; // 패널 최소 크기
|
|
144
|
-
let finalPrevSize = Math.max(MIN_PANEL_SIZE, newPrevSize);
|
|
145
|
-
let finalNextSize = Math.max(MIN_PANEL_SIZE, newNextSize);
|
|
146
295
|
|
|
147
|
-
// 기존 flex 값을 저장하고 복원
|
|
148
296
|
const originalPrevFlex = prev.style.flex;
|
|
149
297
|
const originalNextFlex = next.style.flex;
|
|
150
298
|
|
|
151
|
-
// 드래그 중에는 flex를 "none"으로 설정하여 직접 크기 조절
|
|
152
299
|
prev.style.flex = "none";
|
|
153
300
|
next.style.flex = "none";
|
|
154
301
|
|
|
155
|
-
console.log(finalPrevSize, finalNextSize);
|
|
156
|
-
|
|
157
302
|
if (isHorizontal) {
|
|
158
303
|
prev.style.width = `${finalPrevSize}px`;
|
|
159
304
|
next.style.width = `${finalNextSize}px`;
|
|
@@ -162,9 +307,6 @@ class nxSplitter extends HTMLElement {
|
|
|
162
307
|
next.style.height = `${finalNextSize}px`;
|
|
163
308
|
}
|
|
164
309
|
|
|
165
|
-
// 작업 완료 후 원래 flex 값으로 복원
|
|
166
|
-
// 주의: width/height가 설정된 상태에서 flex를 복원하면 충돌할 수 있음.
|
|
167
|
-
// flex-basis나 flex-grow/shrink를 적절히 조절하는 것이 더 견고한 방법.
|
|
168
310
|
prev.style.flex = originalPrevFlex;
|
|
169
311
|
next.style.flex = originalNextFlex;
|
|
170
312
|
};
|