@designbasekorea/ui 0.1.44 → 0.1.45

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.umd.js CHANGED
@@ -2195,7 +2195,215 @@
2195
2195
  };
2196
2196
  Spinner.displayName = 'Spinner';
2197
2197
 
2198
- const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, fullWidth = false, disabled = false, loading = false, iconOnly = false, startIcon: StartIcon, endIcon: EndIcon, className, children, onPress, type = 'button', ...props }, forwardedRef) => {
2198
+ const GAP$1 = 8; // 트리거와의 간격
2199
+ const ARW = 6; // 화살표 반쪽 길이(삼각형 변 길이)
2200
+ const PAD = 8; // 화살표가 박스 안에서 안전하게 보일 최소 여백
2201
+ const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'default', delay = 200, hideDelay = 80, alwaysShow = false, disabled = false, maxWidth = 240, showArrow = true, className, ...props }) => {
2202
+ const [visible, setVisible] = React.useState(false);
2203
+ const [style, setStyle] = React.useState({});
2204
+ const [arrowStyle, setArrowStyle] = React.useState({});
2205
+ const [placementGroup, setPlacementGroup] = React.useState('top');
2206
+ const triggerRef = React.useRef(null);
2207
+ const tooltipRef = React.useRef(null);
2208
+ const timers = React.useRef({});
2209
+ const rafId = React.useRef(null);
2210
+ const clearTimer = () => {
2211
+ if (timers.current.t) {
2212
+ clearTimeout(timers.current.t);
2213
+ timers.current.t = undefined;
2214
+ }
2215
+ };
2216
+ const groupOf = (p) => p.startsWith('top') ? 'top' :
2217
+ p.startsWith('bottom') ? 'bottom' :
2218
+ p.startsWith('left') ? 'left' : 'right';
2219
+ // 위치 계산 (⚠️ 뷰포트 클램핑 후 화살표도 보정)
2220
+ const calculatePosition = React.useCallback(() => {
2221
+ if (!triggerRef.current || !tooltipRef.current)
2222
+ return;
2223
+ const tRect = triggerRef.current.getBoundingClientRect();
2224
+ const pRect = tooltipRef.current.getBoundingClientRect();
2225
+ let top = 0, left = 0;
2226
+ // 1차: 이상적(미보정) 화살표 좌표
2227
+ let aTop = 0, aLeft = 0;
2228
+ switch (position) {
2229
+ case 'top':
2230
+ top = tRect.top - pRect.height - GAP$1;
2231
+ left = tRect.left + tRect.width / 2 - pRect.width / 2;
2232
+ aTop = pRect.height;
2233
+ aLeft = pRect.width / 2 - ARW;
2234
+ break;
2235
+ case 'top-start':
2236
+ top = tRect.top - pRect.height - GAP$1;
2237
+ left = tRect.left;
2238
+ aTop = pRect.height;
2239
+ aLeft = PAD;
2240
+ break;
2241
+ case 'top-end':
2242
+ top = tRect.top - pRect.height - GAP$1;
2243
+ left = tRect.right - pRect.width;
2244
+ aTop = pRect.height;
2245
+ aLeft = pRect.width - PAD;
2246
+ break;
2247
+ case 'bottom':
2248
+ top = tRect.bottom + GAP$1;
2249
+ left = tRect.left + tRect.width / 2 - pRect.width / 2;
2250
+ aTop = -ARW;
2251
+ aLeft = pRect.width / 2 - ARW;
2252
+ break;
2253
+ case 'bottom-start':
2254
+ top = tRect.bottom + GAP$1;
2255
+ left = tRect.left;
2256
+ aTop = -ARW;
2257
+ aLeft = PAD;
2258
+ break;
2259
+ case 'bottom-end':
2260
+ top = tRect.bottom + GAP$1;
2261
+ left = tRect.right - pRect.width;
2262
+ aTop = -ARW;
2263
+ aLeft = pRect.width - PAD;
2264
+ break;
2265
+ case 'left':
2266
+ top = tRect.top + tRect.height / 2 - pRect.height / 2;
2267
+ left = tRect.left - pRect.width - GAP$1;
2268
+ aTop = pRect.height / 2 - ARW;
2269
+ aLeft = pRect.width;
2270
+ break;
2271
+ case 'left-start':
2272
+ top = tRect.top;
2273
+ left = tRect.left - pRect.width - GAP$1;
2274
+ aTop = PAD;
2275
+ aLeft = pRect.width;
2276
+ break;
2277
+ case 'left-end':
2278
+ top = tRect.bottom - pRect.height;
2279
+ left = tRect.left - pRect.width - GAP$1;
2280
+ aTop = pRect.height - PAD;
2281
+ aLeft = pRect.width;
2282
+ break;
2283
+ case 'right':
2284
+ top = tRect.top + tRect.height / 2 - pRect.height / 2;
2285
+ left = tRect.right + GAP$1;
2286
+ aTop = pRect.height / 2 - ARW;
2287
+ aLeft = -ARW;
2288
+ break;
2289
+ case 'right-start':
2290
+ top = tRect.top;
2291
+ left = tRect.right + GAP$1;
2292
+ aTop = PAD;
2293
+ aLeft = -ARW;
2294
+ break;
2295
+ case 'right-end':
2296
+ top = tRect.bottom - pRect.height;
2297
+ left = tRect.right + GAP$1;
2298
+ aTop = pRect.height - PAD;
2299
+ aLeft = -ARW;
2300
+ break;
2301
+ }
2302
+ // 뷰포트 클램핑
2303
+ const vw = window.innerWidth;
2304
+ const vh = window.innerHeight;
2305
+ if (left < 8)
2306
+ left = 8;
2307
+ if (left + pRect.width > vw - 8)
2308
+ left = vw - pRect.width - 8;
2309
+ if (top < 8)
2310
+ top = 8;
2311
+ if (top + pRect.height > vh - 8)
2312
+ top = vh - pRect.height - 8;
2313
+ // 🔁 클램핑으로 박스 위치가 바뀌었을 수 있으니, 화살표를 박스 내부에서 다시 보정
2314
+ const g = groupOf(position);
2315
+ if (g === 'top' || g === 'bottom') {
2316
+ // 트리거 중앙 X 를 툴팁 좌표계로 변환
2317
+ const triggerCenterX = tRect.left + tRect.width / 2;
2318
+ const localX = triggerCenterX - left - ARW; // 화살표 기준점을 고려
2319
+ // 8px ~ (width-8px) 범위로 제한
2320
+ aLeft = Math.min(pRect.width - PAD, Math.max(PAD, localX));
2321
+ // aTop은 이미 위/아래에 고정(-ARW 또는 높이)
2322
+ }
2323
+ else {
2324
+ const triggerCenterY = tRect.top + tRect.height / 2;
2325
+ const localY = triggerCenterY - top - ARW;
2326
+ aTop = Math.min(pRect.height - PAD, Math.max(PAD, localY));
2327
+ // aLeft는 이미 좌/우에 고정(-ARW 또는 너비)
2328
+ }
2329
+ setStyle({
2330
+ position: 'fixed',
2331
+ top,
2332
+ left,
2333
+ maxWidth,
2334
+ zIndex: 9999,
2335
+ pointerEvents: 'none', // hover 유지
2336
+ });
2337
+ setArrowStyle({ position: 'absolute', top: aTop, left: aLeft });
2338
+ setPlacementGroup(g);
2339
+ }, [position, maxWidth]);
2340
+ // 초기 페인트 전에 위치 확정
2341
+ React.useLayoutEffect(() => {
2342
+ if (visible || alwaysShow)
2343
+ calculatePosition();
2344
+ }, [visible, alwaysShow, calculatePosition]);
2345
+ // 스크롤/리사이즈/크기변화 추적 (rAF 스로틀)
2346
+ React.useEffect(() => {
2347
+ if (!(visible || alwaysShow))
2348
+ return;
2349
+ const onMove = () => {
2350
+ if (rafId.current != null)
2351
+ cancelAnimationFrame(rafId.current);
2352
+ rafId.current = requestAnimationFrame(() => {
2353
+ calculatePosition();
2354
+ rafId.current = null;
2355
+ });
2356
+ };
2357
+ window.addEventListener('scroll', onMove, { capture: true, passive: true });
2358
+ window.addEventListener('resize', onMove, { passive: true });
2359
+ let ro = null;
2360
+ const ResizeObs = window.ResizeObserver;
2361
+ if (ResizeObs) {
2362
+ ro = new ResizeObs(() => onMove());
2363
+ if (tooltipRef.current)
2364
+ ro.observe(tooltipRef.current);
2365
+ if (triggerRef.current)
2366
+ ro.observe(triggerRef.current);
2367
+ }
2368
+ return () => {
2369
+ window.removeEventListener('scroll', onMove, { capture: true });
2370
+ window.removeEventListener('resize', onMove);
2371
+ ro?.disconnect?.();
2372
+ if (rafId.current != null)
2373
+ cancelAnimationFrame(rafId.current);
2374
+ };
2375
+ }, [visible, alwaysShow, calculatePosition]);
2376
+ // show/hide
2377
+ const show = React.useCallback(() => {
2378
+ if (disabled)
2379
+ return;
2380
+ clearTimer();
2381
+ timers.current.t = setTimeout(() => setVisible(true), Math.max(0, delay));
2382
+ }, [disabled, delay]);
2383
+ const hide = React.useCallback(() => {
2384
+ if (disabled)
2385
+ return;
2386
+ clearTimer();
2387
+ timers.current.t = setTimeout(() => setVisible(false), Math.max(0, hideDelay));
2388
+ }, [disabled, hideDelay]);
2389
+ React.useEffect(() => () => clearTimer(), []);
2390
+ React.useEffect(() => { if (!disabled && alwaysShow)
2391
+ setVisible(true);
2392
+ else if (!alwaysShow)
2393
+ setVisible(false); }, [alwaysShow, disabled]);
2394
+ const onKeyDown = React.useCallback((e) => {
2395
+ if (e.key === 'Escape') {
2396
+ clearTimer();
2397
+ setVisible(false);
2398
+ }
2399
+ }, []);
2400
+ const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, { 'designbase-tooltip--visible': visible || alwaysShow, 'designbase-tooltip--disabled': disabled }, className);
2401
+ const arrowClasses = clsx('designbase-tooltip__arrow', `designbase-tooltip__arrow--${position}`);
2402
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { ref: triggerRef, className: "designbase-tooltip__trigger", onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide, onKeyDown: onKeyDown, tabIndex: 0, "aria-describedby": visible || alwaysShow ? 'db-tooltip' : undefined, children: children }), (visible || alwaysShow) && (jsxRuntime.jsxs("div", { ref: tooltipRef, className: classes, style: style, role: "tooltip", id: "db-tooltip", "aria-hidden": !(visible || alwaysShow), "data-placement-group": placementGroup, ...props, children: [jsxRuntime.jsx("div", { className: "designbase-tooltip__content", children: content }), showArrow && jsxRuntime.jsx("div", { className: arrowClasses, style: arrowStyle })] }))] }));
2403
+ };
2404
+ Tooltip.displayName = 'Tooltip';
2405
+
2406
+ const Button = React.forwardRef(({ variant = 'primary', size = 'm', radius, fullWidth = false, disabled = false, loading = false, iconOnly = false, startIcon: StartIcon, endIcon: EndIcon, tooltip, tooltipProps, className, children, onPress, type = 'button', ...props }, forwardedRef) => {
2199
2407
  const ref = $df56164dff5785e2$export$4338b53315abf666(forwardedRef);
2200
2408
  const { buttonProps } = $701a24aa0da5b062$export$ea18c227d4417cc3({
2201
2409
  ...props,
@@ -2259,7 +2467,12 @@
2259
2467
  }
2260
2468
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [StartIcon && (jsxRuntime.jsx(StartIcon, { size: iconSize, className: "designbase-button__start-icon", color: getIconColor() })), children, EndIcon && (jsxRuntime.jsx(EndIcon, { size: iconSize, className: "designbase-button__end-icon", color: getIconColor() }))] }));
2261
2469
  };
2262
- return (jsxRuntime.jsx("button", { ...buttonProps, ref: ref, className: classes, "aria-label": iconOnly ? props['aria-label'] || children : undefined, children: renderContent() }));
2470
+ const buttonElement = (jsxRuntime.jsx("button", { ...buttonProps, ref: ref, className: classes, "aria-label": iconOnly ? props['aria-label'] || children : undefined, children: renderContent() }));
2471
+ // 툴팁이 있는 경우 Tooltip으로 감싸기
2472
+ if (tooltip) {
2473
+ return (jsxRuntime.jsx(Tooltip, { content: tooltip, position: "top", size: "s", variant: "default", ...tooltipProps, children: buttonElement }));
2474
+ }
2475
+ return buttonElement;
2263
2476
  });
2264
2477
  Button.displayName = 'Button';
2265
2478
 
@@ -8514,7 +8727,7 @@
8514
8727
  };
8515
8728
  Pagination.displayName = 'Pagination';
8516
8729
 
8517
- const GAP$1 = 8; // trigger와 popover 사이 간격
8730
+ const GAP = 8; // trigger와 popover 사이 간격
8518
8731
  const Popover = ({ content, children, title, position = 'top', size = 'm', variant = 'default', trigger = 'click', delay = 0, // 클릭/포커스는 즉시, 호버는 아래에서만 적용
8519
8732
  hideDelay = 80, alwaysShow = false, disabled = false, maxWidth = 300, showArrow = true, closeOnOutsideClick = true, closeOnEscape = true, open: controlledOpen, onOpenChange, className, ...props }) => {
8520
8733
  const [internalOpen, setInternalOpen] = React.useState(false);
@@ -8547,74 +8760,74 @@
8547
8760
  };
8548
8761
  switch (position) {
8549
8762
  case 'top':
8550
- top = tRect.top - pRect.height - GAP$1;
8763
+ top = tRect.top - pRect.height - GAP;
8551
8764
  left = tRect.left + tRect.width / 2 - pRect.width / 2;
8552
8765
  aTop = pRect.height;
8553
8766
  aLeft = pRect.width / 2 - 4;
8554
8767
  break;
8555
8768
  case 'top-start':
8556
- top = tRect.top - pRect.height - GAP$1;
8769
+ top = tRect.top - pRect.height - GAP;
8557
8770
  left = tRect.left;
8558
8771
  aTop = pRect.height;
8559
8772
  aLeft = 12;
8560
8773
  break;
8561
8774
  case 'top-end':
8562
- top = tRect.top - pRect.height - GAP$1;
8775
+ top = tRect.top - pRect.height - GAP;
8563
8776
  left = tRect.right - pRect.width;
8564
8777
  aTop = pRect.height;
8565
8778
  aLeft = pRect.width - 12;
8566
8779
  break;
8567
8780
  case 'bottom':
8568
- top = tRect.bottom + GAP$1;
8781
+ top = tRect.bottom + GAP;
8569
8782
  left = tRect.left + tRect.width / 2 - pRect.width / 2;
8570
8783
  aTop = -4;
8571
8784
  aLeft = pRect.width / 2 - 4;
8572
8785
  break;
8573
8786
  case 'bottom-start':
8574
- top = tRect.bottom + GAP$1;
8787
+ top = tRect.bottom + GAP;
8575
8788
  left = tRect.left;
8576
8789
  aTop = -4;
8577
8790
  aLeft = 12;
8578
8791
  break;
8579
8792
  case 'bottom-end':
8580
- top = tRect.bottom + GAP$1;
8793
+ top = tRect.bottom + GAP;
8581
8794
  left = tRect.right - pRect.width;
8582
8795
  aTop = -4;
8583
8796
  aLeft = pRect.width - 12;
8584
8797
  break;
8585
8798
  case 'left':
8586
8799
  top = tRect.top + tRect.height / 2 - pRect.height / 2;
8587
- left = tRect.left - pRect.width - GAP$1;
8800
+ left = tRect.left - pRect.width - GAP;
8588
8801
  aTop = pRect.height / 2 - 4;
8589
8802
  aLeft = pRect.width;
8590
8803
  break;
8591
8804
  case 'left-start':
8592
8805
  top = tRect.top;
8593
- left = tRect.left - pRect.width - GAP$1;
8806
+ left = tRect.left - pRect.width - GAP;
8594
8807
  aTop = 12;
8595
8808
  aLeft = pRect.width;
8596
8809
  break;
8597
8810
  case 'left-end':
8598
8811
  top = tRect.bottom - pRect.height;
8599
- left = tRect.left - pRect.width - GAP$1;
8812
+ left = tRect.left - pRect.width - GAP;
8600
8813
  aTop = pRect.height - 12;
8601
8814
  aLeft = pRect.width;
8602
8815
  break;
8603
8816
  case 'right':
8604
8817
  top = tRect.top + tRect.height / 2 - pRect.height / 2;
8605
- left = tRect.right + GAP$1;
8818
+ left = tRect.right + GAP;
8606
8819
  aTop = pRect.height / 2 - 4;
8607
8820
  aLeft = -4;
8608
8821
  break;
8609
8822
  case 'right-start':
8610
8823
  top = tRect.top;
8611
- left = tRect.right + GAP$1;
8824
+ left = tRect.right + GAP;
8612
8825
  aTop = 12;
8613
8826
  aLeft = -4;
8614
8827
  break;
8615
8828
  case 'right-end':
8616
8829
  top = tRect.bottom - pRect.height;
8617
- left = tRect.right + GAP$1;
8830
+ left = tRect.right + GAP;
8618
8831
  aTop = pRect.height - 12;
8619
8832
  aLeft = -4;
8620
8833
  break;
@@ -10687,214 +10900,6 @@
10687
10900
  return (jsxRuntime.jsx("div", { className: classes, children: jsxRuntime.jsxs("div", { className: "designbase-toolbar__content", children: [Object.entries(groupedItems).map(([groupName, groupItems], groupIndex) => (jsxRuntime.jsxs("div", { className: "designbase-toolbar__group", children: [groupItems.map(renderItem), groupIndex < Object.keys(groupedItems).length - 1 && (jsxRuntime.jsx("div", { className: "designbase-toolbar__group-separator" }))] }, groupName))), children && (jsxRuntime.jsx("div", { className: "designbase-toolbar__children", children: children }))] }) }));
10688
10901
  };
10689
10902
 
10690
- const GAP = 8; // 트리거와의 간격
10691
- const ARW = 6; // 화살표 반쪽 길이(삼각형 변 길이)
10692
- const PAD = 8; // 화살표가 박스 안에서 안전하게 보일 최소 여백
10693
- const Tooltip = ({ content, children, position = 'top', size = 'm', variant = 'default', delay = 200, hideDelay = 80, alwaysShow = false, disabled = false, maxWidth = 240, showArrow = true, className, ...props }) => {
10694
- const [visible, setVisible] = React.useState(false);
10695
- const [style, setStyle] = React.useState({});
10696
- const [arrowStyle, setArrowStyle] = React.useState({});
10697
- const [placementGroup, setPlacementGroup] = React.useState('top');
10698
- const triggerRef = React.useRef(null);
10699
- const tooltipRef = React.useRef(null);
10700
- const timers = React.useRef({});
10701
- const rafId = React.useRef(null);
10702
- const clearTimer = () => {
10703
- if (timers.current.t) {
10704
- clearTimeout(timers.current.t);
10705
- timers.current.t = undefined;
10706
- }
10707
- };
10708
- const groupOf = (p) => p.startsWith('top') ? 'top' :
10709
- p.startsWith('bottom') ? 'bottom' :
10710
- p.startsWith('left') ? 'left' : 'right';
10711
- // 위치 계산 (⚠️ 뷰포트 클램핑 후 화살표도 보정)
10712
- const calculatePosition = React.useCallback(() => {
10713
- if (!triggerRef.current || !tooltipRef.current)
10714
- return;
10715
- const tRect = triggerRef.current.getBoundingClientRect();
10716
- const pRect = tooltipRef.current.getBoundingClientRect();
10717
- let top = 0, left = 0;
10718
- // 1차: 이상적(미보정) 화살표 좌표
10719
- let aTop = 0, aLeft = 0;
10720
- switch (position) {
10721
- case 'top':
10722
- top = tRect.top - pRect.height - GAP;
10723
- left = tRect.left + tRect.width / 2 - pRect.width / 2;
10724
- aTop = pRect.height;
10725
- aLeft = pRect.width / 2 - ARW;
10726
- break;
10727
- case 'top-start':
10728
- top = tRect.top - pRect.height - GAP;
10729
- left = tRect.left;
10730
- aTop = pRect.height;
10731
- aLeft = PAD;
10732
- break;
10733
- case 'top-end':
10734
- top = tRect.top - pRect.height - GAP;
10735
- left = tRect.right - pRect.width;
10736
- aTop = pRect.height;
10737
- aLeft = pRect.width - PAD;
10738
- break;
10739
- case 'bottom':
10740
- top = tRect.bottom + GAP;
10741
- left = tRect.left + tRect.width / 2 - pRect.width / 2;
10742
- aTop = -ARW;
10743
- aLeft = pRect.width / 2 - ARW;
10744
- break;
10745
- case 'bottom-start':
10746
- top = tRect.bottom + GAP;
10747
- left = tRect.left;
10748
- aTop = -ARW;
10749
- aLeft = PAD;
10750
- break;
10751
- case 'bottom-end':
10752
- top = tRect.bottom + GAP;
10753
- left = tRect.right - pRect.width;
10754
- aTop = -ARW;
10755
- aLeft = pRect.width - PAD;
10756
- break;
10757
- case 'left':
10758
- top = tRect.top + tRect.height / 2 - pRect.height / 2;
10759
- left = tRect.left - pRect.width - GAP;
10760
- aTop = pRect.height / 2 - ARW;
10761
- aLeft = pRect.width;
10762
- break;
10763
- case 'left-start':
10764
- top = tRect.top;
10765
- left = tRect.left - pRect.width - GAP;
10766
- aTop = PAD;
10767
- aLeft = pRect.width;
10768
- break;
10769
- case 'left-end':
10770
- top = tRect.bottom - pRect.height;
10771
- left = tRect.left - pRect.width - GAP;
10772
- aTop = pRect.height - PAD;
10773
- aLeft = pRect.width;
10774
- break;
10775
- case 'right':
10776
- top = tRect.top + tRect.height / 2 - pRect.height / 2;
10777
- left = tRect.right + GAP;
10778
- aTop = pRect.height / 2 - ARW;
10779
- aLeft = -ARW;
10780
- break;
10781
- case 'right-start':
10782
- top = tRect.top;
10783
- left = tRect.right + GAP;
10784
- aTop = PAD;
10785
- aLeft = -ARW;
10786
- break;
10787
- case 'right-end':
10788
- top = tRect.bottom - pRect.height;
10789
- left = tRect.right + GAP;
10790
- aTop = pRect.height - PAD;
10791
- aLeft = -ARW;
10792
- break;
10793
- }
10794
- // 뷰포트 클램핑
10795
- const vw = window.innerWidth;
10796
- const vh = window.innerHeight;
10797
- if (left < 8)
10798
- left = 8;
10799
- if (left + pRect.width > vw - 8)
10800
- left = vw - pRect.width - 8;
10801
- if (top < 8)
10802
- top = 8;
10803
- if (top + pRect.height > vh - 8)
10804
- top = vh - pRect.height - 8;
10805
- // 🔁 클램핑으로 박스 위치가 바뀌었을 수 있으니, 화살표를 박스 내부에서 다시 보정
10806
- const g = groupOf(position);
10807
- if (g === 'top' || g === 'bottom') {
10808
- // 트리거 중앙 X 를 툴팁 좌표계로 변환
10809
- const triggerCenterX = tRect.left + tRect.width / 2;
10810
- const localX = triggerCenterX - left - ARW; // 화살표 기준점을 고려
10811
- // 8px ~ (width-8px) 범위로 제한
10812
- aLeft = Math.min(pRect.width - PAD, Math.max(PAD, localX));
10813
- // aTop은 이미 위/아래에 고정(-ARW 또는 높이)
10814
- }
10815
- else {
10816
- const triggerCenterY = tRect.top + tRect.height / 2;
10817
- const localY = triggerCenterY - top - ARW;
10818
- aTop = Math.min(pRect.height - PAD, Math.max(PAD, localY));
10819
- // aLeft는 이미 좌/우에 고정(-ARW 또는 너비)
10820
- }
10821
- setStyle({
10822
- position: 'fixed',
10823
- top,
10824
- left,
10825
- maxWidth,
10826
- zIndex: 9999,
10827
- pointerEvents: 'none', // hover 유지
10828
- });
10829
- setArrowStyle({ position: 'absolute', top: aTop, left: aLeft });
10830
- setPlacementGroup(g);
10831
- }, [position, maxWidth]);
10832
- // 초기 페인트 전에 위치 확정
10833
- React.useLayoutEffect(() => {
10834
- if (visible || alwaysShow)
10835
- calculatePosition();
10836
- }, [visible, alwaysShow, calculatePosition]);
10837
- // 스크롤/리사이즈/크기변화 추적 (rAF 스로틀)
10838
- React.useEffect(() => {
10839
- if (!(visible || alwaysShow))
10840
- return;
10841
- const onMove = () => {
10842
- if (rafId.current != null)
10843
- cancelAnimationFrame(rafId.current);
10844
- rafId.current = requestAnimationFrame(() => {
10845
- calculatePosition();
10846
- rafId.current = null;
10847
- });
10848
- };
10849
- window.addEventListener('scroll', onMove, { capture: true, passive: true });
10850
- window.addEventListener('resize', onMove, { passive: true });
10851
- let ro = null;
10852
- const ResizeObs = window.ResizeObserver;
10853
- if (ResizeObs) {
10854
- ro = new ResizeObs(() => onMove());
10855
- if (tooltipRef.current)
10856
- ro.observe(tooltipRef.current);
10857
- if (triggerRef.current)
10858
- ro.observe(triggerRef.current);
10859
- }
10860
- return () => {
10861
- window.removeEventListener('scroll', onMove, { capture: true });
10862
- window.removeEventListener('resize', onMove);
10863
- ro?.disconnect?.();
10864
- if (rafId.current != null)
10865
- cancelAnimationFrame(rafId.current);
10866
- };
10867
- }, [visible, alwaysShow, calculatePosition]);
10868
- // show/hide
10869
- const show = React.useCallback(() => {
10870
- if (disabled)
10871
- return;
10872
- clearTimer();
10873
- timers.current.t = setTimeout(() => setVisible(true), Math.max(0, delay));
10874
- }, [disabled, delay]);
10875
- const hide = React.useCallback(() => {
10876
- if (disabled)
10877
- return;
10878
- clearTimer();
10879
- timers.current.t = setTimeout(() => setVisible(false), Math.max(0, hideDelay));
10880
- }, [disabled, hideDelay]);
10881
- React.useEffect(() => () => clearTimer(), []);
10882
- React.useEffect(() => { if (!disabled && alwaysShow)
10883
- setVisible(true);
10884
- else if (!alwaysShow)
10885
- setVisible(false); }, [alwaysShow, disabled]);
10886
- const onKeyDown = React.useCallback((e) => {
10887
- if (e.key === 'Escape') {
10888
- clearTimer();
10889
- setVisible(false);
10890
- }
10891
- }, []);
10892
- const classes = clsx('designbase-tooltip', `designbase-tooltip--${size}`, `designbase-tooltip--${variant}`, `designbase-tooltip--${position}`, { 'designbase-tooltip--visible': visible || alwaysShow, 'designbase-tooltip--disabled': disabled }, className);
10893
- const arrowClasses = clsx('designbase-tooltip__arrow', `designbase-tooltip__arrow--${position}`);
10894
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { ref: triggerRef, className: "designbase-tooltip__trigger", onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide, onKeyDown: onKeyDown, tabIndex: 0, "aria-describedby": visible || alwaysShow ? 'db-tooltip' : undefined, children: children }), (visible || alwaysShow) && (jsxRuntime.jsxs("div", { ref: tooltipRef, className: classes, style: style, role: "tooltip", id: "db-tooltip", "aria-hidden": !(visible || alwaysShow), "data-placement-group": placementGroup, ...props, children: [jsxRuntime.jsx("div", { className: "designbase-tooltip__content", children: content }), showArrow && jsxRuntime.jsx("div", { className: arrowClasses, style: arrowStyle })] }))] }));
10895
- };
10896
- Tooltip.displayName = 'Tooltip';
10897
-
10898
10903
  const VideoPlayer = ({ src, poster, title, description, size = 'm', variant = 'default', theme = 'auto', autoPlay = false, loop = false, muted = false, showControls = true, enableFullscreen = true, enableKeyboard = true, enableTouch = true, showProgress = true, showTime = true, showVolume = true, showSettings = false, playlist = [], currentIndex = 0, autoPause = true, playbackRates = [0.5, 0.75, 1, 1.25, 1.5, 2], defaultPlaybackRate = 1, qualities = [], defaultQuality = '', subtitles = [], defaultSubtitle = '', onPlay, onPause, onEnded, onTimeUpdate, onVolumeChange, onFullscreenChange, onPlaylistChange, onPlaybackRateChange, onQualityChange, onSubtitleChange, onError, className, }) => {
10899
10904
  const videoRef = React.useRef(null);
10900
10905
  const containerRef = React.useRef(null);