@designbasekorea/ui 0.2.10 → 0.2.13

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.esm.js CHANGED
@@ -5277,7 +5277,7 @@ const Chip = ({ label, size = 'm', variant = 'default', color = 'primary', delet
5277
5277
  };
5278
5278
  Chip.displayName = 'Chip';
5279
5279
 
5280
- const ColorPicker = ({ size = 'm', type = 'dropdown', value, defaultValue = '#006FFF', showInput = true, showAlpha = false, showFormatSelector = true, showCopyButton = true, disabled = false, readonly = false, onChange, className, }) => {
5280
+ const ColorPicker = ({ size = 'm', type = 'dropdown', position = 'bottom-left', value, defaultValue = '#006FFF', showInput = true, showAlpha = false, showFormatSelector = true, showCopyButton = true, disabled = false, readonly = false, onChange, className, }) => {
5281
5281
  const [selectedColor, setSelectedColor] = useState(value || defaultValue);
5282
5282
  const [isOpen, setIsOpen] = useState(false);
5283
5283
  const [hue, setHue] = useState(211);
@@ -5423,6 +5423,11 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', value, defaultValue = '#00
5423
5423
  const handleInputChange = (e) => {
5424
5424
  const newValue = e.target.value.trim();
5425
5425
  setInputValue(newValue);
5426
+ // HEX 값이 유효하면 실시간으로 색상 업데이트
5427
+ if (/^#[0-9A-F]{6}$/i.test(newValue)) {
5428
+ handleColorChange(newValue.toUpperCase());
5429
+ updateHSLFromHex(newValue);
5430
+ }
5426
5431
  };
5427
5432
  // 입력 필드에서 포커스 아웃 시 검증 및 적용
5428
5433
  const handleInputBlur = () => {
@@ -5510,7 +5515,7 @@ const ColorPicker = ({ size = 'm', type = 'dropdown', value, defaultValue = '#00
5510
5515
  return;
5511
5516
  setIsOpen(!isOpen);
5512
5517
  };
5513
- const classes = clsx('designbase-color-picker', `designbase-color-picker--${size}`, {
5518
+ const classes = clsx('designbase-color-picker', `designbase-color-picker--${size}`, `designbase-color-picker--${position}`, {
5514
5519
  'designbase-color-picker--disabled': disabled,
5515
5520
  'designbase-color-picker--readonly': readonly,
5516
5521
  'designbase-color-picker--open': isOpen,
@@ -10853,8 +10858,11 @@ Table.displayName = 'Table';
10853
10858
  const Tabs = ({ items, defaultSelectedId, selectedId, orientation = 'horizontal', size = 'm', fullWidth = false, variant = 'default', className, onTabChange, ...props }) => {
10854
10859
  const [activeTabId, setActiveTabId] = useState(selectedId ?? defaultSelectedId ?? items[0]?.id ?? '');
10855
10860
  const [focusedTabId, setFocusedTabId] = useState('');
10861
+ const [isDragging, setIsDragging] = useState(false);
10856
10862
  const tabListRef = useRef(null);
10857
10863
  const tabRefs = useRef(new Map());
10864
+ const dragStartRef = useRef(null);
10865
+ const clickStartRef = useRef(null);
10858
10866
  // 제어 컴포넌트 처리
10859
10867
  useEffect(() => {
10860
10868
  if (selectedId !== undefined) {
@@ -10926,18 +10934,126 @@ const Tabs = ({ items, defaultSelectedId, selectedId, orientation = 'horizontal'
10926
10934
  }
10927
10935
  }, [items, orientation]);
10928
10936
  const handleTabSelect = useCallback((tabId) => {
10937
+ // 드래그 중이면 탭 선택 무시
10938
+ if (isDragging)
10939
+ return;
10929
10940
  const tab = items.find(item => item.id === tabId);
10930
10941
  if (tab && !tab.disabled) {
10931
10942
  setActiveTabId(tabId);
10932
10943
  onTabChange?.(tabId);
10933
10944
  }
10934
- }, [items, onTabChange]);
10945
+ }, [items, onTabChange, isDragging]);
10935
10946
  const handleTabFocus = useCallback((tabId) => {
10936
10947
  setFocusedTabId(tabId);
10937
10948
  }, []);
10938
10949
  const handleTabBlur = useCallback(() => {
10939
10950
  setFocusedTabId('');
10940
10951
  }, []);
10952
+ // 드래그 스크롤 핸들러들
10953
+ const handleMouseDown = useCallback((e) => {
10954
+ if (orientation !== 'horizontal' || !tabListRef.current)
10955
+ return;
10956
+ // 클릭 시작 위치와 시간 기록
10957
+ clickStartRef.current = {
10958
+ x: e.pageX,
10959
+ y: e.pageY,
10960
+ time: Date.now()
10961
+ };
10962
+ dragStartRef.current = {
10963
+ x: e.pageX - tabListRef.current.offsetLeft,
10964
+ scrollLeft: tabListRef.current.scrollLeft
10965
+ };
10966
+ }, [orientation]);
10967
+ const handleMouseMove = useCallback((e) => {
10968
+ if (!dragStartRef.current || !tabListRef.current)
10969
+ return;
10970
+ // 아직 드래그가 시작되지 않았다면 드래그 시작 여부 판단
10971
+ if (!isDragging && clickStartRef.current) {
10972
+ const deltaX = Math.abs(e.pageX - clickStartRef.current.x);
10973
+ const deltaY = Math.abs(e.pageY - clickStartRef.current.y);
10974
+ const deltaTime = Date.now() - clickStartRef.current.time;
10975
+ // 5px 이상 이동하거나 200ms 이상 지났으면 드래그 시작
10976
+ if (deltaX > 5 || deltaY > 5 || deltaTime > 200) {
10977
+ setIsDragging(true);
10978
+ }
10979
+ }
10980
+ if (!isDragging || !dragStartRef.current)
10981
+ return;
10982
+ e.preventDefault();
10983
+ const x = e.pageX - tabListRef.current.offsetLeft;
10984
+ const walk = (x - dragStartRef.current.x) * 2; // 스크롤 속도 조절
10985
+ tabListRef.current.scrollLeft = dragStartRef.current.scrollLeft - walk;
10986
+ }, [isDragging]);
10987
+ const handleMouseUp = useCallback(() => {
10988
+ setIsDragging(false);
10989
+ dragStartRef.current = null;
10990
+ clickStartRef.current = null;
10991
+ }, []);
10992
+ const handleMouseLeave = useCallback(() => {
10993
+ setIsDragging(false);
10994
+ dragStartRef.current = null;
10995
+ clickStartRef.current = null;
10996
+ }, []);
10997
+ // 터치 이벤트 핸들러들
10998
+ const handleTouchStart = useCallback((e) => {
10999
+ if (orientation !== 'horizontal' || !tabListRef.current)
11000
+ return;
11001
+ const touch = e.touches[0];
11002
+ // 클릭 시작 위치와 시간 기록
11003
+ clickStartRef.current = {
11004
+ x: touch.pageX,
11005
+ y: touch.pageY,
11006
+ time: Date.now()
11007
+ };
11008
+ dragStartRef.current = {
11009
+ x: touch.pageX - tabListRef.current.offsetLeft,
11010
+ scrollLeft: tabListRef.current.scrollLeft
11011
+ };
11012
+ }, [orientation]);
11013
+ const handleTouchMove = useCallback((e) => {
11014
+ if (!dragStartRef.current || !tabListRef.current)
11015
+ return;
11016
+ // 아직 드래그가 시작되지 않았다면 드래그 시작 여부 판단
11017
+ if (!isDragging && clickStartRef.current) {
11018
+ const touch = e.touches[0];
11019
+ const deltaX = Math.abs(touch.pageX - clickStartRef.current.x);
11020
+ const deltaY = Math.abs(touch.pageY - clickStartRef.current.y);
11021
+ const deltaTime = Date.now() - clickStartRef.current.time;
11022
+ // 5px 이상 이동하거나 200ms 이상 지났으면 드래그 시작
11023
+ if (deltaX > 5 || deltaY > 5 || deltaTime > 200) {
11024
+ setIsDragging(true);
11025
+ }
11026
+ }
11027
+ if (!isDragging || !dragStartRef.current)
11028
+ return;
11029
+ e.preventDefault();
11030
+ const touch = e.touches[0];
11031
+ const x = touch.pageX - tabListRef.current.offsetLeft;
11032
+ const walk = (x - dragStartRef.current.x) * 2; // 스크롤 속도 조절
11033
+ tabListRef.current.scrollLeft = dragStartRef.current.scrollLeft - walk;
11034
+ }, [isDragging]);
11035
+ const handleTouchEnd = useCallback(() => {
11036
+ setIsDragging(false);
11037
+ dragStartRef.current = null;
11038
+ clickStartRef.current = null;
11039
+ }, []);
11040
+ // 드래그 이벤트 리스너 등록/해제
11041
+ useEffect(() => {
11042
+ if (isDragging) {
11043
+ document.addEventListener('mousemove', handleMouseMove);
11044
+ document.addEventListener('mouseup', handleMouseUp);
11045
+ document.addEventListener('mouseleave', handleMouseLeave);
11046
+ document.addEventListener('touchmove', handleTouchMove, { passive: false });
11047
+ document.addEventListener('touchend', handleTouchEnd);
11048
+ }
11049
+ return () => {
11050
+ document.removeEventListener('mousemove', handleMouseMove);
11051
+ document.removeEventListener('mouseup', handleMouseUp);
11052
+ document.removeEventListener('mouseleave', handleMouseLeave);
11053
+ document.removeEventListener('touchmove', handleTouchMove);
11054
+ document.removeEventListener('touchend', handleTouchEnd);
11055
+ };
11056
+ }, [isDragging, handleMouseMove, handleMouseUp, handleMouseLeave, handleTouchMove, handleTouchEnd]);
10941
11057
  const activeTab = items.find(item => item.id === activeTabId);
10942
11058
  items.findIndex(item => item.id === activeTabId);
10943
11059
  const classes = clsx('designbase-tabs', `designbase-tabs--${orientation}`, `designbase-tabs--${size}`, `designbase-tabs--${variant}`, {
@@ -10945,8 +11061,9 @@ const Tabs = ({ items, defaultSelectedId, selectedId, orientation = 'horizontal'
10945
11061
  }, className);
10946
11062
  const tabListClasses = clsx('designbase-tabs__list', `designbase-tabs__list--${orientation}`, {
10947
11063
  'designbase-tabs__list--full-width': fullWidth,
11064
+ 'designbase-tabs__list--dragging': isDragging,
10948
11065
  });
10949
- return (jsxs("div", { className: classes, ...props, children: [jsx("div", { ref: tabListRef, className: tabListClasses, role: "tablist", "aria-orientation": orientation, "aria-label": "\uD0ED \uBAA9\uB85D", children: items.map((item, index) => {
11066
+ return (jsxs("div", { className: classes, ...props, children: [jsx("div", { ref: tabListRef, className: tabListClasses, role: "tablist", "aria-orientation": orientation, "aria-label": "\uD0ED \uBAA9\uB85D", onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, children: items.map((item, index) => {
10950
11067
  const isSelected = item.id === activeTabId;
10951
11068
  const isFocused = item.id === focusedTabId;
10952
11069
  const isDisabled = item.disabled;