@designbasekorea/ui 0.1.28 → 0.1.30

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/index.js CHANGED
@@ -9630,81 +9630,104 @@ const Skeleton = React.forwardRef(({ variant = 'text', size = 'm', width, height
9630
9630
  });
9631
9631
  Skeleton.displayName = 'Skeleton';
9632
9632
 
9633
- const SplitView = ({ direction = 'horizontal', mode = 'ratio', initialSplit = 0.5, firstSize = 200, minSize = 100, maxSize, splitterSize = 4, splitterColor, splitterHoverColor, first, second, fullWidth = false, fullHeight = false, className, }) => {
9634
- const [split, setSplit] = React.useState(initialSplit);
9635
- const [isDragging, setIsDragging] = React.useState(false);
9633
+ const SplitView = ({ direction = 'horizontal', mode = 'ratio', panels, splitterSize = 4, splitterColor, splitterHoverColor, fullWidth = false, fullHeight = false, className,
9634
+ // Legacy props
9635
+ first, second, initialSplit = 0.5, firstSize = 200, minSize = 100, maxSize, }) => {
9636
+ // Legacy mode: first/second props를 panels로 변환
9637
+ const effectivePanels = panels || [
9638
+ {
9639
+ content: first,
9640
+ size: mode === 'fixed' ? firstSize : initialSplit,
9641
+ minSize,
9642
+ maxSize,
9643
+ resizable: true,
9644
+ },
9645
+ {
9646
+ content: second,
9647
+ resizable: false, // 마지막 패널은 flex로 채움
9648
+ },
9649
+ ];
9650
+ // 각 패널의 현재 크기 상태
9651
+ const [panelSizes, setPanelSizes] = React.useState(() => effectivePanels.map((panel, index) => {
9652
+ if (panel.size !== undefined)
9653
+ return panel.size;
9654
+ if (mode === 'ratio')
9655
+ return 1 / effectivePanels.length;
9656
+ return 200; // 기본 고정 크기
9657
+ }));
9658
+ const [draggingIndex, setDraggingIndex] = React.useState(null);
9636
9659
  const containerRef = React.useRef(null);
9637
- const splitterRef = React.useRef(null);
9638
- // 분할 비율 또는 고정 크기 계산
9639
- const getSplitValue = React.useCallback(() => {
9640
- if (mode === 'fixed') {
9641
- return firstSize;
9642
- }
9643
- return Math.max(0, Math.min(1, split)) * 100;
9644
- }, [mode, split, firstSize]);
9645
9660
  // 드래그 시작
9646
- const handleMouseDown = React.useCallback((e) => {
9661
+ const handleMouseDown = React.useCallback((index) => (e) => {
9647
9662
  e.preventDefault();
9648
- setIsDragging(true);
9663
+ setDraggingIndex(index);
9649
9664
  document.body.style.cursor = direction === 'horizontal' ? 'col-resize' : 'row-resize';
9650
9665
  document.body.style.userSelect = 'none';
9651
9666
  }, [direction]);
9652
9667
  // 드래그 중
9653
9668
  const handleMouseMove = React.useCallback((e) => {
9654
- if (!isDragging || !containerRef.current)
9669
+ if (draggingIndex === null || !containerRef.current)
9655
9670
  return;
9656
9671
  const containerRect = containerRef.current.getBoundingClientRect();
9657
- let newSplit;
9672
+ const panel = effectivePanels[draggingIndex];
9673
+ let newSize;
9658
9674
  if (direction === 'horizontal') {
9659
9675
  const containerWidth = containerRect.width;
9660
9676
  const mouseX = e.clientX - containerRect.left;
9677
+ // 이전 패널들의 총 크기 계산
9678
+ let offsetX = 0;
9679
+ for (let i = 0; i < draggingIndex; i++) {
9680
+ const size = panelSizes[i];
9681
+ offsetX += mode === 'fixed' ? size : (size * containerWidth);
9682
+ offsetX += splitterSize; // 분할선 크기 추가
9683
+ }
9684
+ const rawSize = mouseX - offsetX;
9661
9685
  if (mode === 'fixed') {
9662
- // 고정 크기 모드: 픽셀 값으로 설정
9663
- newSplit = Math.max(minSize, Math.min(maxSize || containerWidth - minSize, mouseX));
9664
- setSplit(newSplit);
9686
+ newSize = Math.max(panel.minSize || 0, Math.min(panel.maxSize || containerWidth, rawSize));
9665
9687
  }
9666
9688
  else {
9667
- // 비율 모드: 0-1 값으로 설정
9668
- newSplit = mouseX / containerWidth;
9669
- // 최소/최대 크기 제한
9670
- if (minSize || maxSize) {
9671
- const minSplit = minSize ? minSize / containerWidth : 0;
9672
- const maxSplit = maxSize ? maxSize / containerWidth : 1;
9673
- newSplit = Math.max(minSplit, Math.min(maxSplit, newSplit));
9674
- }
9675
- setSplit(newSplit);
9689
+ newSize = rawSize / containerWidth;
9690
+ const minSplit = panel.minSize ? panel.minSize / containerWidth : 0;
9691
+ const maxSplit = panel.maxSize ? panel.maxSize / containerWidth : 1;
9692
+ newSize = Math.max(minSplit, Math.min(maxSplit, newSize));
9676
9693
  }
9677
9694
  }
9678
9695
  else {
9679
9696
  const containerHeight = containerRect.height;
9680
9697
  const mouseY = e.clientY - containerRect.top;
9698
+ // 이전 패널들의 총 크기 계산
9699
+ let offsetY = 0;
9700
+ for (let i = 0; i < draggingIndex; i++) {
9701
+ const size = panelSizes[i];
9702
+ offsetY += mode === 'fixed' ? size : (size * containerHeight);
9703
+ offsetY += splitterSize; // 분할선 크기 추가
9704
+ }
9705
+ const rawSize = mouseY - offsetY;
9681
9706
  if (mode === 'fixed') {
9682
- // 고정 크기 모드: 픽셀 값으로 설정
9683
- newSplit = Math.max(minSize, Math.min(maxSize || containerHeight - minSize, mouseY));
9684
- setSplit(newSplit);
9707
+ newSize = Math.max(panel.minSize || 0, Math.min(panel.maxSize || containerHeight, rawSize));
9685
9708
  }
9686
9709
  else {
9687
- // 비율 모드: 0-1 값으로 설정
9688
- newSplit = mouseY / containerHeight;
9689
- // 최소/최대 크기 제한
9690
- if (minSize || maxSize) {
9691
- const minSplit = minSize ? minSize / containerHeight : 0;
9692
- const maxSplit = maxSize ? maxSize / containerHeight : 1;
9693
- newSplit = Math.max(minSplit, Math.min(maxSplit, newSplit));
9694
- }
9695
- setSplit(newSplit);
9710
+ newSize = rawSize / containerHeight;
9711
+ const minSplit = panel.minSize ? panel.minSize / containerHeight : 0;
9712
+ const maxSplit = panel.maxSize ? panel.maxSize / containerHeight : 1;
9713
+ newSize = Math.max(minSplit, Math.min(maxSplit, newSize));
9696
9714
  }
9697
9715
  }
9698
- }, [isDragging, direction, mode, minSize, maxSize]);
9716
+ setPanelSizes(prev => {
9717
+ const next = [...prev];
9718
+ next[draggingIndex] = newSize;
9719
+ return next;
9720
+ });
9721
+ }, [draggingIndex, direction, mode, splitterSize, panelSizes, effectivePanels]);
9699
9722
  // 드래그 종료
9700
9723
  const handleMouseUp = React.useCallback(() => {
9701
- setIsDragging(false);
9724
+ setDraggingIndex(null);
9702
9725
  document.body.style.cursor = '';
9703
9726
  document.body.style.userSelect = '';
9704
9727
  }, []);
9705
9728
  // 이벤트 리스너 등록/해제
9706
9729
  React.useEffect(() => {
9707
- if (isDragging) {
9730
+ if (draggingIndex !== null) {
9708
9731
  document.addEventListener('mousemove', handleMouseMove);
9709
9732
  document.addEventListener('mouseup', handleMouseUp);
9710
9733
  }
@@ -9712,20 +9735,41 @@ const SplitView = ({ direction = 'horizontal', mode = 'ratio', initialSplit = 0.
9712
9735
  document.removeEventListener('mousemove', handleMouseMove);
9713
9736
  document.removeEventListener('mouseup', handleMouseUp);
9714
9737
  };
9715
- }, [isDragging, handleMouseMove, handleMouseUp]);
9738
+ }, [draggingIndex, handleMouseMove, handleMouseUp]);
9716
9739
  const classes = clsx('designbase-split-view', `designbase-split-view--direction-${direction}`, `designbase-split-view--mode-${mode}`, {
9717
9740
  'designbase-split-view--full-width': fullWidth,
9718
9741
  'designbase-split-view--full-height': fullHeight,
9719
- 'designbase-split-view--dragging': isDragging,
9742
+ 'designbase-split-view--dragging': draggingIndex !== null,
9720
9743
  }, className);
9721
- const splitterClasses = clsx('designbase-split-view__splitter', `designbase-split-view__splitter--direction-${direction}`);
9722
9744
  const style = {
9723
- '--split-value': mode === 'fixed' ? `${getSplitValue()}px` : `${getSplitValue()}%`,
9724
9745
  '--splitter-size': `${splitterSize}px`,
9725
9746
  '--splitter-color': splitterColor,
9726
9747
  '--splitter-hover-color': splitterHoverColor,
9727
9748
  };
9728
- return (jsxRuntime.jsxs("div", { ref: containerRef, className: classes, style: style, children: [jsxRuntime.jsx("div", { className: "designbase-split-view__first", children: first }), jsxRuntime.jsx("div", { ref: splitterRef, className: splitterClasses, onMouseDown: handleMouseDown }), jsxRuntime.jsx("div", { className: "designbase-split-view__second", children: second })] }));
9749
+ return (jsxRuntime.jsx("div", { ref: containerRef, className: classes, style: style, children: effectivePanels.map((panel, index) => {
9750
+ const size = panelSizes[index];
9751
+ const isLast = index === effectivePanels.length - 1;
9752
+ const isResizable = panel.resizable !== false && !isLast;
9753
+ const panelStyle = {};
9754
+ if (!isLast) {
9755
+ if (direction === 'horizontal') {
9756
+ panelStyle.width = mode === 'fixed' ? `${size}px` : `${size * 100}%`;
9757
+ panelStyle.flexShrink = 0;
9758
+ }
9759
+ else {
9760
+ panelStyle.height = mode === 'fixed' ? `${size}px` : `${size * 100}%`;
9761
+ panelStyle.flexShrink = 0;
9762
+ }
9763
+ }
9764
+ else {
9765
+ panelStyle.flex = 1;
9766
+ }
9767
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("div", { className: clsx('designbase-split-view__panel', panel.className), style: panelStyle, children: panel.content }), !isLast && (jsxRuntime.jsx("div", { className: clsx('designbase-split-view__splitter', `designbase-split-view__splitter--direction-${direction}`, { 'designbase-split-view__splitter--disabled': !isResizable }), onMouseDown: isResizable ? handleMouseDown(index) : undefined, style: {
9768
+ cursor: isResizable
9769
+ ? direction === 'horizontal' ? 'col-resize' : 'row-resize'
9770
+ : 'default',
9771
+ } }))] }, index));
9772
+ }) }));
9729
9773
  };
9730
9774
 
9731
9775
  const Stack = ({ direction = 'vertical', alignment = 'start', justify = 'start', spacing = 'md', fullWidth = false, fullHeight = false, wrap = false, className, children, }) => {