@zenkigen-inc/component-ui 1.14.4 → 1.15.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/index.js CHANGED
@@ -18,7 +18,7 @@ function _objectWithoutPropertiesLoose(r, e) {
18
18
  if (null == r) return {};
19
19
  var t = {};
20
20
  for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
21
- if (e.includes(n)) continue;
21
+ if (-1 !== e.indexOf(n)) continue;
22
22
  t[n] = r[n];
23
23
  }
24
24
  return t;
@@ -612,6 +612,109 @@ function Loading({
612
612
  });
613
613
  }
614
614
 
615
+ /**
616
+ * モーダル表示時にバックグラウンドのスクロールを防止するコンポーネント。
617
+ * コンポーネントがマウントされている間、position: fixedアプローチを使用して
618
+ * body要素にスクロールロックを適用します。
619
+ * 縦横両方のスクロール位置を保存し、コンポーネントのアンマウント時に復元します。
620
+ * スクロールバーの有無を検出し、その幅を考慮してレイアウトシフトを防止します。
621
+ * position、top、left、width、overflow、padding-rightを変更し、
622
+ * 最小限の変更でスクロールを防止します。
623
+ * グローバルCSSに依存せず、すべてインラインスタイルで実装されています。
624
+ * このコンポーネントは実際のDOM要素をレンダリングせず、効果のみを適用します。
625
+ *
626
+ * @example
627
+ * // モーダルコンポーネント内で使用する例
628
+ * const Modal = ({ isOpen, children }) => {
629
+ * return (
630
+ * <>
631
+ * {isOpen && <BodyScrollLock />}
632
+ * {isOpen && (
633
+ * <div className="modal">
634
+ * {children}
635
+ * </div>
636
+ * )}
637
+ * </>
638
+ * );
639
+ * };
640
+ */
641
+ const BodyScrollLock = () => {
642
+ useLayoutEffect(() => {
643
+ // 現在の縦横スクロール位置を記録
644
+ const {
645
+ scrollX,
646
+ scrollY
647
+ } = window;
648
+ const {
649
+ body
650
+ } = document;
651
+ // スクロールバーの有無と幅を検出
652
+ const hasVerticalScrollbar = document.documentElement.scrollHeight > document.documentElement.clientHeight;
653
+ const scrollbarWidth = hasVerticalScrollbar ? window.innerWidth - document.documentElement.clientWidth : 0;
654
+ // 元のインラインスタイルの値を保存
655
+ const originalInlineStyles = {
656
+ position: body.style.position,
657
+ top: body.style.top,
658
+ left: body.style.left,
659
+ width: body.style.width,
660
+ overflow: body.style.overflow,
661
+ paddingRight: body.style.paddingRight
662
+ };
663
+ // スクロールロックスタイルを適用
664
+ body.style.position = 'fixed';
665
+ body.style.top = `-${scrollY}px`;
666
+ body.style.left = `-${scrollX}px`;
667
+ body.style.width = '100%';
668
+ body.style.overflow = 'hidden';
669
+ // スクロールバーがある場合、その幅分だけpadding-rightを調整
670
+ if (hasVerticalScrollbar && scrollbarWidth > 0) {
671
+ // 現在のpadding-rightの値を取得
672
+ const {
673
+ paddingRight
674
+ } = window.getComputedStyle(body);
675
+ const paddingRightValue = paddingRight !== '' ? parseInt(paddingRight, 10) : 0;
676
+ // スクロールバーの幅を加算
677
+ body.style.paddingRight = `${paddingRightValue + scrollbarWidth}px`;
678
+ }
679
+ // クリーンアップ関数
680
+ return () => {
681
+ // 元のスタイル値を取得
682
+ const {
683
+ position,
684
+ top,
685
+ left,
686
+ width,
687
+ overflow,
688
+ paddingRight
689
+ } = originalInlineStyles;
690
+ // プロパティごとに元の値を復元
691
+ restoreProperty(body, 'position', position);
692
+ restoreProperty(body, 'top', top);
693
+ restoreProperty(body, 'left', left);
694
+ restoreProperty(body, 'width', width);
695
+ restoreProperty(body, 'overflow', overflow);
696
+ restoreProperty(body, 'padding-right', paddingRight);
697
+ // スクロール位置を復元
698
+ window.scrollTo(scrollX, scrollY);
699
+ };
700
+ }, []); // 空の依存配列を指定して初回のみ実行
701
+ // DOM要素をレンダリングせず、nullを返す
702
+ return null;
703
+ };
704
+ /**
705
+ * 元のスタイル値を復元するヘルパー関数
706
+ * @param element スタイルを復元する要素
707
+ * @param property 復元するCSSプロパティ名
708
+ * @param value 復元する値
709
+ */
710
+ function restoreProperty(element, property, value) {
711
+ if (value !== '') {
712
+ element.style.setProperty(property, value);
713
+ } else {
714
+ element.style.removeProperty(property);
715
+ }
716
+ }
717
+
615
718
  function ModalBody({
616
719
  children
617
720
  }) {
@@ -679,22 +782,24 @@ function Modal({
679
782
  useEffect(() => {
680
783
  setIsMounted(true);
681
784
  }, []);
682
- return isMounted && isOpen ? /*#__PURE__*/createPortal( /*#__PURE__*/jsx(ModalContext.Provider, {
683
- value: {
684
- onClose
685
- },
686
- children: /*#__PURE__*/jsx("div", {
687
- className: "fixed left-0 top-0 z-overlay flex size-full items-center justify-center bg-backgroundOverlayBlack py-4",
785
+ return isMounted && isOpen ? /*#__PURE__*/jsxs(Fragment, {
786
+ children: [/*#__PURE__*/jsx(BodyScrollLock, {}), /*#__PURE__*/createPortal(/*#__PURE__*/jsx(ModalContext.Provider, {
787
+ value: {
788
+ onClose
789
+ },
688
790
  children: /*#__PURE__*/jsx("div", {
689
- className: "grid max-h-full min-h-[120px] grid-rows-[max-content_1fr_max-content] flex-col rounded-lg bg-uiBackground01 shadow-modalShadow",
690
- style: {
691
- width: renderWidth,
692
- height: renderHeight
693
- },
694
- children: children
791
+ className: "fixed left-0 top-0 z-overlay flex size-full items-center justify-center bg-backgroundOverlayBlack py-4",
792
+ children: /*#__PURE__*/jsx("div", {
793
+ className: "grid max-h-full min-h-[120px] grid-rows-[max-content_1fr_max-content] flex-col rounded-lg bg-uiBackground01 shadow-modalShadow",
794
+ style: {
795
+ width: renderWidth,
796
+ height: renderHeight
797
+ },
798
+ children: children
799
+ })
695
800
  })
696
- })
697
- }), (portalTargetRef == null ? void 0 : portalTargetRef.current) != null ? portalTargetRef.current : document.body) : null;
801
+ }), (portalTargetRef == null ? void 0 : portalTargetRef.current) != null ? portalTargetRef.current : document.body)]
802
+ }) : null;
698
803
  }
699
804
  Modal.Body = ModalBody;
700
805
  Modal.Header = ModalHeader;
@@ -1552,19 +1657,23 @@ const TextInput = /*#__PURE__*/forwardRef((_ref, ref) => {
1552
1657
  onClickClearButton
1553
1658
  } = _ref,
1554
1659
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
1660
+ const isShowClearButton = !!onClickClearButton && props.value.length !== 0 && !disabled;
1555
1661
  const inputWrapClasses = clsx('relative flex items-center gap-2 overflow-hidden rounded border', {
1556
1662
  'border-uiBorder02': !isError && !disabled,
1557
1663
  'border-supportError': isError && !disabled,
1558
1664
  'hover:border-hoverInput': !disabled && !isError,
1559
1665
  'hover:focus-within:border-activeInput': !isError,
1560
1666
  'focus-within:border-activeInput': !isError,
1561
- 'bg-disabled02 border-disabled01': disabled
1667
+ 'bg-disabled02 border-disabled01': disabled,
1668
+ 'pr-2': size === 'medium' && isShowClearButton,
1669
+ 'pr-3': size === 'large' && isShowClearButton
1562
1670
  });
1563
- const inputClasses = clsx('flex-1 pl-2 pr-3 outline-0 placeholder:text-textPlaceholder disabled:text-textPlaceholder', {
1564
- ['typography-label14regular min-h-8']: size === 'medium',
1565
- ['typography-label16regular min-h-10']: size === 'large',
1671
+ const inputClasses = clsx('flex-1 outline-0 placeholder:text-textPlaceholder disabled:text-textPlaceholder', {
1672
+ ['typography-label14regular min-h-8 px-2']: size === 'medium',
1673
+ ['typography-label16regular min-h-10 px-3']: size === 'large',
1566
1674
  'text-text01': !isError,
1567
- 'text-supportError': isError
1675
+ 'text-supportError': isError,
1676
+ 'pr-0': isShowClearButton
1568
1677
  });
1569
1678
  return /*#__PURE__*/jsxs("div", {
1570
1679
  className: inputWrapClasses,
@@ -1574,15 +1683,11 @@ const TextInput = /*#__PURE__*/forwardRef((_ref, ref) => {
1574
1683
  className: inputClasses,
1575
1684
  disabled: disabled,
1576
1685
  onChange: props.onChange
1577
- }, props)), onClickClearButton && props.value.length !== 0 && !disabled && /*#__PURE__*/jsx("div", {
1578
- className: "absolute right-3",
1579
- children: /*#__PURE__*/jsx(IconButton, {
1580
- variant: "text",
1581
- icon: "close",
1582
- size: "small",
1583
- isNoPadding: true,
1584
- onClick: onClickClearButton
1585
- })
1686
+ }, props)), isShowClearButton && /*#__PURE__*/jsx(IconButton, {
1687
+ variant: "text",
1688
+ icon: "close",
1689
+ size: "small",
1690
+ onClick: onClickClearButton
1586
1691
  })]
1587
1692
  });
1588
1693
  });
@@ -1685,7 +1790,7 @@ const ToastProvider = ({
1685
1790
  addToast,
1686
1791
  removeToast
1687
1792
  },
1688
- children: [children, isClientRender && /*#__PURE__*/createPortal( /*#__PURE__*/jsx("div", {
1793
+ children: [children, isClientRender && /*#__PURE__*/createPortal(/*#__PURE__*/jsx("div", {
1689
1794
  className: "pointer-events-none fixed bottom-0 left-0 z-toast mb-4 ml-4 flex w-full flex-col-reverse gap-[16px]",
1690
1795
  children: toasts.map(({
1691
1796
  id,
@@ -1719,7 +1824,7 @@ function Toggle({
1719
1824
  'bg-disabledOn': isDisabled && isChecked,
1720
1825
  'bg-disabled01': isDisabled && !isChecked,
1721
1826
  'bg-interactive01 peer-hover:bg-hover01': !isDisabled && isChecked,
1722
- 'bg-interactive02 peer-hover:bg-hover02Dark': !isDisabled && !isChecked,
1827
+ 'bg-interactive02 peer-hover:bg-hoverGray': !isDisabled && !isChecked,
1723
1828
  'w-8 h-4 px-[3px]': size === 'small',
1724
1829
  'w-12 h-6 px-1': size === 'medium' || size === 'large'
1725
1830
  });
@@ -1961,7 +2066,7 @@ function Tooltip({
1961
2066
  verticalPosition: verticalPosition,
1962
2067
  horizontalAlign: horizontalAlign,
1963
2068
  tooltipPosition: tooltipPosition
1964
- }) : ( /*#__PURE__*/createPortal( /*#__PURE__*/jsx(TooltipContent, {
2069
+ }) : (/*#__PURE__*/createPortal(/*#__PURE__*/jsx(TooltipContent, {
1965
2070
  isPortal: true,
1966
2071
  content: content,
1967
2072
  size: size,