@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/README.md CHANGED
@@ -44,11 +44,6 @@ yarn generate-component
44
44
 
45
45
  使用する側の import を簡略化させるため root にある [packages/component-ui/src/index.ts](https://github.com/zenkigen/zenkigen-component/blob/main/packages/components/src/index.ts) に実装したコンポーネントを export してください。
46
46
 
47
- ### コーディングガイドライン(社内のみ)
48
-
49
- 開発する際は以下を参照してください。
50
- https://www.notion.so/zenkigen/5d4ebd0d93b74124a533cf167b852ec0
51
-
52
47
  ## ライセンス
53
48
 
54
49
  @zenkigen-inc/component-ui は MIT ライセンスに基づいています。
@@ -1,2 +1,2 @@
1
1
  import type { RefObject } from 'react';
2
- export declare const useOutsideClick: <T extends HTMLElement = HTMLElement>(ref: RefObject<T>, handler: (event: Event) => void, enabled?: boolean) => void;
2
+ export declare const useOutsideClick: <T extends HTMLElement = HTMLElement>(ref: RefObject<T | null>, handler: (event: Event) => void, enabled?: boolean) => void;
package/dist/index.esm.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;
@@ -628,6 +628,104 @@ function Loading(_ref) {
628
628
  });
629
629
  }
630
630
 
631
+ /**
632
+ * モーダル表示時にバックグラウンドのスクロールを防止するコンポーネント。
633
+ * コンポーネントがマウントされている間、position: fixedアプローチを使用して
634
+ * body要素にスクロールロックを適用します。
635
+ * 縦横両方のスクロール位置を保存し、コンポーネントのアンマウント時に復元します。
636
+ * スクロールバーの有無を検出し、その幅を考慮してレイアウトシフトを防止します。
637
+ * position、top、left、width、overflow、padding-rightを変更し、
638
+ * 最小限の変更でスクロールを防止します。
639
+ * グローバルCSSに依存せず、すべてインラインスタイルで実装されています。
640
+ * このコンポーネントは実際のDOM要素をレンダリングせず、効果のみを適用します。
641
+ *
642
+ * @example
643
+ * // モーダルコンポーネント内で使用する例
644
+ * const Modal = ({ isOpen, children }) => {
645
+ * return (
646
+ * <>
647
+ * {isOpen && <BodyScrollLock />}
648
+ * {isOpen && (
649
+ * <div className="modal">
650
+ * {children}
651
+ * </div>
652
+ * )}
653
+ * </>
654
+ * );
655
+ * };
656
+ */
657
+ var BodyScrollLock = function BodyScrollLock() {
658
+ useLayoutEffect(function () {
659
+ // 現在の縦横スクロール位置を記録
660
+ var _window = window,
661
+ scrollX = _window.scrollX,
662
+ scrollY = _window.scrollY;
663
+ var _document = document,
664
+ body = _document.body;
665
+ // スクロールバーの有無と幅を検出
666
+ var hasVerticalScrollbar = document.documentElement.scrollHeight > document.documentElement.clientHeight;
667
+ var scrollbarWidth = hasVerticalScrollbar ? window.innerWidth - document.documentElement.clientWidth : 0;
668
+ // 元のインラインスタイルの値を保存
669
+ var originalInlineStyles = {
670
+ position: body.style.position,
671
+ top: body.style.top,
672
+ left: body.style.left,
673
+ width: body.style.width,
674
+ overflow: body.style.overflow,
675
+ paddingRight: body.style.paddingRight
676
+ };
677
+ // スクロールロックスタイルを適用
678
+ body.style.position = 'fixed';
679
+ body.style.top = "-" + scrollY + "px";
680
+ body.style.left = "-" + scrollX + "px";
681
+ body.style.width = '100%';
682
+ body.style.overflow = 'hidden';
683
+ // スクロールバーがある場合、その幅分だけpadding-rightを調整
684
+ if (hasVerticalScrollbar && scrollbarWidth > 0) {
685
+ // 現在のpadding-rightの値を取得
686
+ var _window$getComputedSt = window.getComputedStyle(body),
687
+ paddingRight = _window$getComputedSt.paddingRight;
688
+ var paddingRightValue = paddingRight !== '' ? parseInt(paddingRight, 10) : 0;
689
+ // スクロールバーの幅を加算
690
+ body.style.paddingRight = paddingRightValue + scrollbarWidth + "px";
691
+ }
692
+ // クリーンアップ関数
693
+ return function () {
694
+ // 元のスタイル値を取得
695
+ var position = originalInlineStyles.position,
696
+ top = originalInlineStyles.top,
697
+ left = originalInlineStyles.left,
698
+ width = originalInlineStyles.width,
699
+ overflow = originalInlineStyles.overflow,
700
+ paddingRight = originalInlineStyles.paddingRight;
701
+ // プロパティごとに元の値を復元
702
+ restoreProperty(body, 'position', position);
703
+ restoreProperty(body, 'top', top);
704
+ restoreProperty(body, 'left', left);
705
+ restoreProperty(body, 'width', width);
706
+ restoreProperty(body, 'overflow', overflow);
707
+ restoreProperty(body, 'padding-right', paddingRight);
708
+ // スクロール位置を復元
709
+ window.scrollTo(scrollX, scrollY);
710
+ };
711
+ }, []); // 空の依存配列を指定して初回のみ実行
712
+ // DOM要素をレンダリングせず、nullを返す
713
+ return null;
714
+ };
715
+ /**
716
+ * 元のスタイル値を復元するヘルパー関数
717
+ * @param element スタイルを復元する要素
718
+ * @param property 復元するCSSプロパティ名
719
+ * @param value 復元する値
720
+ */
721
+ function restoreProperty(element, property, value) {
722
+ if (value !== '') {
723
+ element.style.setProperty(property, value);
724
+ } else {
725
+ element.style.removeProperty(property);
726
+ }
727
+ }
728
+
631
729
  function ModalBody(_ref) {
632
730
  var children = _ref.children;
633
731
  return /*#__PURE__*/jsx("div", {
@@ -697,22 +795,24 @@ function Modal(_ref) {
697
795
  useEffect(function () {
698
796
  setIsMounted(true);
699
797
  }, []);
700
- return isMounted && isOpen ? /*#__PURE__*/createPortal( /*#__PURE__*/jsx(ModalContext.Provider, {
701
- value: {
702
- onClose: onClose
703
- },
704
- children: /*#__PURE__*/jsx("div", {
705
- className: "fixed left-0 top-0 z-overlay flex size-full items-center justify-center bg-backgroundOverlayBlack py-4",
798
+ return isMounted && isOpen ? /*#__PURE__*/jsxs(Fragment, {
799
+ children: [/*#__PURE__*/jsx(BodyScrollLock, {}), /*#__PURE__*/createPortal(/*#__PURE__*/jsx(ModalContext.Provider, {
800
+ value: {
801
+ onClose: onClose
802
+ },
706
803
  children: /*#__PURE__*/jsx("div", {
707
- className: "grid max-h-full min-h-[120px] grid-rows-[max-content_1fr_max-content] flex-col rounded-lg bg-uiBackground01 shadow-modalShadow",
708
- style: {
709
- width: renderWidth,
710
- height: renderHeight
711
- },
712
- children: children
804
+ className: "fixed left-0 top-0 z-overlay flex size-full items-center justify-center bg-backgroundOverlayBlack py-4",
805
+ children: /*#__PURE__*/jsx("div", {
806
+ className: "grid max-h-full min-h-[120px] grid-rows-[max-content_1fr_max-content] flex-col rounded-lg bg-uiBackground01 shadow-modalShadow",
807
+ style: {
808
+ width: renderWidth,
809
+ height: renderHeight
810
+ },
811
+ children: children
812
+ })
713
813
  })
714
- })
715
- }), (portalTargetRef == null ? void 0 : portalTargetRef.current) != null ? portalTargetRef.current : document.body) : null;
814
+ }), (portalTargetRef == null ? void 0 : portalTargetRef.current) != null ? portalTargetRef.current : document.body)]
815
+ }) : null;
716
816
  }
717
817
  Modal.Body = ModalBody;
718
818
  Modal.Header = ModalHeader;
@@ -1602,15 +1702,18 @@ var TextInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
1602
1702
  disabled = _ref$disabled === void 0 ? false : _ref$disabled,
1603
1703
  onClickClearButton = _ref.onClickClearButton,
1604
1704
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
1705
+ var isShowClearButton = !!onClickClearButton && props.value.length !== 0 && !disabled;
1605
1706
  var inputWrapClasses = clsx('relative flex items-center gap-2 overflow-hidden rounded border', {
1606
1707
  'border-uiBorder02': !isError && !disabled,
1607
1708
  'border-supportError': isError && !disabled,
1608
1709
  'hover:border-hoverInput': !disabled && !isError,
1609
1710
  'hover:focus-within:border-activeInput': !isError,
1610
1711
  'focus-within:border-activeInput': !isError,
1611
- 'bg-disabled02 border-disabled01': disabled
1712
+ 'bg-disabled02 border-disabled01': disabled,
1713
+ 'pr-2': size === 'medium' && isShowClearButton,
1714
+ 'pr-3': size === 'large' && isShowClearButton
1612
1715
  });
1613
- var inputClasses = clsx('flex-1 pl-2 pr-3 outline-0 placeholder:text-textPlaceholder disabled:text-textPlaceholder', (_clsx = {}, _clsx['typography-label14regular min-h-8'] = size === 'medium', _clsx['typography-label16regular min-h-10'] = size === 'large', _clsx['text-text01'] = !isError, _clsx['text-supportError'] = isError, _clsx));
1716
+ var inputClasses = clsx('flex-1 outline-0 placeholder:text-textPlaceholder disabled:text-textPlaceholder', (_clsx = {}, _clsx['typography-label14regular min-h-8 px-2'] = size === 'medium', _clsx['typography-label16regular min-h-10 px-3'] = size === 'large', _clsx['text-text01'] = !isError, _clsx['text-supportError'] = isError, _clsx['pr-0'] = isShowClearButton, _clsx));
1614
1717
  return /*#__PURE__*/jsxs("div", {
1615
1718
  className: inputWrapClasses,
1616
1719
  children: [/*#__PURE__*/jsx("input", _extends({
@@ -1619,15 +1722,11 @@ var TextInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
1619
1722
  className: inputClasses,
1620
1723
  disabled: disabled,
1621
1724
  onChange: props.onChange
1622
- }, props)), onClickClearButton && props.value.length !== 0 && !disabled && /*#__PURE__*/jsx("div", {
1623
- className: "absolute right-3",
1624
- children: /*#__PURE__*/jsx(IconButton, {
1625
- variant: "text",
1626
- icon: "close",
1627
- size: "small",
1628
- isNoPadding: true,
1629
- onClick: onClickClearButton
1630
- })
1725
+ }, props)), isShowClearButton && /*#__PURE__*/jsx(IconButton, {
1726
+ variant: "text",
1727
+ icon: "close",
1728
+ size: "small",
1729
+ onClick: onClickClearButton
1631
1730
  })]
1632
1731
  });
1633
1732
  });
@@ -1745,7 +1844,7 @@ var ToastProvider = function ToastProvider(_ref) {
1745
1844
  addToast: addToast,
1746
1845
  removeToast: removeToast
1747
1846
  },
1748
- children: [children, isClientRender && /*#__PURE__*/createPortal( /*#__PURE__*/jsx("div", {
1847
+ children: [children, isClientRender && /*#__PURE__*/createPortal(/*#__PURE__*/jsx("div", {
1749
1848
  className: "pointer-events-none fixed bottom-0 left-0 z-toast mb-4 ml-4 flex w-full flex-col-reverse gap-[16px]",
1750
1849
  children: toasts.map(function (_ref3) {
1751
1850
  var id = _ref3.id,
@@ -1784,7 +1883,7 @@ function Toggle(_ref) {
1784
1883
  'bg-disabledOn': isDisabled && isChecked,
1785
1884
  'bg-disabled01': isDisabled && !isChecked,
1786
1885
  'bg-interactive01 peer-hover:bg-hover01': !isDisabled && isChecked,
1787
- 'bg-interactive02 peer-hover:bg-hover02Dark': !isDisabled && !isChecked,
1886
+ 'bg-interactive02 peer-hover:bg-hoverGray': !isDisabled && !isChecked,
1788
1887
  'w-8 h-4 px-[3px]': size === 'small',
1789
1888
  'w-12 h-6 px-1': size === 'medium' || size === 'large'
1790
1889
  });
@@ -2032,7 +2131,7 @@ function Tooltip(_ref) {
2032
2131
  verticalPosition: verticalPosition,
2033
2132
  horizontalAlign: horizontalAlign,
2034
2133
  tooltipPosition: tooltipPosition
2035
- }) : ( /*#__PURE__*/createPortal( /*#__PURE__*/jsx(TooltipContent, {
2134
+ }) : (/*#__PURE__*/createPortal(/*#__PURE__*/jsx(TooltipContent, {
2036
2135
  isPortal: true,
2037
2136
  content: content,
2038
2137
  size: size,