@zenkigen-inc/component-ui 1.14.5 → 1.15.1
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 +115 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +120 -15
- package/dist/index.js.map +1 -1
- package/dist/modal/body-scroll-lock.d.ts +27 -0
- package/dist/modal/modal.d.ts +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -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__*/
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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: "
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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
|
-
})
|
|
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;
|
|
@@ -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-
|
|
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
|
});
|