@ehfuse/overlay-scrollbar 1.7.34 → 1.7.36

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/OverlayScrollbar.tsx", "../src/utils/dragScrollUtils.ts", "../src/utils/bottomSpacerUtils.ts", "../src/hooks/usePullToRefresh.ts", "../src/hooks/useCoarsePointer.ts"],
4
- "sourcesContent": ["/**\n * OverlayScrollbar.tsx\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useMemo,\n ReactNode,\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useId,\n} from \"react\";\nimport { isTextInputElement } from \"./utils/dragScrollUtils\";\nimport { extractBottomPadding } from \"./utils/bottomSpacerUtils\";\nimport { usePullToRefresh } from \"./hooks/usePullToRefresh\";\nimport { useCoarsePointer } from \"./hooks/useCoarsePointer\";\nimport type { PullToRefreshConfig } from \"./types\";\n\n// thumb \uAD00\uB828 \uC124\uC815\nexport interface ThumbConfig {\n width?: number; // \uC378\uC758 \uB108\uBE44 (\uAE30\uBCF8\uAC00: 8px)\n minHeight?: number; // \uC378\uC758 \uCD5C\uC18C \uB192\uC774 (\uAE30\uBCF8\uAC00: 50px)\n radius?: number; // \uC378\uC758 border-radius (\uAE30\uBCF8\uAC00: width / 2)\n color?: string; // \uC378 \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: \"#606060\")\n opacity?: number; // \uAE30\uBCF8 \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 0.6)\n hoverColor?: string; // \uD638\uBC84 \uC2DC \uC0C9\uC0C1 (\uAE30\uBCF8\uAC12: color \uB3D9\uC77C)\n hoverOpacity?: number; // \uD638\uBC84 \uC2DC \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 1.0)\n}\n\n// track \uAD00\uB828 \uC124\uC815\nexport interface TrackConfig {\n width?: number; // \uD638\uBC84 \uC601\uC5ED\uC778 \uD2B8\uB799\uC758 \uB108\uBE44 (\uAE30\uBCF8\uAC12: 16px)\n color?: string; // \uD2B8\uB799 \uBC30\uACBD \uC0C9\uC0C1 (\uAE30\uBCF8\uAC12: \"rgba(128, 128, 128, 0.1)\")\n visible?: boolean; // \uD2B8\uB799 \uBC30\uACBD \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n alignment?: \"default\" | \"outside\"; // \uD2B8\uB799 \uC815\uB82C (\uAE30\uBCF8\uAC12: \"outside\", \"default\"\uB294 \uCF58\uD150\uCE20 \uC548\uCABD\uC5D0 \uD45C\uC2DC)\n radius?: number; // \uD2B8\uB799 \uBC30\uACBD\uC758 border-radius (\uAE30\uBCF8\uAC12: thumb.radius \uB610\uB294 4px)\n margin?: number; // \uD2B8\uB799 \uC0C1\uD558 \uB9C8\uC9C4 (\uAE30\uBCF8\uAC12: 4px)\n overflowX?: boolean; // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n overflowY?: boolean; // \uC138\uB85C \uC2A4\uD06C\uB864\uBC14 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n}\n\n// arrows \uAD00\uB828 \uC124\uC815\nexport interface ArrowsConfig {\n visible?: boolean; // \uD654\uC0B4\uD45C \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC00: false)\n step?: number; // \uD654\uC0B4\uD45C \uD074\uB9AD\uC2DC \uC2A4\uD06C\uB864 \uC774\uB3D9 \uAC70\uB9AC (\uAE30\uBCF8\uAC00: 50px)\n color?: string; // \uD654\uC0B4\uD45C \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: \"#808080\")\n opacity?: number; // \uAE30\uBCF8 \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 0.6)\n hoverColor?: string; // \uD638\uBC84 \uC2DC \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: color \uB3D9\uC77C)\n hoverOpacity?: number; // \uD638\uBC84 \uC2DC \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 1.0)\n}\n\n// \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uAD00\uB828 \uC124\uC815\nexport interface DragScrollConfig {\n enabled?: boolean; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n excludeClasses?: string[]; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uCD94\uAC00 \uD074\uB798\uC2A4\uB4E4 (\uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C \uD655\uC778, \uCD5C\uB300 5\uB2E8\uACC4)\n excludeSelectors?: string[]; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uCD94\uAC00 CSS \uC140\uB809\uD130\uB4E4 (element.matches() \uC0AC\uC6A9)\n}\n\n// \uC790\uB3D9 \uC228\uAE40 \uAD00\uB828 \uC124\uC815\nexport interface AutoHideConfig {\n enabled?: boolean; // \uC790\uB3D9 \uC228\uAE40 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n delay?: number; // \uAE30\uBCF8 \uC790\uB3D9 \uC228\uAE40 \uC2DC\uAC04 (\uAE30\uBCF8\uAC12: 1500ms)\n delayOnWheel?: number; // \uD720 \uC2A4\uD06C\uB864 \uD6C4 \uC790\uB3D9 \uC228\uAE40 \uC2DC\uAC04 (\uAE30\uBCF8\uAC00: 700ms)\n initialDelay?: number; // \uB9C8\uC6B4\uD2B8 \uD6C4 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC9C0\uC5F0 \uC2DC\uAC04 (\uAE30\uBCF8\uAC12: 200ms, 0\uBCF4\uB2E4 \uD06C\uBA74 \uCD08\uAE30 \uC2A4\uD06C\uB864 \uC2DC \uC2A4\uD06C\uB864\uBC14 \uC228\uAE40)\n}\n\nexport interface OverlayScrollbarProps {\n className?: string;\n style?: React.CSSProperties; // wrapper div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n containerStyle?: React.CSSProperties; // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n contentStyle?: React.CSSProperties; // \uB0B4\uBD80 content div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n children: ReactNode;\n onScroll?: (event: Event) => void;\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\n thumb?: ThumbConfig; // \uC378 \uAD00\uB828 \uC124\uC815\n track?: TrackConfig; // \uD2B8\uB799 \uAD00\uB828 \uC124\uC815\n arrows?: ArrowsConfig; // \uD654\uC0B4\uD45C\uB4E4 \uAD00\uB828 \uC124\uC815\n dragScroll?: DragScrollConfig; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uAD00\uB828 \uC124\uC815\n autoHide?: AutoHideConfig; // \uC790\uB3D9 \uC228\uAE40 \uAD00\uB828 \uC124\uC815\n pullToRefresh?: PullToRefreshConfig; // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uAD00\uB828 \uC124\uC815(\uD130\uCE58 \uC804\uC6A9)\n\n // \uAE30\uD0C0 \uC124\uC815\uB4E4\n showScrollbar?: boolean; // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n showHorizontalScrollbar?: boolean; // \uD558\uB2E8(\uAC00\uB85C) \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n detectInnerScroll?: boolean; // children \uB0B4\uBD80\uC758 \uC2A4\uD06C\uB864 \uC694\uC18C \uAC10\uC9C0 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: false, \uAC00\uC0C1 \uD14C\uC774\uBE14 \uB4F1\uC5D0 \uC0AC\uC6A9)\n scrollTopFab?: boolean | ScrollTopFabConfig; // \"\uB9E8 \uC704\uB85C\" FAB \uD45C\uC2DC(\uAE30\uBCF8\uAC12: false). true \uBA74 \uAE30\uBCF8 \uC124\uC815\uC73C\uB85C, \uAC1D\uCCB4\uBA74 \uC138\uBD80 \uC124\uC815.\n}\n\n// \"\uB9E8 \uC704\uB85C\" FAB \uC124\uC815\nexport interface ScrollTopFabConfig {\n threshold?: number; // \uC774 \uAC12(px) \uC774\uC0C1 \uB0B4\uB824\uAC00\uBA74 FAB \uB97C \uD45C\uC2DC\uD55C\uB2E4 (\uAE30\uBCF8\uAC12: 600)\n idleOpacity?: number; // \uD3C9\uC0C1\uC2DC FAB \uBD88\uD22C\uBA85\uB3C4 \u2014 \uB0B4\uC6A9\uC744 \uAC00\uB9AC\uC9C0 \uC54A\uAC8C \uBC18\uD22C\uBA85 \uC720\uC9C0, \uB204\uB97C \uB54C\uB9CC 1 (\uAE30\uBCF8\uAC12: 0.45)\n scrollingOpacity?: number; // @deprecated idleOpacity \uB85C \uB300\uCCB4(\uD558\uC704\uD638\uD658\uC6A9 \uBCC4\uCE6D)\n hideDelay?: number; // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 \uC774 \uC2DC\uAC04(ms) \uD6C4 FAB \uB97C \uC790\uB3D9\uC73C\uB85C \uC228\uAE34\uB2E4 (\uAE30\uBCF8\uAC12: 1500, 0 \uC774\uBA74 \uC790\uB3D9\uC228\uAE40 \uC5C6\uC74C)\n position?: \"left\" | \"right\"; // FAB \uB97C \uC5B4\uB290 \uCABD \uD558\uB2E8\uC5D0 \uB458\uC9C0 (\uAE30\uBCF8\uAC12: \"right\")\n offset?: number; // \uD654\uBA74 \uAC00\uC7A5\uC790\uB9AC\uB85C\uBD80\uD130\uC758 \uC5EC\uBC31(px) (\uAE30\uBCF8\uAC12: 16)\n offsetBottom?: number; // \uC544\uB798\uCABD \uC5EC\uBC31(px)\uB9CC \uB530\uB85C \uC904 \uB54C (\uAE30\uBCF8\uAC12: offset). \uD558\uB2E8 \uBC14 \uC704\uB85C \uB744\uC6B0\uBA74\uC11C \uC606 \uC5EC\uBC31\uC740 \uADF8\uB300\uB85C \uB458 \uB54C \uC4F4\uB2E4.\n background?: string; // FAB \uBC30\uACBD\uC0C9 (\uAE30\uBCF8\uAC12: \"#1976d2\" \u2014 MUI \uAE30\uBCF8 primary)\n color?: string; // FAB \uC544\uC774\uCF58 \uC0C9 (\uAE30\uBCF8\uAC12: \"#ffffff\")\n size?: number; // FAB \uC9C0\uB984(px) (\uAE30\uBCF8\uAC12: 48)\n}\n\n// OverlayScrollbar\uAC00 \uB178\uCD9C\uD560 \uBA54\uC11C\uB4DC\uB4E4\nexport interface OverlayScrollbarRef {\n getScrollContainer: () => HTMLDivElement | null;\n scrollTo: (options: ScrollToOptions) => void;\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n}\n\n// \uAE30\uBCF8 \uC124\uC815 \uAC1D\uCCB4\uB4E4\uC744 \uCEF4\uD3EC\uB10C\uD2B8 \uC678\uBD80\uC5D0 \uC0C1\uC218\uB85C \uC120\uC5B8 (\uC7AC\uB80C\uB354\uB9C1 \uC2DC \uB3D9\uC77C\uD55C \uCC38\uC870 \uC720\uC9C0)\nconst DEFAULT_THUMB_CONFIG: ThumbConfig = {};\nconst DEFAULT_TRACK_CONFIG: TrackConfig = {};\nconst DEFAULT_ARROWS_CONFIG: ArrowsConfig = {};\nconst DEFAULT_DRAG_SCROLL_CONFIG: DragScrollConfig = {};\nconst DEFAULT_AUTO_HIDE_CONFIG: AutoHideConfig = {};\nconst DEFAULT_PULL_TO_REFRESH_CONFIG: PullToRefreshConfig = {};\n\nconst OverlayScrollbar = forwardRef<OverlayScrollbarRef, OverlayScrollbarProps>(\n (\n {\n className = \"\",\n style = {},\n containerStyle = {},\n contentStyle = {},\n children,\n onScroll,\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\n thumb = DEFAULT_THUMB_CONFIG,\n track = DEFAULT_TRACK_CONFIG,\n arrows = DEFAULT_ARROWS_CONFIG,\n dragScroll = DEFAULT_DRAG_SCROLL_CONFIG,\n autoHide = DEFAULT_AUTO_HIDE_CONFIG,\n pullToRefresh = DEFAULT_PULL_TO_REFRESH_CONFIG,\n\n // \uAE30\uD0C0 \uC124\uC815\uB4E4 (showScrollbar/showHorizontalScrollbar \uB294 \uD130\uCE58 \uAE30\uAE30\uC5D0\uC11C \uC544\uB798 \uD30C\uC0DD\uAC12\uC73C\uB85C \uAC15\uC81C off)\n showScrollbar: showScrollbarProp = true,\n showHorizontalScrollbar: showHorizontalScrollbarProp = true,\n detectInnerScroll = false,\n scrollTopFab = false,\n },\n ref,\n ) => {\n // \uD130\uCE58 \uC6B0\uC120 \uAE30\uAE30(\uBAA8\uBC14\uC77C/\uD0DC\uBE14\uB9BF) \uD310\uC815. \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uBC14\uB97C CSS \uB85C \uAC15\uC81C\uD574\uB3C4 \uBB34\uC2DC\uD558\uB294 \uC6F9\uBDF0\uAC00 \uC788\uC5B4,\n // \uD130\uCE58\uC5D0\uC11C\uB3C4 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774 \uC138\uB85C \uC2A4\uD06C\uB864\uBC14(\uC790\uCCB4 \uD2B8\uB799/thumb)\uB97C \uD45C\uC2DC\uD55C\uB2E4(\uD56D\uC0C1 \uBCF4\uC784 \uBCF4\uC7A5).\n // \uB2E4\uB9CC \u2460 \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14\uB294 \uB044\uACE0(\uD558\uB2E8 \uD68C\uC0C9\uC120 \uBC29\uC9C0) \u2461 \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC740 \uB048\uB2E4(\uC544\uB798 finalDragScrollConfig)\n // \u2014 \uD130\uCE58 \uC81C\uC2A4\uCC98\uAC00 \uCEE4\uC2A4\uD140 \uB4DC\uB798\uADF8 \uB85C\uC9C1\uC5D0 \uAC00\uB85C\uCC44\uC774\uC9C0 \uC54A\uAC8C.\n const isCoarsePointer = useCoarsePointer();\n // \uC138\uB85C \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14\uB294 \uC18C\uBE44\uCC98 \uAC12 \uADF8\uB300\uB85C(\uD130\uCE58\uC5D0\uC11C\uB3C4 \uD45C\uC2DC). \uAC00\uB85C\uB9CC \uD130\uCE58\uC5D0\uC11C \uAC15\uC81C off.\n const showScrollbar = showScrollbarProp;\n const showHorizontalScrollbar = isCoarsePointer ? false : showHorizontalScrollbarProp;\n\n // \uC778\uC2A4\uD134\uC2A4 \uACE0\uC720 \uD074\uB798\uC2A4 \u2014 \uC2A4\uD06C\uB864\uBC14 \uC228\uAE40/\uD45C\uC2DC CSS \uB97C \uC774 \uC778\uC2A4\uD134\uC2A4 \uCEE8\uD14C\uC774\uB108\uC5D0\uB9CC \uC801\uC6A9\uD574\n // \uC804\uC5ED `.overlay-scrollbar-container` \uC120\uD0DD\uC790\uAC00 \uD398\uC774\uC9C0\uC758 \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAE4C\uC9C0 \uC624\uC5FC\uC2DC\uD0A4\uC9C0 \uC54A\uAC8C \uD55C\uB2E4.\n // (fine/coarse \uD310\uC815\uC774 \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC11E\uC5EC\uB3C4 \uC11C\uB85C \uC2A4\uD06C\uB864\uBC14\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4.)\n const rawInstanceId = useId();\n const instanceClass = `os-${rawInstanceId.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n // \"\uB9E8 \uC704\uB85C\" FAB \uC124\uC815\uC744 \uC815\uADDC\uD654\uD55C\uB2E4(boolean \uC774\uBA74 \uAE30\uBCF8\uAC12, \uAC1D\uCCB4\uBA74 \uBCD1\uD569).\n const fabEnabled = scrollTopFab !== false && scrollTopFab != null;\n const fabConfig = typeof scrollTopFab === \"object\" ? scrollTopFab : {};\n const fabThreshold = fabConfig.threshold ?? 600;\n const fabIdleOpacity = fabConfig.idleOpacity ?? fabConfig.scrollingOpacity ?? 0.45;\n const fabHideDelay = fabConfig.hideDelay ?? 1500;\n const fabPosition = fabConfig.position ?? \"right\";\n const fabOffset = fabConfig.offset ?? 16;\n // \uC544\uB798\uCABD \uC5EC\uBC31\uC740 \uB530\uB85C \uBC1B\uC744 \uC218 \uC788\uB2E4 \u2014 \uD558\uB2E8 \uB0B4\uBE44\uAC8C\uC774\uC158 \uBC14\uAC00 \uC788\uB294 \uD654\uBA74\uC5D0\uC11C \uC606 \uC5EC\uBC31\uC740 \uADF8\uB300\uB85C \uB450\uACE0 \uC704\uB85C\uB9CC \uC62C\uB9B0\uB2E4.\n const fabOffsetBottom = fabConfig.offsetBottom ?? fabOffset;\n const fabBackground = fabConfig.background ?? \"#1976d2\";\n const fabColor = fabConfig.color ?? \"#ffffff\";\n const fabSize = fabConfig.size ?? 48;\n // FAB \uB178\uCD9C \uC5EC\uBD80 \u2014 \uCEE8\uD14C\uC774\uB108 scrollTop \uC774 threshold \uB97C \uB118\uC73C\uBA74 \uD45C\uC2DC\uD55C\uB2E4.\n const [showScrollTopFab, setShowScrollTopFab] = useState(false);\n // FAB \uB97C \uB204\uB974\uACE0 \uC788\uB294 \uB3D9\uC548\uB9CC \uC120\uBA85\uD558\uAC8C \uBCF4\uC5EC\uC8FC\uAE30 \uC704\uD55C \uC0C1\uD0DC(\uADF8 \uC678\uC5D0\uB294 \uBC18\uD22C\uBA85 \uC720\uC9C0).\n const [isFabPressed, setIsFabPressed] = useState(false);\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 FAB \uB97C \uC790\uB3D9\uC73C\uB85C \uC228\uAE30\uB294 \uD0C0\uC774\uBA38(\uC2A4\uD06C\uB864\uB9C8\uB2E4 \uB9AC\uC14B).\n const fabHideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // props \uBCC0\uACBD \uCD94\uC801\uC6A9 ref\n const prevPropsRef = useRef<{\n children?: ReactNode;\n onScroll?: (event: Event) => void;\n showScrollbar?: boolean;\n showHorizontalScrollbar?: boolean;\n thumb?: ThumbConfig;\n track?: TrackConfig;\n arrows?: ArrowsConfig;\n dragScroll?: DragScrollConfig;\n autoHide?: AutoHideConfig;\n }>({});\n\n // \uB80C\uB354\uB9C1 \uC2DC \uC5B4\uB5A4 prop\uC774 \uBCC0\uACBD\uB418\uC5C8\uB294\uC9C0 \uCCB4\uD06C\n useEffect(() => {\n // \uD604\uC7AC props \uC800\uC7A5\n prevPropsRef.current = {\n children,\n onScroll,\n showScrollbar,\n showHorizontalScrollbar,\n thumb,\n track,\n arrows,\n dragScroll,\n autoHide,\n };\n });\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const scrollbarRef = useRef<HTMLDivElement>(null);\n const thumbRef = useRef<HTMLDivElement>(null);\n\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 \uCE90\uC2F1\uC6A9 ref (\uC131\uB2A5 \uCD5C\uC801\uD654)\n const cachedScrollContainerRef = useRef<HTMLElement | null>(null);\n\n // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \u2014 \uCEE8\uD14C\uC774\uB108\uAC00 \uB9E8 \uC704\uC77C \uB54C \uD558\uD5A5 \uD130\uCE58 \uB2F9\uAE40\uC744 \uAC10\uC9C0\uD574 onRefresh \uB97C \uC2E4\uD589\uD55C\uB2E4.\n const {\n pullDistance,\n isRefreshing,\n pullProgress,\n } = usePullToRefresh({ containerRef, config: pullToRefresh });\n const pullIndicatorColor = pullToRefresh.indicatorColor ?? \"#1976d2\";\n const pullIndicatorVisible = pullDistance > 0 || isRefreshing;\n\n // \uAE30\uBCF8 \uC0C1\uD0DC\uB4E4\n const [scrollbarVisible, setScrollbarVisible] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isThumbHovered, setIsThumbHovered] = useState(false);\n const [dragStart, setDragStart] = useState({ y: 0, scrollTop: 0 });\n const [thumbHeight, setThumbHeight] = useState(0);\n const [thumbTop, setThumbTop] = useState(0);\n const [hasScrollableContent, setHasScrollableContent] = useState(false);\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uC0C1\uD0DC\n const [isHorizontalThumbHovered, setIsHorizontalThumbHovered] =\n useState(false);\n const [isDraggingHorizontal, setIsDraggingHorizontal] = useState(false);\n const [dragStartHorizontal, setDragStartHorizontal] = useState({\n x: 0,\n scrollLeft: 0,\n });\n const [thumbWidth, setThumbWidth] = useState(0);\n const [thumbLeft, setThumbLeft] = useState(0);\n const [\n hasHorizontalScrollableContent,\n setHasHorizontalScrollableContent,\n ] = useState(false);\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC0C1\uD0DC\n const isDragScrollPendingRef = useRef(false);\n const [isDragScrolling, setIsDragScrolling] = useState(false);\n // \uB05D\uB0BC \uB54C\uC758 \uD310\uC815\uC6A9 \u2014 \uC9E7\uAC8C \uB04C\uACE0 \uBC14\uB85C \uB5BC\uBA74 mouseup \uC774 \uB9AC\uB80C\uB354\uBCF4\uB2E4 \uBA3C\uC800 \uC640 state \uB294 \uC544\uC9C1 false \uB77C,\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB97C \uAC78\uC9C0 \uC54A\uACE0 \uB05D\uB098 \uC2A4\uD06C\uB864\uBC14\uAC00 \uB0A8\uC558\uB2E4.\n const isDragScrollingRef = useRef(false);\n const [dragScrollStart, setDragScrollStart] = useState({\n x: 0,\n y: 0,\n scrollTop: 0,\n scrollLeft: 0,\n });\n const [activeArrow, setActiveArrow] = useState<\"up\" | \"down\" | null>(\n null,\n );\n const [hoveredArrow, setHoveredArrow] = useState<\"up\" | \"down\" | null>(\n null,\n );\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC2DC hover \uBC29\uC9C0\uC6A9\n const [isInitialized, setIsInitialized] = useState(false);\n\n // wrapper\uC758 padding-bottom \uC800\uC7A5\n const [wrapperPaddingBottom, setWrapperPaddingBottom] = useState(0);\n\n // \uD720 \uC2A4\uD06C\uB864 \uAC10\uC9C0\uC6A9\n const wheelTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n // state \uAC00 \uC544\uB2C8\uB77C ref \uB2E4 \u2014 state \uB85C \uB450\uBA74 \uAC12\uC774 \uBC14\uB014 \uB54C\uB9C8\uB2E4 \uC2A4\uD06C\uB864 \uB9AC\uC2A4\uB108 effect \uAC00 \uB2E4\uC2DC \uB3CC\uBA74\uC11C\n // \uBC29\uAE08 \uAC74 \uD720 \uD0C0\uC774\uBA38\uB97C \uC9C0\uC6CC, \uD720 \uD55C \uCE78\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14\uAC00 \uC548 \uB728\uACE0 \"\uD720 \uC911\" \uC0C1\uD0DC\uB3C4 \uD480\uB9AC\uC9C0 \uC54A\uC558\uB2E4.\n const isWheelScrollingRef = useRef(false);\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 (\uB514\uBC14\uC6B4\uC2A4\uC6A9)\n const hoverEnterTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uD45C\uC2DC \uD0C0\uC774\uBA38 (\uB514\uBC14\uC6B4\uC2A4\uC6A9)\n const wheelShowTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\uC5D0 \uAE30\uBCF8\uAC12 \uC124\uC815\n const finalThumbConfig = useMemo(() => {\n const baseColor = thumb.color ?? \"#606060\";\n return {\n width: thumb.width ?? 8,\n minHeight: thumb.minHeight ?? 50,\n radius: thumb.radius ?? (thumb.width ?? 8) / 2,\n color: baseColor,\n opacity: thumb.opacity ?? 0.6,\n hoverColor: thumb.hoverColor ?? baseColor,\n hoverOpacity: thumb.hoverOpacity ?? 1.0,\n };\n }, [thumb]);\n\n const finalTrackConfig = useMemo(\n () => ({\n width: track.width ?? 16,\n color: track.color ?? \"rgba(128, 128, 128, 0.1)\",\n visible: track.visible ?? true,\n alignment: track.alignment ?? \"outside\",\n radius: track.radius ?? finalThumbConfig.radius ?? 4,\n margin: track.margin ?? 4,\n overflowX: track.overflowX ?? true,\n overflowY: track.overflowY ?? true,\n }),\n [track, finalThumbConfig.radius],\n );\n\n const finalArrowsConfig = useMemo(() => {\n const baseColor = arrows.color ?? \"#808080\";\n return {\n visible: arrows.visible ?? false,\n step: arrows.step ?? 50,\n color: baseColor,\n opacity: arrows.opacity ?? 0.6,\n hoverColor: arrows.hoverColor ?? baseColor,\n hoverOpacity: arrows.hoverOpacity ?? 1.0,\n };\n }, [arrows]);\n\n const finalDragScrollConfig = useMemo(\n () => ({\n // \uD130\uCE58 \uAE30\uAE30\uB294 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uC774 \uC790\uC5F0\uC2A4\uB7EC\uC6B0\uBBC0\uB85C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uB048\uB2E4(\uC18C\uBE44\uCC98 prop \uBB34\uAD00).\n enabled: isCoarsePointer ? false : dragScroll.enabled ?? true,\n excludeClasses: dragScroll.excludeClasses ?? [],\n excludeSelectors: dragScroll.excludeSelectors ?? [],\n }),\n [dragScroll, isCoarsePointer],\n );\n\n const finalAutoHideConfig = useMemo(\n () => ({\n enabled: autoHide.enabled ?? true,\n delay: autoHide.delay ?? 1500,\n delayOnWheel: autoHide.delayOnWheel ?? 700,\n initialDelay: autoHide.initialDelay ?? 200,\n }),\n [autoHide],\n );\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC2DC \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC9C0\uC5F0 \uC0C1\uD0DC\n const [isInitialDelayActive, setIsInitialDelayActive] = useState(\n () => (autoHide.initialDelay ?? 200) > 0,\n );\n\n // \uD638\uD658\uC131\uC744 \uC704\uD55C \uBCC0\uC218\uB4E4 (\uC790\uC8FC \uC0AC\uC6A9\uB418\uB294 \uBCC0\uC218\uB4E4\uB9CC \uC720\uC9C0)\n const finalThumbWidth = finalThumbConfig.width;\n const finalTrackWidth = finalTrackConfig.width;\n const thumbMinHeight = finalThumbConfig.minHeight;\n const showArrows = finalArrowsConfig.visible;\n const arrowStep = finalArrowsConfig.step;\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 \uD568\uC218 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n const maintainFocus = useCallback(() => {\n if (!containerRef.current) return;\n\n // \uD604\uC7AC \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C \uD655\uC778\n const activeElement = document.activeElement;\n\n // \uC624\uBC84\uB808\uC774 \uC2A4\uD06C\uB864\uBC14 \uB0B4\uBD80\uC5D0 \uC774\uBBF8 \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C\uAC00 \uC788\uC73C\uBA74 \uC2A4\uD0B5\n if (\n activeElement &&\n containerRef.current.contains(activeElement) &&\n activeElement !== containerRef.current\n ) {\n return;\n }\n\n // \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C\uAC00 \uC5C6\uAC70\uB098 \uC678\uBD80\uC5D0 \uC788\uC73C\uBA74 \uCEE8\uD14C\uC774\uB108\uC5D0 \uD3EC\uCEE4\uC2A4\n containerRef.current.focus();\n }, []);\n\n // ref\uB97C \uD1B5\uD574 \uC678\uBD80\uC5D0\uC11C \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC5D0 \uC811\uADFC\uD560 \uC218 \uC788\uB3C4\uB85D \uD568\n useImperativeHandle(\n ref,\n () => ({\n getScrollContainer: () => containerRef.current,\n scrollTo: (options: ScrollToOptions) => {\n if (containerRef.current) {\n containerRef.current.scrollTo(options);\n }\n },\n get scrollTop() {\n return containerRef.current?.scrollTop || 0;\n },\n get scrollHeight() {\n return containerRef.current?.scrollHeight || 0;\n },\n get clientHeight() {\n return containerRef.current?.clientHeight || 0;\n },\n }),\n [],\n );\n\n // \uC2E4\uC81C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30 (\uCE90\uC2F1 \uCD5C\uC801\uD654)\n const findScrollableElement = useCallback((): HTMLElement | null => {\n // \uCE90\uC2DC\uB41C \uC694\uC18C\uAC00 \uC5EC\uC804\uD788 \uC720\uD6A8\uD55C\uC9C0 \uD655\uC778\n if (cachedScrollContainerRef.current) {\n const cached = cachedScrollContainerRef.current;\n // DOM\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uACE0 \uC5EC\uC804\uD788 \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C\uC9C0 \uD655\uC778\n if (\n document.contains(cached) &&\n (cached.scrollHeight > cached.clientHeight + 2 ||\n cached.scrollWidth > cached.clientWidth + 2)\n ) {\n return cached;\n }\n // \uCE90\uC2DC \uBB34\uD6A8\uD654\n cachedScrollContainerRef.current = null;\n }\n\n if (!containerRef.current) {\n return null;\n }\n\n // \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uC758 \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC5EC\uBD80 \uD655\uC778\n if (\n containerRef.current.scrollHeight >\n containerRef.current.clientHeight + 2 ||\n containerRef.current.scrollWidth >\n containerRef.current.clientWidth + 2\n ) {\n cachedScrollContainerRef.current = containerRef.current;\n return containerRef.current;\n }\n\n // detectInnerScroll \uC635\uC158\uC774 \uD65C\uC131\uD654\uB41C \uACBD\uC6B0\uC5D0\uB9CC children \uB0B4\uBD80\uC758 \uC2A4\uD06C\uB864 \uC694\uC18C \uCC3E\uAE30\n // (\uAC00\uC0C1 \uD14C\uC774\uBE14 \uB4F1 \uB0B4\uBD80\uC5D0\uC11C \uC2A4\uD06C\uB864\uC744 \uCC98\uB9AC\uD558\uB294 \uACBD\uC6B0\uC5D0 \uC0AC\uC6A9)\n if (!detectInnerScroll) {\n return null;\n }\n\n // children \uC694\uC18C\uC5D0\uC11C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30\n // \uC911\uCCA9\uB41C OverlayScrollbar\uC758 \uC601\uC5ED\uC740 \uC81C\uC678 (\uB2E4\uB978 OverlayScrollbar\uC758 container\uB294 \uC2A4\uD0B5)\n const childScrollableElements =\n containerRef.current.querySelectorAll(\n '[data-virtuoso-scroller], [style*=\"overflow\"], .virtuoso-scroller, [style*=\"overflow: auto\"], [style*=\"overflow:auto\"]',\n );\n\n for (const child of childScrollableElements) {\n const element = child as HTMLElement;\n\n // \uC774 \uC694\uC18C\uAC00 \uB2E4\uB978 OverlayScrollbar\uC758 container\uC778\uC9C0 \uD655\uC778\n // (\uC790\uC2E0\uC758 containerRef\uB294 \uC544\uB2C8\uC5B4\uC57C \uD558\uACE0, overlay-scrollbar-container \uD074\uB798\uC2A4\uB97C \uAC00\uC9C4 \uACBD\uC6B0)\n if (\n element !== containerRef.current &&\n element.classList.contains(\"overlay-scrollbar-container\")\n ) {\n // \uC911\uCCA9\uB41C OverlayScrollbar\uC758 container\uC774\uBBC0\uB85C \uC2A4\uD0B5\n continue;\n }\n\n // \uC774 \uC694\uC18C\uC758 \uBD80\uBAA8 \uC911\uC5D0 \uB2E4\uB978 OverlayScrollbar container\uAC00 \uC788\uB294\uC9C0 \uD655\uC778\n let parent: HTMLElement | null = element.parentElement;\n let isNestedInAnotherScrollbar = false;\n\n while (parent && parent !== containerRef.current) {\n if (\n parent.classList.contains(\n \"overlay-scrollbar-container\",\n ) &&\n parent !== containerRef.current\n ) {\n // \uB2E4\uB978 OverlayScrollbar \uB0B4\uBD80\uC758 \uC694\uC18C\uC774\uBBC0\uB85C \uC2A4\uD0B5\n isNestedInAnotherScrollbar = true;\n break;\n }\n parent = parent.parentElement;\n }\n\n if (isNestedInAnotherScrollbar) {\n continue;\n }\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C\uC778\uC9C0 \uD655\uC778\n if (\n element.scrollHeight > element.clientHeight + 2 ||\n element.scrollWidth > element.clientWidth + 2\n ) {\n cachedScrollContainerRef.current = element;\n return element;\n }\n }\n\n return null;\n }, []);\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC5EC\uBD80 \uCCB4\uD06C\n const isScrollable = useCallback(() => {\n return findScrollableElement() !== null;\n }, [findScrollableElement]);\n\n // \uD0C0\uC774\uBA38 \uC815\uB9AC\n const clearHideTimer = useCallback(() => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }, []);\n\n const clearHoverEnterTimer = useCallback(() => {\n if (hoverEnterTimeoutRef.current) {\n clearTimeout(hoverEnterTimeoutRef.current);\n hoverEnterTimeoutRef.current = null;\n }\n }, []);\n\n const clearWheelShowTimer = useCallback(() => {\n if (wheelShowTimeoutRef.current) {\n clearTimeout(wheelShowTimeoutRef.current);\n wheelShowTimeoutRef.current = null;\n }\n }, []);\n\n // \uC2A4\uD06C\uB864 \uC601\uC5ED \uBC14\uAE65\uC73C\uB85C \uB098\uAC00\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uC989\uC2DC \uC228\uAE34\uB2E4.\n const handleWrapperMouseLeave = useCallback(() => {\n clearHoverEnterTimer();\n\n if (\n isDragging ||\n isDraggingHorizontal ||\n isDragScrolling ||\n isDragScrollPendingRef.current\n ) {\n return;\n }\n\n clearHideTimer();\n setScrollbarVisible(false);\n }, [\n clearHideTimer,\n clearHoverEnterTimer,\n isDragging,\n isDraggingHorizontal,\n isDragScrolling,\n ]);\n\n // \uC2A4\uD06C\uB864\uBC14 \uC228\uAE30\uAE30 \uD0C0\uC774\uBA38\n const setHideTimer = useCallback(\n (delay: number) => {\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uC73C\uBA74 \uD0C0\uC774\uBA38\uB97C \uC124\uC815\uD558\uC9C0 \uC54A\uC74C\n if (!finalAutoHideConfig.enabled) {\n return;\n }\n clearHideTimer();\n hideTimeoutRef.current = setTimeout(() => {\n setScrollbarVisible(false);\n hideTimeoutRef.current = null;\n }, delay);\n },\n [clearHideTimer, finalAutoHideConfig.enabled],\n );\n\n // \uC2A4\uD06C\uB864\uBC14 \uC704\uCE58 \uBC0F \uD06C\uAE30 \uC5C5\uB370\uC774\uD2B8\n const updateScrollbar = useCallback(() => {\n // \uC6B0\uC120 \uAC10\uC9C0 \uB85C\uC9C1\uC744 \uC0AC\uC6A9\uD558\uACE0, \uC2E4\uD328 \uC2DC container\uB97C fallback\uC73C\uB85C \uC0AC\uC6A9\uD55C\uB2E4.\n // horizontal-only \uCF00\uC774\uC2A4\uC5D0\uC11C \uAC10\uC9C0 \uD0C0\uC774\uBC0D \uC774\uC288\uAC00 \uBC1C\uC0DD\uD574\uB3C4 \uAC00\uB85C \uC624\uBC84\uD50C\uB85C\uC6B0 \uACC4\uC0B0\uC744 \uC720\uC9C0\uD55C\uB2E4.\n const scrollableElement =\n findScrollableElement() ?? containerRef.current;\n if (!scrollableElement) {\n // \uCE21\uC815 \uB300\uC0C1\uC774 \uC5C6\uC73C\uBA74 \uC228\uAE40\n setScrollbarVisible(false);\n setHasScrollableContent(false);\n setHasHorizontalScrollableContent(false);\n clearHideTimer();\n return;\n }\n\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uACE0 \uCD08\uAE30 \uC9C0\uC5F0\uC774 \uB05D\uB0AC\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uD56D\uC0C1 \uD45C\uC2DC\n if (!finalAutoHideConfig.enabled && !isInitialDelayActive) {\n setScrollbarVisible(true);\n clearHideTimer();\n }\n\n const containerHeight = scrollableElement.clientHeight;\n const contentHeight = scrollableElement.scrollHeight;\n const scrollTop = scrollableElement.scrollTop;\n\n const containerWidth = scrollableElement.clientWidth;\n const contentWidth = scrollableElement.scrollWidth;\n const scrollLeft = scrollableElement.scrollLeft;\n const hasVerticalOverflow = contentHeight - containerHeight > 0;\n const hasHorizontalOverflow = contentWidth - containerWidth > 0;\n const hasAnyOverflow = hasVerticalOverflow || hasHorizontalOverflow;\n\n // \uCD95\uBCC4 \uC624\uBC84\uD50C\uB85C\uC6B0 \uC0C1\uD0DC \uBC18\uC601\n setHasScrollableContent(hasVerticalOverflow);\n setHasHorizontalScrollableContent(hasHorizontalOverflow);\n\n // \uC624\uBC84\uD50C\uB85C\uC6B0\uAC00 \uC5C6\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uC228\uAE34\uB2E4.\n if (!hasAnyOverflow) {\n setScrollbarVisible(false);\n clearHideTimer();\n }\n\n // wrapper\uC758 \uD328\uB529 \uACC4\uC0B0 (\uC0C1\uD558 \uD328\uB529\uB9CC \uD544\uC694)\n let wrapperPaddingTopBottom = 0;\n let paddingBottom = 0;\n if (wrapperRef.current) {\n const computedStyle = window.getComputedStyle(\n wrapperRef.current,\n );\n const paddingTop = parseFloat(computedStyle.paddingTop) || 0;\n paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;\n wrapperPaddingTopBottom = paddingTop + paddingBottom;\n setWrapperPaddingBottom(paddingBottom);\n }\n\n if (hasVerticalOverflow) {\n // \uD654\uC0B4\uD45C\uC640 \uAC04\uACA9 \uACF5\uAC04 \uACC4\uC0B0 (\uD654\uC0B4\uD45C + \uC704\uC544\uB798 \uB9C8\uC9C4, \uD654\uC0B4\uD45C \uC5C6\uC5B4\uB3C4 \uC704\uC544\uB798 \uB9C8\uC9C4)\n const arrowSpace = showArrows\n ? finalThumbWidth * 2 + finalTrackConfig.margin * 4\n : finalTrackConfig.margin * 2;\n\n // \uC378 \uB192\uC774 \uACC4\uC0B0 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uCD5C\uC18C \uB192\uC774 \uC0AC\uC6A9, \uD654\uC0B4\uD45C \uACF5\uAC04 \uC81C\uC678, wrapper \uD328\uB529 \uCD94\uAC00)\n const availableHeight =\n containerHeight - arrowSpace + wrapperPaddingTopBottom;\n const scrollRatio = containerHeight / contentHeight;\n const calculatedThumbHeight = Math.max(\n availableHeight * scrollRatio,\n thumbMinHeight,\n );\n\n // \uC378 \uC704\uCE58 \uACC4\uC0B0 (\uD654\uC0B4\uD45C\uC640 \uAC04\uACA9 \uACF5\uAC04 \uC81C\uC678)\n const scrollableHeight = contentHeight - containerHeight;\n const thumbScrollableHeight =\n availableHeight - calculatedThumbHeight;\n const calculatedThumbTop =\n scrollableHeight > 0\n ? (scrollTop / scrollableHeight) * thumbScrollableHeight\n : 0;\n\n setThumbHeight(calculatedThumbHeight);\n setThumbTop(calculatedThumbTop);\n } else {\n setThumbHeight(0);\n setThumbTop(0);\n }\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uACC4\uC0B0\n const horizontalScrollableWidth = contentWidth - containerWidth;\n if (horizontalScrollableWidth > 0) {\n const scrollRatioHorizontal = containerWidth / contentWidth;\n const calculatedThumbWidth = Math.max(\n containerWidth * scrollRatioHorizontal,\n 50, // \uCD5C\uC18C \uB108\uBE44\n );\n\n const thumbScrollableWidth =\n containerWidth - calculatedThumbWidth;\n const calculatedThumbLeft =\n horizontalScrollableWidth > 0\n ? (scrollLeft / horizontalScrollableWidth) *\n thumbScrollableWidth\n : 0;\n\n setThumbWidth(calculatedThumbWidth);\n setThumbLeft(calculatedThumbLeft);\n } else {\n setThumbWidth(0);\n setThumbLeft(0);\n }\n }, [\n findScrollableElement,\n clearHideTimer,\n showArrows,\n finalThumbWidth,\n thumbMinHeight,\n finalAutoHideConfig.enabled,\n isInitialDelayActive,\n ]);\n\n // \uC378 \uB4DC\uB798\uADF8 \uC2DC\uC791\n const handleThumbMouseDown = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n setIsDragging(true);\n setDragStart({\n y: event.clientY,\n scrollTop: actualScrollContainer.scrollTop,\n });\n\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [findScrollableElement, clearHideTimer, maintainFocus],\n );\n\n // \uC378 \uB4DC\uB798\uADF8 \uC911\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!isDragging) return;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerHeight = actualScrollContainer.clientHeight;\n const contentHeight = actualScrollContainer.scrollHeight;\n const scrollableHeight = contentHeight - containerHeight;\n\n const deltaY = event.clientY - dragStart.y;\n const thumbScrollableHeight = containerHeight - thumbHeight;\n const scrollDelta =\n (deltaY / thumbScrollableHeight) * scrollableHeight;\n\n const newScrollTop = Math.max(\n 0,\n Math.min(\n scrollableHeight,\n dragStart.scrollTop + scrollDelta,\n ),\n );\n\n actualScrollContainer.scrollTop = newScrollTop;\n updateScrollbar();\n },\n [\n isDragging,\n dragStart,\n thumbHeight,\n updateScrollbar,\n findScrollableElement,\n ],\n );\n\n // \uC378 \uB4DC\uB798\uADF8 \uC885\uB8CC\n const handleMouseUp = useCallback(() => {\n setIsDragging(false);\n if (isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay); // \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uD2B8\uB799 \uD074\uB9AD\uC73C\uB85C \uC2A4\uD06C\uB864 \uC810\uD504\n const handleTrackClick = useCallback(\n (event: React.MouseEvent) => {\n if (!scrollbarRef.current) {\n return;\n }\n\n const scrollbar = scrollbarRef.current;\n const rect = scrollbar.getBoundingClientRect();\n const clickY = event.clientY - rect.top;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerHeight = actualScrollContainer.clientHeight;\n const contentHeight = actualScrollContainer.scrollHeight;\n\n const scrollRatio = clickY / containerHeight;\n const newScrollTop =\n scrollRatio * (contentHeight - containerHeight);\n\n actualScrollContainer.scrollTop = Math.max(\n 0,\n Math.min(contentHeight - containerHeight, newScrollTop),\n );\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n finalAutoHideConfig.delay,\n findScrollableElement,\n maintainFocus,\n ],\n );\n\n // \uC704\uCABD \uD654\uC0B4\uD45C \uD074\uB9AD \uD578\uB4E4\uB7EC\n const handleUpArrowClick = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n if (!containerRef.current) return;\n\n const newScrollTop = Math.max(\n 0,\n containerRef.current.scrollTop - arrowStep,\n );\n\n containerRef.current.scrollTop = newScrollTop;\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n arrowStep,\n finalAutoHideConfig.delay,\n maintainFocus,\n ],\n );\n\n // \uC544\uB798\uCABD \uD654\uC0B4\uD45C \uD074\uB9AD \uD578\uB4E4\uB7EC\n const handleDownArrowClick = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n if (!containerRef.current || !contentRef.current) return;\n\n const container = containerRef.current;\n const content = contentRef.current;\n const maxScrollTop =\n content.scrollHeight - container.clientHeight;\n const newScrollTop = Math.min(\n maxScrollTop,\n container.scrollTop + arrowStep,\n );\n\n container.scrollTop = newScrollTop;\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n arrowStep,\n finalAutoHideConfig.delay,\n maintainFocus,\n ],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC2DC\uC791\n const handleHorizontalThumbMouseDown = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n setIsDraggingHorizontal(true);\n setDragStartHorizontal({\n x: event.clientX,\n scrollLeft: actualScrollContainer.scrollLeft,\n });\n\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [findScrollableElement, clearHideTimer, maintainFocus],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC911\n const handleHorizontalMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!isDraggingHorizontal) return;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerWidth = actualScrollContainer.clientWidth;\n const contentWidth = actualScrollContainer.scrollWidth;\n const scrollableWidth = contentWidth - containerWidth;\n\n const deltaX = event.clientX - dragStartHorizontal.x;\n const thumbScrollableWidth = containerWidth - thumbWidth;\n const scrollDelta =\n (deltaX / thumbScrollableWidth) * scrollableWidth;\n\n const newScrollLeft = Math.max(\n 0,\n Math.min(\n scrollableWidth,\n dragStartHorizontal.scrollLeft + scrollDelta,\n ),\n );\n\n actualScrollContainer.scrollLeft = newScrollLeft;\n updateScrollbar();\n },\n [\n isDraggingHorizontal,\n dragStartHorizontal,\n thumbWidth,\n updateScrollbar,\n findScrollableElement,\n ],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC885\uB8CC\n const handleHorizontalMouseUp = useCallback(() => {\n setIsDraggingHorizontal(false);\n if (isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay); // \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uAC00\uB85C \uD2B8\uB799 \uD074\uB9AD\uC73C\uB85C \uC2A4\uD06C\uB864 \uC810\uD504\n const handleHorizontalTrackClick = useCallback(\n (event: React.MouseEvent) => {\n if (!scrollbarRef.current) {\n return;\n }\n\n const scrollbar = scrollbarRef.current;\n const rect = scrollbar.getBoundingClientRect();\n const clickX = event.clientX - rect.left;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerWidth = actualScrollContainer.clientWidth;\n const contentWidth = actualScrollContainer.scrollWidth;\n\n const scrollRatio = clickX / containerWidth;\n const newScrollLeft =\n scrollRatio * (contentWidth - containerWidth);\n\n actualScrollContainer.scrollLeft = Math.max(\n 0,\n Math.min(contentWidth - containerWidth, newScrollLeft),\n );\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n finalAutoHideConfig.delay,\n findScrollableElement,\n maintainFocus,\n ],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC2DC\uC791\n const handleDragScrollStart = useCallback(\n (event: React.MouseEvent) => {\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uBE44\uD65C\uC131\uD654\uB41C \uACBD\uC6B0\n if (!finalDragScrollConfig.enabled) return;\n\n // \uB354 \uAC00\uAE4C\uC6B4(\uC548\uCABD) OverlayScrollbar \uC778\uC2A4\uD134\uC2A4\uAC00 \uC774\uBBF8 \uC774 press \uB85C \uB4DC\uB798\uADF8\uB97C\n // \uC2DC\uC791\uD588\uC73C\uBA74(\uC2A4\uD06C\uB864 \uAC00\uB2A5 \u2192 preventDefault \uD638\uCD9C) \uBC14\uAE65 \uC778\uC2A4\uD134\uC2A4\uB294 \uC591\uBCF4\uD55C\uB2E4.\n // \uC548\uCABD \uC778\uC2A4\uD134\uC2A4\uAC00 \uC2A4\uD06C\uB864\uD560 \uAC8C \uC5C6\uC73C\uBA74 preventDefault \uC5C6\uC774 \uBC18\uD658\uD558\uBBC0\uB85C\n // \uC790\uC5F0\uC2A4\uB7FD\uAC8C \uBC14\uAE65 \uC778\uC2A4\uD134\uC2A4\uB85C \uB4DC\uB798\uADF8\uAC00 \uC804\uD30C\uB41C\uB2E4.\n if (event.defaultPrevented) return;\n\n // \uD14D\uC2A4\uD2B8 \uC785\uB825 \uC694\uC18C\uB098 \uC81C\uC678 \uB300\uC0C1\uC774\uBA74 \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uD558\uC9C0 \uC54A\uC74C\n const target = event.target as Element;\n\n // React \uD3EC\uD138\uB85C \uB80C\uB354\uB41C \uC790\uC2DD(\uC911\uCCA9 \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB4F1)\uC758 press \uB294 React \uD2B8\uB9AC\uB85C\n // \uBC84\uBE14\uB9C1\uB418\uC5B4 \uC774 \uD578\uB4E4\uB7EC\uAE4C\uC9C0 \uB3C4\uB2EC\uD558\uC9C0\uB9CC DOM \uC0C1\uC73C\uB85C\uB294 \uC774 \uCEE8\uD14C\uC774\uB108 \uBC16\uC774\uB2E4.\n // \uC2E4\uC81C DOM \uCEE8\uD14C\uC774\uB108 \uB0B4\uBD80 press \uB9CC \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uB300\uC0C1\uC73C\uB85C \uC0BC\uB294\uB2E4.\n // (\uD3EC\uD138 \uC704 \uB2E4\uC774\uC5BC\uB85C\uADF8\uC5D0 \uC2A4\uD06C\uB864\uC774 \uC5C6\uC5B4\uB3C4 \uC544\uB798 \uB2E4\uC774\uC5BC\uB85C\uADF8\uB85C \uC804\uD30C\uD558\uC9C0 \uC54A\uB294\uB2E4)\n const container = containerRef.current;\n if (!container || !container.contains(target)) return;\n\n if (isTextInputElement(target, finalDragScrollConfig)) {\n return;\n }\n\n // \uC624\uB978\uCABD \uD074\uB9AD\uC774\uB098 \uD720 \uD074\uB9AD\uC740 \uC81C\uC678\n if (event.button !== 0) return;\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC601\uC5ED\uC774 \uC544\uB2C8\uBA74 \uC81C\uC678\n if (\n scrollableElement.scrollHeight <=\n scrollableElement.clientHeight &&\n scrollableElement.scrollWidth <=\n scrollableElement.clientWidth\n )\n return;\n\n event.preventDefault();\n // preventDefault \uB294 \uBE0C\uB77C\uC6B0\uC800\uC758 \"\uBE48 \uACF3\uC744 \uB204\uB974\uBA74 \uD3EC\uCEE4\uC2A4 \uD574\uC81C\" \uB3C4 \uB9C9\uB294\uB2E4 \u2014 \uC785\uB825\uCE78\uC5D0 \uD3EC\uCEE4\uC2A4\uAC00\n // \uB0A8\uC544 \uBE48 \uACF3\uC744 \uB20C\uB7EC\uB3C4 \uD3EC\uCEE4\uC2A4 \uD14C\uB450\uB9AC\uAC00 \uD480\uB9AC\uC9C0 \uC54A\uC558\uB2E4. \uB204\uB978 \uACF3 \uBC16\uC5D0 \uC788\uB294 \uD3EC\uCEE4\uC2A4\uB294 \uC9C1\uC811 \uD47C\uB2E4.\n const active = document.activeElement;\n if (\n active instanceof HTMLElement &&\n active !== document.body &&\n !active.contains(target)\n ) {\n active.blur();\n }\n isDragScrollPendingRef.current = true;\n setDragScrollStart({\n x: event.clientX,\n y: event.clientY,\n scrollTop: scrollableElement.scrollTop,\n scrollLeft: scrollableElement.scrollLeft || 0,\n });\n\n // \uC2A4\uD06C\uB864\uBC14\uB294 \uC2E4\uC81C \uB4DC\uB798\uADF8\uAC00 \uBC1C\uC0DD\uD560 \uB54C \uD45C\uC2DC (handleDragScrollMove\uC5D0\uC11C \uCC98\uB9AC)\n // \u26A0\uFE0F \uC5EC\uAE30\uC11C \uC228\uAE40 \uD0C0\uC774\uBA38\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uB204\uB974\uAE30\uB9CC \uD558\uACE0 \uB04C\uC9C0 \uC54A\uC73C\uBA74(\uADF8\uB0E5 \uD074\uB9AD) \uB2E4\uC2DC \uAC78\uC5B4 \uC8FC\uB294 \uACF3\uC774\n // \uC5C6\uC5B4, \uC2A4\uD06C\uB864 \uC9C1\uD6C4 \uBCF8\uBB38\uC744 \uD074\uB9AD\uD558\uBA74 \uC2A4\uD06C\uB864\uBC14\uAC00 \uC0AC\uB77C\uC9C0\uC9C0 \uC54A\uACE0 \uACC4\uC18D \uB0A8\uC558\uB2E4.\n },\n [finalDragScrollConfig, isTextInputElement, findScrollableElement],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC911\n const handleDragScrollMove = useCallback(\n (event: MouseEvent) => {\n if (!isDragScrollPendingRef.current && !isDragScrolling) return;\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n const deltaX = dragScrollStart.x - event.clientX;\n const deltaY = dragScrollStart.y - event.clientY;\n\n // 5px \uC774\uC0C1 \uC6C0\uC9C1\uC600\uC744 \uB54C\uB9CC \uC2E4\uC81C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC2DC\uC791\uD55C\uB2E4.\n if (Math.abs(deltaY) < 5 && Math.abs(deltaX) < 5) {\n return;\n }\n\n if (isDragScrollPendingRef.current) {\n isDragScrollPendingRef.current = false;\n isDragScrollingRef.current = true;\n setIsDragScrolling(true);\n }\n\n // \uC2E4\uC81C \uB4DC\uB798\uADF8\uAC00 \uBC1C\uC0DD\uD588\uC73C\uBBC0\uB85C \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n setScrollbarVisible(true);\n\n // \uC138\uB85C \uC2A4\uD06C\uB864 \uCC98\uB9AC\n const newScrollTop = Math.max(\n 0,\n Math.min(\n scrollableElement.scrollHeight -\n scrollableElement.clientHeight,\n dragScrollStart.scrollTop + deltaY,\n ),\n );\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864 \uCC98\uB9AC\n const newScrollLeft = Math.max(\n 0,\n Math.min(\n scrollableElement.scrollWidth -\n scrollableElement.clientWidth,\n dragScrollStart.scrollLeft + deltaX,\n ),\n );\n\n scrollableElement.scrollTop = newScrollTop;\n scrollableElement.scrollLeft = newScrollLeft;\n updateScrollbar();\n },\n [\n isDragScrolling,\n dragScrollStart,\n findScrollableElement,\n updateScrollbar,\n ],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC885\uB8CC\n const handleDragScrollEnd = useCallback(() => {\n const wasDragScrolling = isDragScrollingRef.current;\n isDragScrollPendingRef.current = false;\n isDragScrollingRef.current = false;\n setIsDragScrolling(false);\n\n if (wasDragScrolling && isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uC2A4\uD06C\uB864 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 (externalScrollContainer \uC6B0\uC120 \uC0AC\uC6A9)\n useEffect(() => {\n const handleScroll = (event: Event) => {\n updateScrollbar();\n\n // \"\uB9E8 \uC704\uB85C\" FAB \uB178\uCD9C \uC5EC\uBD80 \uAC31\uC2E0(\uD65C\uC131 \uC2DC\uC5D0\uB9CC).\n // \uC784\uACC4\uAC12\uC744 \uB118\uC73C\uBA74 **\uBC18\uD22C\uBA85 \uC0C1\uD0DC\uB85C \uACC4\uC18D \uD45C\uC2DC**\uD558\uACE0, \uC2A4\uD06C\uB864\uC774 \uBA48\uCD94\uBA74 fabHideDelay \uD6C4 \uD398\uC774\uB4DC\uC544\uC6C3\uD55C\uB2E4.\n // \uC120\uBA85\uD574\uC9C0\uB294 \uAC74 \uB20C\uB800\uC744 \uB54C(isFabPressed)\uBFD0\uC774\uB2E4 \u2014 \uBA48\uCD9C \uB54C\uB9C8\uB2E4 \uC120\uBA85\u2194\uBC18\uD22C\uBA85\uC774 \uC624\uAC00\uBA74 \uAE5C\uBE61\uC784\uC774 \uC2EC\uD558\uB2E4.\n // \uBE0C\uB77C\uC6B0\uC800\uC5D0 \"\uC2A4\uD06C\uB864 \uC885\uB8CC\" \uC774\uBCA4\uD2B8\uAC00 \uC5C6\uC5B4 \uC815\uC9C0 \uD310\uC815\uC740 \uB9C8\uC9C0\uB9C9 \uC2A4\uD06C\uB864 \uAE30\uC900 \uD0C0\uC774\uBA38\uB85C \uD55C\uB2E4.\n if (fabEnabled) {\n const top = (event.target as HTMLElement | null)?.scrollTop ?? 0;\n const shouldShow = top > fabThreshold;\n setShowScrollTopFab(shouldShow);\n if (fabHideTimerRef.current) {\n clearTimeout(fabHideTimerRef.current);\n fabHideTimerRef.current = null;\n }\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 fabHideDelay \uD6C4 \uC790\uB3D9\uC73C\uB85C \uC228\uAE34\uB2E4(\uC2A4\uD06C\uB864\uB9C8\uB2E4 \uB9AC\uC14B).\n if (shouldShow && fabHideDelay > 0) {\n fabHideTimerRef.current = setTimeout(() => {\n fabHideTimerRef.current = null;\n setShowScrollTopFab(false);\n }, fabHideDelay);\n }\n }\n\n // \uCD08\uAE30 \uC9C0\uC5F0 \uC911\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC74C\n if (isInitialDelayActive) {\n if (onScroll) {\n onScroll(event);\n }\n return;\n }\n\n // \uC2A4\uD06C\uB864 \uC911\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uB514\uBC14\uC6B4\uC2A4 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC2E4\uC81C \uC2A4\uD06C\uB864 \uBC1C\uC0DD \uC2DC)\n clearWheelShowTimer();\n\n // \uD720 \uC2A4\uD06C\uB864 \uC911\uC774\uBA74 \uBE60\uB978 \uC228\uAE40, \uC544\uB2C8\uBA74 \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n const delay = isWheelScrollingRef.current\n ? finalAutoHideConfig.delayOnWheel\n : finalAutoHideConfig.delay;\n setHideTimer(delay);\n\n if (onScroll) {\n onScroll(event);\n }\n };\n\n const handleWheel = () => {\n // \uD720 \uC2A4\uD06C\uB864 \uC0C1\uD0DC \uD45C\uC2DC\n isWheelScrollingRef.current = true;\n\n // \uAE30\uC874 \uD720 \uD0C0\uC774\uBA38 \uC81C\uAC70\n if (wheelTimeoutRef.current) {\n clearTimeout(wheelTimeoutRef.current);\n }\n\n // 300ms \uD6C4 \uD720 \uC2A4\uD06C\uB864 \uC0C1\uD0DC \uD574\uC81C (\uD720 \uC2A4\uD06C\uB864\uC774 \uB05D\uB0AC\uB2E4\uACE0 \uAC04\uC8FC)\n wheelTimeoutRef.current = setTimeout(() => {\n isWheelScrollingRef.current = false;\n }, 300);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uC2DC 50ms \uB514\uBC14\uC6B4\uC2A4 \uC801\uC6A9 (\uC2E4\uC81C \uC2A4\uD06C\uB864 \uBC1C\uC0DD \uC2DC handleScroll\uC5D0\uC11C \uCDE8\uC18C\uB428)\n clearWheelShowTimer();\n wheelShowTimeoutRef.current = setTimeout(() => {\n setScrollbarVisible(true);\n wheelShowTimeoutRef.current = null;\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uD6C4 \uC790\uB3D9 \uC228\uAE40 \uD0C0\uC774\uBA38 \uC124\uC815\n setHideTimer(finalAutoHideConfig.delayOnWheel);\n }, 50);\n };\n\n const elementsToWatch: HTMLElement[] = [];\n\n // \uC2E4\uC81C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30\n const scrollableElement = findScrollableElement();\n if (scrollableElement) {\n elementsToWatch.push(scrollableElement);\n }\n\n // fallback: \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uC640 children \uC694\uC18C\uB3C4 \uAC10\uC9C0\n const container = containerRef.current;\n if (container && !scrollableElement) {\n elementsToWatch.push(container);\n\n // children \uC694\uC18C\uB4E4\uC758 \uC2A4\uD06C\uB864\uB3C4 \uAC10\uC9C0 (\uC911\uCCA9\uB41C OverlayScrollbar \uC81C\uC678)\n const childScrollableElements = container.querySelectorAll(\n '[data-virtuoso-scroller], [style*=\"overflow\"], .virtuoso-scroller, [style*=\"overflow: auto\"], [style*=\"overflow:auto\"]',\n );\n childScrollableElements.forEach((child) => {\n const element = child as HTMLElement;\n\n // \uB2E4\uB978 OverlayScrollbar\uC758 container\uB294 \uC81C\uC678\n if (\n element !== container &&\n element.classList.contains(\n \"overlay-scrollbar-container\",\n )\n ) {\n return;\n }\n\n // \uBD80\uBAA8 \uC911\uC5D0 \uB2E4\uB978 OverlayScrollbar container\uAC00 \uC788\uC73C\uBA74 \uC81C\uC678\n let parent: HTMLElement | null = element.parentElement;\n while (parent && parent !== container) {\n if (\n parent.classList.contains(\n \"overlay-scrollbar-container\",\n ) &&\n parent !== container\n ) {\n return; // \uC911\uCCA9\uB41C OverlayScrollbar \uB0B4\uBD80\uC774\uBBC0\uB85C \uC81C\uC678\n }\n parent = parent.parentElement;\n }\n\n elementsToWatch.push(element);\n });\n }\n\n // \uBAA8\uB4E0 \uC694\uC18C\uC5D0 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 \uB4F1\uB85D\n elementsToWatch.forEach((element) => {\n element.addEventListener(\"scroll\", handleScroll, {\n passive: true,\n });\n element.addEventListener(\"wheel\", handleWheel, {\n passive: true,\n });\n });\n\n return () => {\n // \uBAA8\uB4E0 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 \uC81C\uAC70\n elementsToWatch.forEach((element) => {\n element.removeEventListener(\"scroll\", handleScroll);\n element.removeEventListener(\"wheel\", handleWheel);\n });\n };\n }, [\n findScrollableElement,\n updateScrollbar,\n onScroll,\n clearHideTimer,\n setHideTimer,\n finalAutoHideConfig,\n isInitialDelayActive,\n fabEnabled,\n fabThreshold,\n fabHideDelay,\n ]);\n\n // \uC5B8\uB9C8\uC6B4\uD2B8 \uC2DC FAB \uC790\uB3D9\uC228\uAE40\u00B7\uD720 \uD0C0\uC774\uBA38\uB97C \uC815\uB9AC\uD55C\uB2E4.\n // \uD720 \uD0C0\uC774\uBA38\uB97C \uB9AC\uC2A4\uB108 effect \uC815\uB9AC\uC5D0\uC11C \uC9C0\uC6B0\uC9C0 \uC54A\uB294 \uC774\uC720: \uADF8 effect \uB294 onScroll \uAC19\uC740 prop \uC774 \uBC14\uB014 \uB54C\uB9C8\uB2E4\n // \uB2E4\uC2DC \uB3C4\uB294\uB370, \uADF8\uB54C\uB9C8\uB2E4 \uC9C0\uC6B0\uBA74 \uAC78\uC5B4 \uB454 \uD45C\uC2DC\u00B7\"\uD720 \uC911\" \uD574\uC81C\uAC00 \uC0AC\uB77C\uC9C4\uB2E4.\n useEffect(\n () => () => {\n if (fabHideTimerRef.current) {\n clearTimeout(fabHideTimerRef.current);\n }\n if (wheelTimeoutRef.current) {\n clearTimeout(wheelTimeoutRef.current);\n }\n if (wheelShowTimeoutRef.current) {\n clearTimeout(wheelShowTimeoutRef.current);\n }\n },\n []\n );\n\n // \uD0A4\uBCF4\uB4DC \uB124\uBE44\uAC8C\uC774\uC158 \uD578\uB4E4\uB7EC (\uBC29\uD5A5\uD0A4, PageUp/PageDown/Home/End)\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const target = event.target;\n if (event.defaultPrevented) {\n return;\n }\n\n if (\n target instanceof Element &&\n isTextInputElement(target, finalDragScrollConfig)\n ) {\n return;\n }\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n const { key } = event;\n const { scrollTop, scrollHeight, clientHeight } =\n scrollableElement;\n const maxScrollTop = scrollHeight - clientHeight;\n\n // \uD55C \uC904 \uC2A4\uD06C\uB864 \uB2E8\uC704 (rowHeight \uB610\uB294 \uAE30\uBCF8\uAC12)\n const lineScrollStep = 50;\n\n let newScrollTop: number | null = null;\n\n switch (key) {\n case \"ArrowUp\":\n event.preventDefault();\n newScrollTop = Math.max(0, scrollTop - lineScrollStep);\n break;\n case \"ArrowDown\":\n event.preventDefault();\n newScrollTop = Math.min(\n maxScrollTop,\n scrollTop + lineScrollStep,\n );\n break;\n case \"PageUp\":\n event.preventDefault();\n newScrollTop = Math.max(0, scrollTop - clientHeight);\n break;\n case \"PageDown\":\n event.preventDefault();\n newScrollTop = Math.min(\n maxScrollTop,\n scrollTop + clientHeight,\n );\n break;\n case \"Home\":\n event.preventDefault();\n newScrollTop = 0;\n break;\n case \"End\":\n event.preventDefault();\n newScrollTop = maxScrollTop;\n break;\n default:\n return;\n }\n\n if (newScrollTop !== null) {\n // \uC378 \uC704\uCE58\uB97C \uBA3C\uC800 \uC5C5\uB370\uC774\uD2B8\n const scrollRatio = newScrollTop / maxScrollTop;\n const arrowSpace = showArrows\n ? finalThumbWidth * 2 + finalTrackConfig.margin * 4\n : finalTrackConfig.margin * 2;\n const availableHeight = clientHeight - arrowSpace;\n const scrollableThumbHeight = availableHeight - thumbHeight;\n const newThumbTop = scrollableThumbHeight * scrollRatio;\n\n setThumbTop(newThumbTop);\n\n // \uC2A4\uD06C\uB864 \uC704\uCE58\uB97C \uC989\uC2DC \uBCC0\uACBD (\uC560\uB2C8\uBA54\uC774\uC158 \uC5C6\uC74C)\n scrollableElement.scrollTop = newScrollTop;\n\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n clearHideTimer();\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n }\n };\n\n const container = containerRef.current;\n if (container) {\n container.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n }\n }, [\n findScrollableElement,\n showArrows,\n finalThumbWidth,\n finalTrackConfig.margin,\n thumbHeight,\n clearHideTimer,\n setHideTimer,\n finalAutoHideConfig.delay,\n finalDragScrollConfig,\n ]);\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n document.addEventListener(\"mousemove\", handleDragScrollMove);\n document.addEventListener(\"mouseup\", handleDragScrollEnd);\n return () => {\n document.removeEventListener(\"mousemove\", handleDragScrollMove);\n document.removeEventListener(\"mouseup\", handleDragScrollEnd);\n };\n }, [isDragScrolling, handleDragScrollMove, handleDragScrollEnd]);\n\n // \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n if (isDragging) {\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }\n }, [isDragging, handleMouseMove, handleMouseUp]);\n\n // \uAC00\uB85C \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n if (isDraggingHorizontal) {\n document.addEventListener(\n \"mousemove\",\n handleHorizontalMouseMove,\n );\n document.addEventListener(\"mouseup\", handleHorizontalMouseUp);\n return () => {\n document.removeEventListener(\n \"mousemove\",\n handleHorizontalMouseMove,\n );\n document.removeEventListener(\n \"mouseup\",\n handleHorizontalMouseUp,\n );\n };\n }\n }, [\n isDraggingHorizontal,\n handleHorizontalMouseMove,\n handleHorizontalMouseUp,\n ]);\n\n // \uCD08\uAE30 \uC2A4\uD06C\uB864\uBC14 \uC5C5\uB370\uC774\uD2B8\n useEffect(() => {\n // \uC989\uC2DC \uC5C5\uB370\uC774\uD2B8\n updateScrollbar();\n // \uC57D\uAC04\uC758 \uC9C0\uC5F0 \uD6C4\uC5D0\uB3C4 \uC5C5\uB370\uC774\uD2B8 (DOM\uC774 \uC644\uC804\uD788 \uB80C\uB354\uB9C1\uB41C \uD6C4)\n const timer = setTimeout(() => {\n updateScrollbar();\n }, 100);\n return () => clearTimeout(timer);\n }, [updateScrollbar]);\n\n // \uCEF4\uD3EC\uB10C\uD2B8 \uCD08\uAE30\uD654 \uC644\uB8CC \uD45C\uC2DC (hover \uC774\uBCA4\uD2B8 \uD65C\uC131\uD654\uC6A9)\n useLayoutEffect(() => {\n setIsInitialized(true);\n // \uCD08\uAE30\uD654 \uC9C1\uD6C4 \uC2A4\uD06C\uB864\uBC14 \uC5C5\uB370\uC774\uD2B8 (\uC378 \uB192\uC774 \uC815\uD655\uD558\uAC8C \uACC4\uC0B0)\n updateScrollbar();\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uACE0 \uCD08\uAE30 \uC9C0\uC5F0\uC774 \uB05D\uB0AC\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uD56D\uC0C1 \uD45C\uC2DC\n if (\n !finalAutoHideConfig.enabled &&\n !isInitialDelayActive &&\n isScrollable()\n ) {\n setScrollbarVisible(true);\n }\n }, [\n isScrollable,\n updateScrollbar,\n finalAutoHideConfig.enabled,\n isInitialDelayActive,\n ]);\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC9C0\uC5F0 \uD0C0\uC774\uBA38\n useEffect(() => {\n if (finalAutoHideConfig.initialDelay > 0) {\n const timer = setTimeout(() => {\n setIsInitialDelayActive(false);\n }, finalAutoHideConfig.initialDelay);\n return () => clearTimeout(timer);\n }\n }, [finalAutoHideConfig.initialDelay]);\n\n // \uD06C\uAE30 \uBCC0\uACBD \uC54C\uB9BC\uC740 \uD55C \uD654\uBA74 \uADF8\uB9BC\uC5D0 \uD55C \uBC88\uB9CC \uBC18\uC601\uD55C\uB2E4(1.7.31 \uBC30\uD3EC\uBCF8\uC5D0\uC11C \uC62E\uAE40 \u2014 \uC800\uC7A5\uC18C\uC5D0 \uBE60\uC838 \uC788\uC5C8\uB2E4).\n // ResizeObserver \uCF5C\uBC31 \uC548\uC5D0\uC11C \uACE7\uBC14\uB85C \uC7AC\uBA74 \"ResizeObserver loop\" \uACBD\uACE0\uC640 \uC5F0\uC18D \uC7AC\uACC4\uC0B0\uC774 \uC0DD\uAE34\uB2E4.\n const updateScrollbarRef = useRef(updateScrollbar);\n updateScrollbarRef.current = updateScrollbar;\n const resizeFrameRef = useRef<number | null>(null);\n const scheduleResizeUpdate = useCallback(() => {\n if (resizeFrameRef.current !== null) return;\n resizeFrameRef.current = requestAnimationFrame(() => {\n resizeFrameRef.current = null;\n updateScrollbarRef.current();\n });\n }, []);\n useEffect(\n () => () => {\n if (resizeFrameRef.current !== null) {\n cancelAnimationFrame(resizeFrameRef.current);\n resizeFrameRef.current = null;\n }\n },\n [],\n );\n\n // Resize observer\uB85C \uD06C\uAE30 \uBCC0\uACBD \uAC10\uC9C0\n useEffect(() => {\n const resizeObserver = new ResizeObserver(() => {\n scheduleResizeUpdate();\n });\n\n const elementsToObserve: HTMLElement[] = [];\n\n // \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uB4E4 \uAD00\uCC30\n if (containerRef.current) {\n elementsToObserve.push(containerRef.current);\n }\n if (contentRef.current) {\n elementsToObserve.push(contentRef.current);\n }\n\n // \uCE90\uC2DC\uB41C \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uB3C4 \uAD00\uCC30\n if (\n cachedScrollContainerRef.current &&\n document.contains(cachedScrollContainerRef.current)\n ) {\n elementsToObserve.push(cachedScrollContainerRef.current);\n }\n\n // \uBAA8\uB4E0 \uC694\uC18C\uB4E4 \uAD00\uCC30 \uC2DC\uC791\n elementsToObserve.forEach((element) => {\n resizeObserver.observe(element);\n });\n\n return () => resizeObserver.disconnect();\n }, [updateScrollbar, scheduleResizeUpdate]);\n\n // MutationObserver\uB85C DOM \uBCC0\uACBD \uAC10\uC9C0\n useEffect(() => {\n if (!containerRef.current) {\n return;\n }\n\n const observer = new MutationObserver(() => {\n // \uCE90\uC2DC \uCD08\uAE30\uD654\uD558\uC5EC \uC0C8\uB85C\uC6B4 \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 \uAC10\uC9C0\n cachedScrollContainerRef.current = null;\n updateScrollbar();\n });\n\n observer.observe(containerRef.current, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\"],\n });\n\n return () => observer.disconnect();\n }, [updateScrollbar]);\n\n // trackWidth\uAC00 thumbWidth\uBCF4\uB2E4 \uC791\uC73C\uBA74 thumbWidth\uC640 \uAC19\uAC8C \uC124\uC815\n const adjustedTrackWidth = Math.max(finalTrackWidth, finalThumbWidth);\n\n // PTR \uC2A4\uD53C\uB108 keyframes\u00B7editor-content \uC2A4\uD06C\uB864\uBC14\uB294 \uC785\uB825\uC7A5\uCE58\uC640 \uBB34\uAD00\uD558\uAC8C \uC804\uC5ED 1\uD68C\uB9CC \uC8FC\uC785\uD55C\uB2E4.\n useEffect(() => {\n const styleId = \"overlay-scrollbar-shared\";\n if (document.getElementById(styleId)) {\n return; // \uC774\uBBF8 \uC788\uC73C\uBA74 \uC7AC\uC8FC\uC785\uD558\uC9C0 \uC54A\uB294\uB2E4(\uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uB123\uC5B4\uB460).\n }\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n /* ehfuse-editor-content\uB294 \uC2A4\uD06C\uB864\uBC14 \uC720\uC9C0 */\n .overlay-scrollbar-container .ehfuse-editor-content {\n scrollbar-width: thin !important;\n -ms-overflow-style: auto !important;\n overflow: auto !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar {\n display: block !important;\n width: 8px !important;\n height: 8px !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-track {\n display: block !important;\n background: #f1f1f1 !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-thumb {\n display: block !important;\n background: #c1c1c1 !important;\n border-radius: 4px !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-thumb:hover {\n background: #a1a1a1 !important;\n }\n /* \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC2A4\uD53C\uB108 \uD68C\uC804 */\n @keyframes overlay-scrollbar-ptr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `;\n document.head.appendChild(style);\n // \uACF5\uC720 keyframes \uB294 \uC81C\uAC70\uD558\uC9C0 \uC54A\uB294\uB2E4(\uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uACC4\uC18D \uCC38\uC870\uD560 \uC218 \uC788\uC74C).\n }, []);\n\n // \uC778\uC2A4\uD134\uC2A4\uBCC4 \uC2A4\uD06C\uB864\uBC14 CSS \uB97C \uC774 \uC778\uC2A4\uD134\uC2A4 \uCEE8\uD14C\uC774\uB108 \uD074\uB798\uC2A4(instanceClass)\uC5D0\uB9CC \uC8FC\uC785\uD55C\uB2E4.\n // \uC804\uC5ED `.overlay-scrollbar-container` \uC120\uD0DD\uC790\uB97C \uC4F0\uC9C0 \uC54A\uC73C\uBBC0\uB85C \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uC758 \uC2A4\uD06C\uB864\uBC14\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4.\n useEffect(() => {\n const styleId = `overlay-scrollbar-instance-${instanceClass}`;\n const existing = document.getElementById(styleId);\n if (existing) existing.remove();\n\n const style = document.createElement(\"style\");\n style.id = styleId;\n const sel = `.${instanceClass}`;\n // fine/coarse \uBAA8\uB450 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uBC14\uB294 \uC228\uAE34\uB2E4 \u2014 \uD45C\uC2DC\uB418\uB294 \uC2A4\uD06C\uB864\uBC14\uB294 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774(\uC790\uCCB4 \uD2B8\uB799/thumb)\uBFD0.\n // (\uD130\uCE58\uC5D0\uC11C\uB3C4 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774\uB97C \uCF1C\uBBC0\uB85C \uB124\uC774\uD2F0\uBE0C\uB97C \uADF8\uB9B4 \uD544\uC694\uAC00 \uC5C6\uACE0, \uB458\uC774 \uACB9\uCE58\uBA74 \uC9C0\uC800\uBD84\uD558\uB2E4.)\n style.textContent = `\n ${sel} {\n scrollbar-width: none !important;\n -ms-overflow-style: none !important;\n }\n ${sel}::-webkit-scrollbar {\n display: none !important;\n width: 0 !important;\n height: 0 !important;\n }\n ${sel}::-webkit-scrollbar-track { display: none !important; }\n ${sel}::-webkit-scrollbar-thumb { display: none !important; }`;\n document.head.appendChild(style);\n\n return () => {\n document.getElementById(styleId)?.remove();\n };\n }, [instanceClass]);\n\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 padding-bottom \uC740 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uBE0C\uB77C\uC6B0\uC800\uAC00 \uBB34\uC2DC\uD558\uBBC0\uB85C,\n // container/content \uC5D0 \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uAC77\uC5B4\uB0B4 spacer \uB192\uC774\uB85C \uD569\uC0B0\uD55C\uB2E4.\n const { normalizedContainerStyle, normalizedContentStyle, bottomSpacerHeight } =\n useMemo(() => {\n const container = extractBottomPadding(containerStyle);\n const content = extractBottomPadding(contentStyle);\n return {\n normalizedContainerStyle: container.style,\n normalizedContentStyle: content.style,\n bottomSpacerHeight:\n container.spacerHeight + content.spacerHeight,\n };\n }, [containerStyle, contentStyle]);\n\n return (\n <div\n ref={wrapperRef}\n className={`overlay-scrollbar-wrapper ${className}`}\n onMouseLeave={handleWrapperMouseLeave}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n position: \"relative\",\n minHeight: 0, // shrink \uAC00\uB2A5\uD558\uB3C4\uB85D\n height: \"100%\", // \uBD80\uBAA8\uC758 \uC804\uCCB4 \uB192\uC774 \uC0AC\uC6A9\n flex: \"1 1 0%\", // \uAE30\uBCF8\uC801\uC73C\uB85C flex item\uC73C\uB85C \uB3D9\uC791\n overflow: \"visible\", // content \uADF8\uB9BC\uC790\uC640 outside track\uC774 \uC0C1\uC704 wrapper\uC5D0\uC11C \uC798\uB9AC\uC9C0 \uC54A\uB3C4\uB85D \uD5C8\uC6A9\n ...style, // \uC0AC\uC6A9\uC790\uAC00 flex\uB97C override \uD560 \uC218 \uC788\uB3C4\uB85D style\uC744 \uB4A4\uC5D0 \uBC30\uCE58\n }}\n >\n {/* \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC778\uB514\uCF00\uC774\uD130 \u2014 \uB2F9\uAE40 \uAC70\uB9AC\uB97C \uB530\uB77C \uB0B4\uB824\uC624\uACE0, \uC2E4\uD589 \uC911\uC5D4 \uC2A4\uD53C\uB108\uB85C \uD68C\uC804 */}\n {pullIndicatorVisible && (\n <div\n className=\"overlay-scrollbar-pull-indicator\"\n style={{\n position: \"absolute\",\n top: 0,\n left: \"50%\",\n transform: `translate(-50%, ${pullDistance - 68}px)`,\n transition: isRefreshing\n ? \"transform 0.15s ease-out\"\n : pullDistance === 0\n ? \"transform 0.2s ease-out\"\n : \"none\",\n width: 60,\n height: 60,\n borderRadius: \"50%\",\n background: \"#ffffff\",\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.25)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 1001,\n pointerEvents: \"none\",\n opacity: isRefreshing ? 1 : Math.min(pullProgress * 1.5, 1),\n }}\n >\n {isRefreshing ? (\n // \uC0C8\uB85C\uACE0\uCE68 \uC2E4\uD589 \uC911 \u2014 \uC2A4\uD53C\uB108\n <svg\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 24 24\"\n style={{\n animation:\n \"overlay-scrollbar-ptr-spin 0.8s linear infinite\",\n }}\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n fill=\"none\"\n stroke={pullIndicatorColor}\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeDasharray=\"42\"\n strokeDashoffset=\"14\"\n />\n </svg>\n ) : (\n // \uB2F9\uAE30\uB294 \uC911 \u2014 \uC9C4\uD589\uB960\uC5D0 \uB530\uB77C \uD68C\uC804\uD558\uB294 \uC0C8\uB85C\uACE0\uCE68 \uD654\uC0B4\uD45C\n <svg\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 24 24\"\n style={{\n transform: `rotate(${pullProgress * 270}deg)`,\n opacity: pullProgress >= 1 ? 1 : 0.6,\n }}\n >\n <path\n d=\"M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"\n fill={pullIndicatorColor}\n />\n </svg>\n )}\n </div>\n )}\n\n {/* \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 */}\n <div\n ref={containerRef}\n // \uACF5\uC6A9 \uD074\uB798\uC2A4 + \uC778\uC2A4\uD134\uC2A4 \uACE0\uC720 \uD074\uB798\uC2A4. \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC/\uC228\uAE40 CSS \uB294 \uC778\uC2A4\uD134\uC2A4 \uD074\uB798\uC2A4\uC5D0\uB9CC \uAC78\uB9B0\uB2E4.\n className={`overlay-scrollbar-container ${instanceClass}`}\n tabIndex={-1} // \uD0A4\uBCF4\uB4DC \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5\uD558\uAC8C \uD568\n onMouseDown={handleDragScrollStart}\n style={{\n display: \"flex\",\n // \u26A0\uFE0F flexDirection \uC744 column \uC73C\uB85C \uBC14\uAFB8\uC9C0 \uC54A\uB294\uB2E4.\n // row \uB77C\uC11C content \uC758 flex \uB294 **\uAC00\uB85C**\uB9CC \uC81C\uC5B4\uD55C\uB2E4. column \uC73C\uB85C \uBC14\uAFB8\uBA74 \uADF8 flex \uAC00\n // \uC138\uB85C\uB97C \uC7A1\uC544 \uCF58\uD150\uCE20 \uB192\uC774\uAC00 \uCEE8\uD14C\uC774\uB108\uC5D0 \uACE0\uC815\uB418\uACE0 scrollHeight == clientHeight \uAC00 \uB418\uC5B4\n // \uC2A4\uD06C\uB864\uC774 \uC8FD\uB294\uB2E4. (\uD558\uB2E8 spacer \uB294 content \uC548 \uB9C8\uC9C0\uB9C9 \uC790\uC2DD\uC73C\uB85C \uBC30\uCE58\uD55C\uB2E4)\n width: \"100%\", // \uBA85\uC2DC\uC801 \uB108\uBE44 \uC124\uC815\n flex: \"1 1 auto\", // flex item\uC73C\uB85C \uC124\uC815\uD558\uC5EC \uB192\uC774\uB97C \uC790\uB3D9\uC73C\uB85C \uACC4\uC0B0\n minHeight: 0, // \uCD5C\uC18C \uB192\uC774 \uBCF4\uC7A5\n overflow: \"auto\", // \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864 \uAE30\uB2A5 \uC720\uC9C0\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC/\uC228\uAE40\uC740 \uC778\uC2A4\uD134\uC2A4 \uD074\uB798\uC2A4 CSS(useEffect)\uAC00 \uB2F4\uB2F9\uD55C\uB2E4(\uC778\uB77C\uC778\uC73C\uB85C \uAC15\uC81C\uD558\uC9C0 \uC54A\uC74C).\n // \uD0A4\uBCF4\uB4DC \uD3EC\uCEE4\uC2A4 \uC2A4\uD0C0\uC77C (\uC811\uADFC\uC131)\n outline: \"none\", // \uAE30\uBCF8 \uC544\uC6C3\uB77C\uC778 \uC81C\uAC70\n userSelect: isDragScrolling ? \"none\" : \"auto\", // \uC2E4\uC81C \uB4DC\uB798\uADF8 \uC911 \uD14D\uC2A4\uD2B8 \uC120\uD0DD \uBC29\uC9C0\n ...normalizedContainerStyle, // \uC0AC\uC6A9\uC790 \uC815\uC758 \uC2A4\uD0C0\uC77C(\uD558\uB2E8 padding \uC740 spacer \uB85C \uBD84\uB9AC)\n }}\n >\n <div\n ref={contentRef}\n className=\"overlay-scrollbar-content\"\n style={{\n // \u26A0\uFE0F basis \uB294 auto(\uB0B4\uC6A9 \uAE30\uC900). \"1 1 0%\" \uB85C \uB450\uBA74 basis 0 \uC774\uB77C \uC548\uCABD flex:1 \uC790\uC2DD\uC774\n // \uB0A8\uB294 \uACF5\uAC04\uC744 \uB2E4 \uBA39\uC5B4 \uD558\uB2E8 spacer \uB97C \uBC00\uC5B4\uB0B4\uACE0, \uCF58\uD150\uCE20\uAC00 \uB118\uCCD0\uB3C4 \uB192\uC774\uAC00 \uC548 \uB298\uC5B4\uB09C\uB2E4.\n flex: \"1 1 auto\", // grow \uD558\uB418 \uB0B4\uC6A9 \uB192\uC774\uB97C \uAE30\uC900\uC73C\uB85C \uC7A1\uB294\uB2E4\n display: \"flex\", // flex \uCEE8\uD14C\uC774\uB108\uB85C \uC124\uC815\n flexDirection: \"column\", // \uC138\uB85C \uBC29\uD5A5 \uC815\uB82C\n boxSizing: \"border-box\",\n ...normalizedContentStyle, // \uC0AC\uC6A9\uC790 \uC815\uC758 \uC2A4\uD0C0\uC77C(\uD558\uB2E8 padding \uC740 spacer \uB85C \uBD84\uB9AC)\n }}\n >\n {children}\n {bottomSpacerHeight > 0 && (\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 padding-bottom \uC740 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uBB34\uC2DC\uB418\uBBC0\uB85C,\n // \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uC2E4\uC81C \uB192\uC774\uB97C \uAC00\uC9C4 spacer \uB85C \uB300\uCCB4\uD55C\uB2E4.\n // content \uB294 column flex \uB77C \uC774 \uC790\uC2DD\uC774 \uB9C8\uC9C0\uB9C9 \uC904\uB85C \uC313\uC5EC \uC2A4\uD06C\uB864 \uC601\uC5ED\uC744 \uB298\uB9B0\uB2E4.\n <div\n className=\"overlay-scrollbar-bottom-spacer\"\n aria-hidden=\"true\"\n style={{ flex: \"0 0 auto\", height: bottomSpacerHeight }}\n />\n )}\n </div>\n </div>\n\n {/* \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 */}\n {showScrollbar &&\n hasScrollableContent &&\n (finalTrackConfig.overflowY ?? true) && (\n <div\n ref={scrollbarRef}\n className=\"overlay-scrollbar-track\"\n onMouseEnter={() => {\n // \uD130\uCE58\uC758 \uD0ED\uC740 mouseenter \uB9CC \uD749\uB0B4 \uB0B4\uACE0 mouseleave \uB294 \uB2E4\uB978 \uACF3\uC744 \uB204\uB97C \uB54C\uAE4C\uC9C0 \uC624\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uD638\uBC84\uB85C \uBD99\uC7A1\uC73C\uBA74 \uC624\uB978\uCABD \uB05D\uC744 \uD55C \uBC88 \uB20C\uB800\uC744 \uB54C \uC2A4\uD06C\uB864\uBC14\uAC00 \uACC4\uC18D \uB0A8\uB294\uB2E4.\n if (isCoarsePointer) return;\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB294 \uC989\uC2DC \uCDE8\uC18C\n clearHideTimer();\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uC124\uC815 (100ms \uD6C4 \uD45C\uC2DC)\n hoverEnterTimeoutRef.current = setTimeout(\n () => {\n setScrollbarVisible(true);\n hoverEnterTimeoutRef.current = null;\n },\n 100,\n );\n }}\n onMouseLeave={() => {\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC9C0\uB098\uAC00\uAE30\uB9CC \uD55C \uACBD\uC6B0)\n clearHoverEnterTimer();\n\n if (!isDragging) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }}\n style={{\n position: \"absolute\",\n top: 0,\n right: 0,\n width: `${adjustedTrackWidth}px`,\n height: \"100%\",\n opacity: scrollbarVisible ? 1 : 0,\n transition: \"opacity 0.2s ease-in-out\",\n cursor: \"pointer\",\n zIndex: 1000,\n pointerEvents: \"auto\",\n }}\n >\n {/* \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799 \uBC30\uACBD */}\n {finalTrackConfig.visible && (\n <div\n className=\"overlay-scrollbar-track-background\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n handleTrackClick(e);\n }}\n style={{\n position: \"absolute\",\n top: showArrows\n ? `${\n finalThumbConfig.width +\n finalTrackConfig.margin * 2\n }px`\n : `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment ===\n \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbConfig.width) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbConfig.width}px`,\n height: showArrows\n ? `calc(100% - ${\n finalThumbConfig.width * 2 +\n finalTrackConfig.margin * 4\n }px)`\n : `calc(100% - ${\n finalTrackConfig.margin * 2\n }px)`,\n backgroundColor: finalTrackConfig.color,\n borderRadius: `${finalTrackConfig.radius}px`,\n cursor: \"pointer\",\n }}\n />\n )}\n\n {/* \uC2A4\uD06C\uB864\uBC14 \uC378 */}\n <div\n ref={thumbRef}\n className=\"overlay-scrollbar-thumb\"\n onMouseDown={handleThumbMouseDown}\n onMouseEnter={() => setIsThumbHovered(true)}\n onMouseLeave={() => setIsThumbHovered(false)}\n style={{\n position: \"absolute\",\n top: `${\n (showArrows\n ? finalThumbWidth +\n finalTrackConfig.margin * 2\n : finalTrackConfig.margin) +\n thumbTop\n }px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${Math.max(\n thumbHeight,\n thumbMinHeight,\n )}px`,\n backgroundColor:\n isThumbHovered || isDragging\n ? finalThumbConfig.hoverColor\n : finalThumbConfig.color,\n opacity:\n isThumbHovered || isDragging\n ? finalThumbConfig.hoverOpacity\n : finalThumbConfig.opacity,\n borderRadius: `${finalThumbConfig.radius}px`,\n cursor: \"pointer\",\n transition:\n \"background-color 0.2s ease-in-out, opacity 0.2s ease-in-out\",\n }}\n />\n </div>\n )}\n\n {/* \uC704\uCABD \uD654\uC0B4\uD45C \uBC84\uD2BC */}\n {showScrollbar && hasScrollableContent && showArrows && (\n <div\n className=\"overlay-scrollbar-up-arrow\"\n onClick={handleUpArrowClick}\n onMouseEnter={() => setHoveredArrow(\"up\")}\n onMouseLeave={() => setHoveredArrow(null)}\n style={{\n position: \"absolute\",\n top: `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${finalThumbWidth}px`,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${Math.max(\n finalThumbWidth * 0.75,\n 8,\n )}px`,\n color:\n hoveredArrow === \"up\"\n ? finalArrowsConfig.hoverColor\n : finalArrowsConfig.color,\n userSelect: \"none\",\n zIndex: 1001,\n opacity: scrollbarVisible\n ? hoveredArrow === \"up\"\n ? finalArrowsConfig.hoverOpacity\n : finalArrowsConfig.opacity\n : 0,\n transition:\n \"opacity 0.2s ease-in-out, color 0.15s ease-in-out\",\n }}\n >\n \u25B2\n </div>\n )}\n\n {/* \uC544\uB798\uCABD \uD654\uC0B4\uD45C \uBC84\uD2BC */}\n {showScrollbar && hasScrollableContent && showArrows && (\n <div\n className=\"overlay-scrollbar-down-arrow\"\n onClick={handleDownArrowClick}\n onMouseEnter={() => setHoveredArrow(\"down\")}\n onMouseLeave={() => setHoveredArrow(null)}\n style={{\n position: \"absolute\",\n bottom: `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${finalThumbWidth}px`,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${Math.max(\n finalThumbWidth * 0.75,\n 8,\n )}px`,\n color:\n hoveredArrow === \"down\"\n ? finalArrowsConfig.hoverColor\n : finalArrowsConfig.color,\n userSelect: \"none\",\n zIndex: 1001,\n opacity: scrollbarVisible\n ? hoveredArrow === \"down\"\n ? finalArrowsConfig.hoverOpacity\n : finalArrowsConfig.opacity\n : 0,\n transition:\n \"opacity 0.2s ease-in-out, color 0.15s ease-in-out\",\n }}\n >\n \u25BC\n </div>\n )}\n\n {/* \uAC00\uB85C \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 */}\n {showScrollbar &&\n showHorizontalScrollbar &&\n hasHorizontalScrollableContent &&\n (finalTrackConfig.overflowX ?? true) && (\n <div\n className=\"overlay-scrollbar-horizontal-track\"\n onMouseEnter={() => {\n // \uD130\uCE58\uC758 \uD0ED\uC740 mouseenter \uB9CC \uD749\uB0B4 \uB0B4\uACE0 mouseleave \uB294 \uB2E4\uB978 \uACF3\uC744 \uB204\uB97C \uB54C\uAE4C\uC9C0 \uC624\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uD638\uBC84\uB85C \uBD99\uC7A1\uC73C\uBA74 \uC624\uB978\uCABD \uB05D\uC744 \uD55C \uBC88 \uB20C\uB800\uC744 \uB54C \uC2A4\uD06C\uB864\uBC14\uAC00 \uACC4\uC18D \uB0A8\uB294\uB2E4.\n if (isCoarsePointer) return;\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB294 \uC989\uC2DC \uCDE8\uC18C\n clearHideTimer();\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uC124\uC815 (100ms \uD6C4 \uD45C\uC2DC)\n hoverEnterTimeoutRef.current = setTimeout(\n () => {\n setScrollbarVisible(true);\n hoverEnterTimeoutRef.current = null;\n },\n 100,\n );\n }}\n onMouseLeave={() => {\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC9C0\uB098\uAC00\uAE30\uB9CC \uD55C \uACBD\uC6B0)\n clearHoverEnterTimer();\n\n if (!isDraggingHorizontal) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }}\n style={{\n position: \"absolute\",\n bottom: `${wrapperPaddingBottom}px`,\n left: 0,\n width: \"100%\",\n height: `${adjustedTrackWidth}px`,\n opacity: scrollbarVisible ? 1 : 0,\n transition: \"opacity 0.2s ease-in-out\",\n cursor: \"pointer\",\n zIndex: 1000,\n pointerEvents: \"auto\",\n }}\n >\n {/* \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799 \uBC30\uACBD */}\n {finalTrackConfig.visible && (\n <div\n className=\"overlay-scrollbar-horizontal-track-background\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n handleHorizontalTrackClick(e);\n }}\n style={{\n position: \"absolute\",\n bottom:\n finalTrackConfig.alignment ===\n \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`,\n left: `${finalTrackConfig.margin}px`,\n right: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `${\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px`\n : `${adjustedTrackWidth}px`\n : `${finalTrackConfig.margin}px`,\n height: `${finalThumbWidth}px`,\n backgroundColor: finalTrackConfig.color,\n borderRadius: `${finalTrackConfig.radius}px`,\n cursor: \"pointer\",\n }}\n />\n )}\n\n {/* \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uC378 */}\n <div\n className=\"overlay-scrollbar-horizontal-thumb\"\n onMouseDown={handleHorizontalThumbMouseDown}\n onMouseEnter={() =>\n setIsHorizontalThumbHovered(true)\n }\n onMouseLeave={() =>\n setIsHorizontalThumbHovered(false)\n }\n style={{\n position: \"absolute\",\n bottom:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`,\n left: `${\n finalTrackConfig.margin + thumbLeft\n }px`,\n right: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `${\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px`\n : `${adjustedTrackWidth}px`\n : \"auto\",\n maxWidth: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `calc(100% - ${\n finalTrackConfig.margin +\n thumbLeft +\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px)`\n : `calc(100% - ${\n finalTrackConfig.margin +\n thumbLeft +\n adjustedTrackWidth\n }px)`\n : \"none\",\n width: `${Math.max(thumbWidth, 50)}px`,\n height: `${finalThumbWidth}px`,\n backgroundColor:\n isHorizontalThumbHovered ||\n isDraggingHorizontal\n ? finalThumbConfig.hoverColor\n : finalThumbConfig.color,\n opacity:\n isHorizontalThumbHovered ||\n isDraggingHorizontal\n ? finalThumbConfig.hoverOpacity\n : finalThumbConfig.opacity,\n borderRadius: `${finalThumbConfig.radius}px`,\n cursor: \"pointer\",\n transition:\n \"background-color 0.2s ease-in-out, opacity 0.2s ease-in-out\",\n }}\n />\n </div>\n )}\n\n {/* \"\uB9E8 \uC704\uB85C\" FAB \u2014 \uC2A4\uD06C\uB864\uC744 threshold \uC774\uC0C1 \uB0B4\uB9AC\uBA74 \uB098\uD0C0\uB098\uACE0, \uB204\uB974\uBA74 \uCEE8\uD14C\uC774\uB108\uB97C \uB9E8 \uC704\uB85C \uB418\uB3CC\uB9B0\uB2E4.\n MUI \uB4F1 \uC678\uBD80 \uC758\uC874\uC131 \uC5C6\uC774 \uC21C\uC218 button + \uC778\uB77C\uC778 SVG \uB85C \uADF8\uB9B0\uB2E4. */}\n {fabEnabled && (\n <button\n type=\"button\"\n aria-label=\"\uB9E8 \uC704\uB85C\"\n onClick={() => {\n setIsFabPressed(false);\n // \uC2E4\uC81C\uB85C \uAD74\uB7EC\uAC00\uB294 \uC0C1\uC790\uB97C \uC62C\uB9B0\uB2E4 \u2014 detectInnerScroll(\uAC00\uC0C1 \uD45C \uB4F1)\uC774\uBA74 \uC548\uCABD \uC694\uC18C\uAC00 \uAD74\uB7EC\uAC00\uC11C,\n // \uBC14\uAE65 \uCEE8\uD14C\uC774\uB108\uB9CC \uC62C\uB9AC\uBA74 \uC544\uBB34 \uC77C\uB3C4 \uC5C6\uC5C8\uB2E4(1.7.33). \uB178\uCD9C \uD310\uC815(handleScroll)\uB3C4 \uAC19\uC740 \uC694\uC18C \uAE30\uC900\uC774\uB2E4.\n (findScrollableElement() ?? containerRef.current)?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n // \uB204\uB974\uB294 \uB3D9\uC548\uB9CC \uC120\uBA85\uD574\uC9C4\uB2E4(\uADF8 \uC678\uC5D0\uB294 \uBC18\uD22C\uBA85 \uC720\uC9C0).\n onPointerDown={() => setIsFabPressed(true)}\n onPointerUp={() => setIsFabPressed(false)}\n onPointerLeave={() => setIsFabPressed(false)}\n onPointerCancel={() => setIsFabPressed(false)}\n style={{\n position: \"absolute\",\n [fabPosition]: fabOffset,\n bottom: fabOffsetBottom,\n zIndex: 1200,\n width: fabSize,\n height: fabSize,\n padding: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n border: \"none\",\n borderRadius: \"50%\",\n backgroundColor: fabBackground,\n color: fabColor,\n boxShadow: \"0 3px 8px rgba(15, 23, 42, 0.28)\",\n cursor: \"pointer\",\n // \uD45C\uC2DC \uC911\uC5D0\uB294 \uBC18\uD22C\uBA85\uC744 \uACC4\uC18D \uC720\uC9C0\uD574 \uB0B4\uC6A9\uC744 \uAC00\uB9AC\uC9C0 \uC54A\uACE0, \uB204\uB97C \uB54C\uB9CC \uC120\uBA85\uD574\uC9C4\uB2E4.\n opacity: showScrollTopFab ? (isFabPressed ? 1 : fabIdleOpacity) : 0,\n pointerEvents: showScrollTopFab ? \"auto\" : \"none\",\n // \uD06C\uAE30/\uC774\uB3D9 \uC5C6\uC774 \uD398\uC774\uB4DC\uC778\u00B7\uC544\uC6C3\uB9CC \uD55C\uB2E4.\n // \uD45C\uC2DC \uC911(\uBC18\uD22C\uBA85\u2194\uC120\uBA85)\uC740 \uC989\uAC01\uC801\uC73C\uB85C, \uC790\uB3D9\uC228\uAE40 \uD398\uC774\uB4DC\uC544\uC6C3\uC740 \uBD80\uB4DC\uB7FD\uAC8C(0.45s).\n transition: showScrollTopFab ? \"opacity 0.15s ease\" : \"opacity 0.45s ease\",\n }}\n >\n <svg\n width={Math.round(fabSize * 0.5)}\n height={Math.round(fabSize * 0.5)}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.4\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {/* \uC704\uCABD \uD654\uC0B4\uD45C(\u2191) \u2014 \uAEBE\uC1E0(^)\uBCF4\uB2E4 \"\uB9E8 \uC704\uB85C\" \uC758\uBBF8\uAC00 \uBD84\uBA85\uD558\uB2E4. */}\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"5\" />\n <polyline points=\"5 12 12 5 19 12\" />\n </svg>\n </button>\n )}\n </div>\n );\n },\n);\n\nexport default OverlayScrollbar;\nexport { OverlayScrollbar };\n", "/**\n * MIT License\n *\n * Copyright (c) 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { DragScrollConfig } from \"../types\";\n\n// \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uD074\uB798\uC2A4\uB4E4 (\uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C\uC5D0\uC11C \uD655\uC778)\nconst DEFAULT_EXCLUDE_CLASSES = [\n // \uAE30\uBCF8 \uC785\uB825 \uC694\uC18C\uB4E4\n \"editor\",\n \"textarea\",\n \"input\",\n \"select\",\n \"textfield\",\n \"form-control\",\n \"contenteditable\",\n\n // Material-UI \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"MuiInputBase-input\",\n \"MuiSelect-select\",\n \"MuiOutlinedInput-input\",\n \"MuiFilledInput-input\",\n \"MuiInput-input\",\n \"MuiFormControl-root\",\n \"MuiTextField-root\",\n \"MuiSelect-root\",\n \"MuiOutlinedInput-root\",\n \"MuiFilledInput-root\",\n \"MuiInput-root\",\n \"MuiAutocomplete-input\",\n \"MuiDatePicker-input\",\n \"MuiSlider-thumb\",\n \"MuiSlider-rail\",\n \"MuiSlider-track\",\n \"MuiSlider-mark\",\n \"MuiSlider-markLabel\",\n \"MuiSlider-root\",\n \"MuiSlider-colorPrimary\",\n \"MuiSlider-sizeMedium\",\n \"MuiIconButton-root\",\n \"MuiButton-root\",\n \"MuiButtonBase-root\",\n \"MuiTouchRipple-root\",\n \"MuiCheckbox-root\",\n \"MuiRadio-root\",\n \"MuiSwitch-root\",\n \"PrivateSwitchBase-root\",\n\n // Ant Design \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"ant-input\",\n \"ant-input-affix-wrapper\",\n \"ant-input-group-addon\",\n \"ant-input-number\",\n \"ant-input-number-handler\",\n \"ant-select\",\n \"ant-select-selector\",\n \"ant-select-selection-search\",\n \"ant-select-dropdown\",\n \"ant-cascader\",\n \"ant-cascader-input\",\n \"ant-picker\",\n \"ant-picker-input\",\n \"ant-time-picker\",\n \"ant-calendar-picker\",\n \"ant-slider\",\n \"ant-slider-track\",\n \"ant-slider-handle\",\n \"ant-switch\",\n \"ant-checkbox\",\n \"ant-checkbox-wrapper\",\n \"ant-radio\",\n \"ant-radio-wrapper\",\n \"ant-rate\",\n \"ant-upload\",\n \"ant-upload-drag\",\n \"ant-form-item\",\n \"ant-form-item-control\",\n \"ant-btn\",\n \"ant-dropdown\",\n \"ant-dropdown-trigger\",\n \"ant-menu\",\n \"ant-menu-item\",\n \"ant-tooltip\",\n \"ant-popover\",\n \"ant-modal\",\n \"ant-drawer\",\n \"ant-tree-select\",\n \"ant-auto-complete\",\n \"ant-mentions\",\n \"ant-transfer\",\n\n // Shadcn/ui \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"ui-input\",\n \"ui-textarea\",\n \"ui-select\",\n \"ui-select-trigger\",\n \"ui-select-content\",\n \"ui-select-item\",\n \"ui-button\",\n \"ui-checkbox\",\n \"ui-radio-group\",\n \"ui-switch\",\n \"ui-slider\",\n \"ui-range-slider\",\n \"ui-calendar\",\n \"ui-date-picker\",\n \"ui-combobox\",\n \"ui-command\",\n \"ui-command-input\",\n \"ui-popover\",\n \"ui-dialog\",\n \"ui-sheet\",\n \"ui-dropdown-menu\",\n \"ui-context-menu\",\n \"ui-menubar\",\n \"ui-navigation-menu\",\n \"ui-form\",\n \"ui-form-control\",\n \"ui-form-item\",\n \"ui-form-field\",\n \"ui-label\",\n // Radix UI \uAE30\uBCF8 \uD074\uB798\uC2A4\uB4E4 (Shadcn \uAE30\uBC18)\n \"radix-ui\",\n \"radix-select\",\n \"radix-dropdown\",\n \"radix-dialog\",\n \"radix-popover\",\n \"radix-accordion\",\n \"radix-tabs\",\n \"radix-slider\",\n \"radix-switch\",\n \"radix-checkbox\",\n \"radix-radio\",\n\n // Quill Editor\n \"ql-editor\",\n \"ql-container\",\n \"ql-toolbar\",\n \"ql-picker\",\n \"ql-picker-label\",\n \"ql-picker-options\",\n \"ql-formats\",\n \"ql-snow\",\n \"ql-bubble\",\n \"quill\",\n \"quilleditor\",\n\n // Monaco Editor\n \"monaco-editor\",\n \"monaco-editor-background\",\n \"view-lines\",\n \"decorationsOverviewRuler\",\n \"monaco-scrollable-element\",\n\n // CodeMirror\n \"CodeMirror\",\n \"CodeMirror-code\",\n \"CodeMirror-lines\",\n \"CodeMirror-scroll\",\n \"CodeMirror-sizer\",\n \"cm-editor\",\n \"cm-focused\",\n \"cm-content\",\n\n // TinyMCE\n \"tox-editor-container\",\n \"tox-editor-header\",\n \"tox-edit-area\",\n \"tox-tinymce\",\n \"mce-content-body\",\n\n // CKEditor\n \"ck-editor\",\n \"ck-content\",\n \"ck-toolbar\",\n \"ck-editor__editable\",\n \"ck-widget\",\n\n // Slate.js\n \"slate-editor\",\n \"slate-content\",\n\n // Draft.js\n \"DraftEditor-root\",\n \"DraftEditor-editorContainer\",\n \"public-DraftEditor-content\",\n\n // EhfuseEditor\n \"ehfuse-editor\",\n \"ehfuse-editor-wrapper\",\n \"ehfuse-editor-content\",\n \"ehfuse-toolbar\",\n \"ehfuse-toolbar-group\",\n \"ehfuse-cursor\",\n\n // \uAE30\uD0C0 \uC5D0\uB514\uD130\uB4E4\n \"text-editor\",\n \"rich-text-editor\",\n \"wysiwyg\",\n \"ace_editor\",\n \"ace_content\",\n];\n\n/**\n * \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uC694\uC18C\uB4E4\uC778\uC9C0 \uD655\uC778\n */\n/**\n * \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uC694\uC18C\uB4E4\uC778\uC9C0 \uD655\uC778\n */\nexport const isTextInputElement = (\n element: Element,\n config?: DragScrollConfig\n): boolean => {\n // \uC0C1\uC18D\uB41C contenteditable(\uC790\uC2DD\uB9CC \uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC5D0\uB514\uD130 \uB4F1) \u2014 getAttribute\uB9CC\uC73C\uB85C\uB294 \uB193\uCE68\n if (element instanceof HTMLElement && element.isContentEditable) {\n return true;\n }\n\n // \uB124\uC774\uD2F0\uBE0C input/textarea\uAC00 \uC544\uB2CC ARIA \uD14D\uC2A4\uD2B8 \uC785\uB825\n if (element.closest('[role=\"textbox\"],[role=\"searchbox\"]')) {\n return true;\n }\n\n const tagName = element.tagName.toLowerCase();\n // \uC2A4\uD06C\uB864 \uC804\uC6A9 \uD0A4(Home/End \uB4F1)\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0 \uB9E1\uACA8\uC57C \uD558\uB294 \uC785\uB825\uB9CC \uC81C\uC678.\n // \uC608\uC804\uC5D0\uB294 \uC77C\uBD80 type\uB9CC \uD5C8\uC6A9\uD574 date \uB4F1\uC5D0\uC11C \uC2A4\uD06C\uB864 \uAC00\uB85C\uCC44\uAE30\uAC00 \uBC1C\uC0DD\uD560 \uC218 \uC788\uC5C8\uC74C.\n if (tagName === \"input\") {\n const type = (\n (element as HTMLInputElement).type || \"text\"\n ).toLowerCase();\n const typesWithoutCaretLikeKeyboardBehavior = [\n \"button\",\n \"submit\",\n \"reset\",\n \"image\",\n \"hidden\",\n ];\n return !typesWithoutCaretLikeKeyboardBehavior.includes(type);\n }\n\n // textarea, select, \uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC694\uC18C\uB4E4\n if ([\"textarea\", \"select\", \"button\"].includes(tagName)) {\n return true;\n }\n\n // SVG \uC694\uC18C\uB4E4 (\uC544\uC774\uCF58\uB4E4)\n if (\n [\n \"svg\",\n \"path\",\n \"circle\",\n \"rect\",\n \"line\",\n \"polygon\",\n \"polyline\",\n ].includes(tagName)\n ) {\n return true;\n }\n\n // contenteditable \uC18D\uC131\uC774 \uC788\uB294 \uC694\uC18C\n if (element.getAttribute(\"contenteditable\") === \"true\") {\n return true;\n }\n\n // \uCD94\uAC00 \uC140\uB809\uD130 \uCCB4\uD06C\n if (config?.excludeSelectors) {\n for (const selector of config.excludeSelectors) {\n if (element.matches(selector)) {\n return true;\n }\n }\n }\n\n return checkElementAndParents(element, config);\n};\n\n/**\n * \uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C\uB4E4\uC744 \uD655\uC778\uD558\uC5EC \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uC694\uC18C\uC778\uC9C0 \uD310\uB2E8\n */\nconst checkElementAndParents = (\n element: Element,\n config?: DragScrollConfig\n): boolean => {\n // \uD45C\uC2DC \uC18D\uC131\uC73C\uB85C \uB048 \uC601\uC5ED \u2014 \uC904 \uC790\uCCB4\uB97C \uB04C\uC5B4 \uC21C\uC11C\uB97C \uBC14\uAFB8\uB294 \uBAA9\uB85D(dnd-kit \uB4F1)\uC740 \uB04C\uAE30\uAC00 \uACE7 \uC21C\uC11C \uC62E\uAE30\uAE30\uB77C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uAC19\uC774 \uB3CC\uBA74 \uC548 \uB41C\uB2E4.\n // \uD074\uB798\uC2A4 \uBAA9\uB85D\uC5D0 \uC5C6\uB294 \uC694\uC18C\uB3C4 \uC18C\uBE44\uCC98\uAC00 data-drag-scroll=\"off\" \uB9CC \uBD99\uC774\uBA74 \uBE60\uC9C4\uB2E4(\uAE4A\uC774 \uC81C\uD55C \uC5C6\uC774 \uC870\uC0C1\uAE4C\uC9C0).\n if (element.closest('[data-drag-scroll=\"off\"]')) {\n return true;\n }\n\n // \uBAA8\uB4E0 \uC81C\uC678 \uD074\uB798\uC2A4\uB4E4 \uD569\uCE58\uAE30 (\uAE30\uBCF8 \uD074\uB798\uC2A4 + \uC0AC\uC6A9\uC790 \uCD94\uAC00 \uD074\uB798\uC2A4)\n const allExcludeClasses = [\n ...DEFAULT_EXCLUDE_CLASSES,\n ...(config?.excludeClasses || []),\n ];\n\n let currentElement: Element | null = element;\n let depth = 0;\n const maxDepth = 5; // \uCD5C\uB300 5\uB2E8\uACC4\uAE4C\uC9C0 \uBD80\uBAA8 \uC694\uC18C \uD655\uC778\n\n while (currentElement && depth <= maxDepth) {\n // \uD604\uC7AC \uC694\uC18C\uAC00 \uC81C\uC678 \uD074\uB798\uC2A4\uB97C \uAC00\uC9C0\uACE0 \uC788\uB294\uC9C0 \uD655\uC778\n if (\n allExcludeClasses.some((cls) =>\n currentElement!.classList.contains(cls)\n )\n ) {\n return true;\n }\n\n // \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB8E8\uD2B8\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC911\uB2E8\n if (currentElement.classList.contains(\"MuiDialogContent-root\")) {\n break;\n }\n\n currentElement = currentElement.parentElement;\n depth++;\n }\n\n return false;\n};\n\n/**\n * \uB2E4\uB978 \uB2E4\uC774\uC5BC\uB85C\uADF8\uB098 \uBAA8\uB2EC\uC774 \uC0C1\uC704\uC5D0 \uC788\uB294\uC9C0 \uD655\uC778\n */\nexport const hasUpperModal = (scrollContainer: Element): boolean => {\n const currentDialog = scrollContainer.closest(\n \".MuiDialog-root\"\n ) as HTMLElement;\n if (!currentDialog) return false;\n\n const allModals = document.querySelectorAll(\n \".MuiDialog-root, .MuiModal-root, .MuiPopover-root\"\n );\n const currentZIndex = parseInt(\n window.getComputedStyle(currentDialog).zIndex || \"0\"\n );\n\n for (const modal of allModals) {\n if (modal !== currentDialog) {\n const modalElement = modal as HTMLElement;\n const modalZIndex = parseInt(\n window.getComputedStyle(modalElement).zIndex || \"0\"\n );\n\n if (\n modalZIndex > currentZIndex &&\n modalElement.style.display !== \"none\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n};\n\n/**\n * \uD074\uB9AD\uD55C \uC694\uC18C\uAC00 \uB2E4\uB978 \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB0B4\uBD80\uC778\uC9C0 \uD655\uC778\n */\nexport const isClickInOtherDialog = (\n clickedElement: Element,\n currentDialog: Element | null\n): boolean => {\n const parentDialog = clickedElement.closest(\n \".MuiDialog-root, .MuiPopover-root, .MuiModal-root\"\n );\n return parentDialog !== null && parentDialog !== currentDialog;\n};\n", "import type React from \"react\";\n\n/**\n * \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 `padding-bottom` \uC740 \uB0B4\uC6A9\uC774 \uB118\uCE60 \uB54C \uBE0C\uB77C\uC6B0\uC800\uAC00 \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC601\uC5ED(scrollHeight)\uC5D0\n * \uD3EC\uD568\uC2DC\uD0A4\uC9C0 \uC54A\uB294\uB2E4. `padding-top` \uC740 \uBC18\uC601\uB418\uC9C0\uB9CC bottom \uB9CC \uBB34\uC2DC\uB418\uB294 \uBE14\uB85D/\uD50C\uB809\uC2A4 \uC624\uBC84\uD50C\uB85C \uB3D9\uC791\uC774\uB77C,\n * \uC0AC\uC6A9\uC790\uAC00 `containerStyle`/`contentStyle` \uC5D0 `paddingBottom` \uC744 \uC918\uB3C4 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uC5EC\uBC31\uC774 \uC0AC\uB77C\uC9C4\uB2E4.\n *\n * \uADF8\uB798\uC11C \uC774 \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uC2A4\uD0C0\uC77C\uC5D0\uC11C \uAC77\uC5B4\uB0B4\uACE0, \uAC19\uC740 \uB192\uC774\uC758 spacer \uC5D8\uB9AC\uBA3C\uD2B8\uB97C\n * content \uB9E8 \uB05D\uC5D0 \uB80C\uB354\uD574 \uB3D9\uC77C\uD55C \uC5EC\uBC31\uC744 \uC2E4\uC81C \uB808\uC774\uC544\uC6C3 \uB192\uC774\uB85C \uB9CC\uB4E4\uC5B4 \uC900\uB2E4.\n */\n\n/** CSS \uAE38\uC774 \uAC12\uC744 px \uC22B\uC790\uB85C \uBC14\uAFBC\uB2E4. px/\uC22B\uC790\uB9CC \uC9C0\uC6D0\uD558\uACE0 \uADF8 \uC678 \uB2E8\uC704(%, em \uB4F1)\uB294 null \uC744 \uBC18\uD658\uD55C\uB2E4. */\nexport const toPixelLength = (\n value: string | number | undefined,\n): number | null => {\n if (value === undefined || value === null) return null;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n\n const trimmed = value.trim();\n if (trimmed === \"\") return null;\n\n // \uC22B\uC790\uB9CC \uC788\uAC70\uB098 px \uB2E8\uC704\uC778 \uACBD\uC6B0\uB9CC \uC548\uC804\uD558\uAC8C \uBCC0\uD658\uD55C\uB2E4.\n const match = /^(-?\\d*\\.?\\d+)(px)?$/.exec(trimmed);\n if (!match) return null;\n\n const parsed = Number.parseFloat(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\n/** \uCD95\uC57D\uD615 `padding` \uBB38\uC790\uC5F4\uC5D0\uC11C bottom \uAC12\uC744 \uCD94\uCD9C\uD55C\uB2E4(1~4\uAC1C \uAC12 \uADDC\uCE59). */\nconst readShorthandPaddingBottom = (\n padding: string | number | undefined,\n): string | number | undefined => {\n if (padding === undefined || padding === null) return undefined;\n // \uC22B\uC790 \uCD95\uC57D\uD615\uC740 \uB124 \uBC29\uD5A5\uC774 \uAC19\uC740 \uAC12\uC774\uB2E4.\n if (typeof padding === \"number\") return padding;\n\n const parts = padding.trim().split(/\\s+/);\n if (parts.length === 0 || parts[0] === \"\") return undefined;\n // padding: a | a b | a b c | a b c d \u2192 bottom \uC740 \uAC01\uAC01 a | a | c | c\n if (parts.length === 1) return parts[0];\n if (parts.length === 2) return parts[0];\n return parts[2];\n};\n\n/** \uCD95\uC57D\uD615 `padding` \uBB38\uC790\uC5F4\uC5D0\uC11C bottom \uB9CC 0 \uC73C\uB85C \uBC14\uAFBC\uB2E4. */\nconst withShorthandPaddingBottomRemoved = (\n padding: string | number,\n): string => {\n const parts =\n typeof padding === \"number\"\n ? [`${padding}px`]\n : padding.trim().split(/\\s+/);\n\n // \uAC01 \uCD95\uC57D\uD615\uC744 4\uAC12 \uD615\uD0DC\uB85C \uD3BC\uCE5C \uB4A4 bottom \uB9CC 0 \uC73C\uB85C \uB9CC\uB4E0\uB2E4.\n const [top, right = top, bottom = top, left = right] = parts;\n void bottom;\n return `${top} ${right} 0 ${left}`;\n};\n\n/** paddingBottom \uC744 \uAC77\uC5B4\uB0B8 \uC2A4\uD0C0\uC77C\uACFC, spacer \uB85C \uC62E\uAE38 \uB192\uC774(px)\uB97C \uD568\uAED8 \uBC18\uD658\uD55C\uB2E4. */\nexport interface ExtractedBottomPadding {\n style: React.CSSProperties | undefined; // \uD558\uB2E8 padding \uC744 \uC81C\uAC70\uD55C \uC2A4\uD0C0\uC77C\n spacerHeight: number; // spacer \uB85C \uB9CC\uB4E4\uC5B4\uC57C \uD560 \uB192\uC774(px, 0 \uC774\uBA74 \uBD88\uD544\uC694)\n}\n\n/**\n * \uC2A4\uD0C0\uC77C\uC5D0\uC11C \uD558\uB2E8 padding \uC744 \uBD84\uB9AC\uD55C\uB2E4.\n * px \uB85C \uD658\uC0B0 \uAC00\uB2A5\uD55C \uAC12\uB9CC spacer \uB85C \uC62E\uAE30\uACE0, %/em \uAC19\uC774 \uD658\uC0B0 \uBD88\uAC00\uD55C \uAC12\uC740 \uC6D0\uB798 \uC2A4\uD0C0\uC77C\uC5D0 \uADF8\uB300\uB85C \uB454\uB2E4.\n */\nexport const extractBottomPadding = (\n style: React.CSSProperties | undefined,\n): ExtractedBottomPadding => {\n if (!style) return { style, spacerHeight: 0 };\n\n const hasLonghand = style.paddingBottom !== undefined;\n const hasShorthand = style.padding !== undefined;\n if (!hasLonghand && !hasShorthand) return { style, spacerHeight: 0 };\n\n // longhand(paddingBottom)\uAC00 \uCD95\uC57D\uD615(padding)\uBCF4\uB2E4 \uC6B0\uC120\uD55C\uB2E4.\n const rawBottom = hasLonghand\n ? style.paddingBottom\n : readShorthandPaddingBottom(style.padding);\n\n const pixels = toPixelLength(rawBottom as string | number | undefined);\n // \uD658\uC0B0 \uBD88\uAC00\uD558\uAC70\uB098 0 \uC774\uD558\uBA74 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uB294\uB2E4(\uC0AC\uC6A9\uC790 \uC758\uB3C4 \uBCF4\uC874).\n if (pixels === null || pixels <= 0) return { style, spacerHeight: 0 };\n\n const nextStyle: React.CSSProperties = { ...style };\n if (hasShorthand && style.padding !== undefined) {\n nextStyle.padding = withShorthandPaddingBottomRemoved(\n style.padding as string | number,\n );\n }\n if (hasLonghand) {\n nextStyle.paddingBottom = 0;\n }\n\n return { style: nextStyle, spacerHeight: pixels };\n};\n", "/**\n * usePullToRefresh.ts\n * \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68(pull-to-refresh) \uB85C\uC9C1\n *\n * \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uAC00 \uB9E8 \uC704(scrollTop <= 0)\uC5D0 \uC788\uC744 \uB54C \uC544\uB798\uB85C \uB2F9\uAE30\uB294 \uD130\uCE58 \uC81C\uC2A4\uCC98\uB97C \uAC10\uC9C0\uD574,\n * \uC784\uACC4 \uAC70\uB9AC \uC774\uC0C1 \uB2F9\uAE34 \uC0C1\uD0DC\uC5D0\uC11C \uC190\uC744 \uB5BC\uBA74 onRefresh \uCF5C\uBC31\uC744 \uC2E4\uD589\uD55C\uB2E4.\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n */\n\nimport { useEffect, useRef, useState, RefObject } from \"react\";\nimport { PullToRefreshConfig } from \"../types\";\n\ninterface UsePullToRefreshProps {\n containerRef: RefObject<HTMLDivElement | null>; // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 ref\n config: PullToRefreshConfig; // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC124\uC815\n}\n\ninterface UsePullToRefreshResult {\n pullDistance: number; // \uD604\uC7AC \uB2F9\uAE40 \uAC70\uB9AC(px, \uAC10\uC1E0 \uC801\uC6A9 \uD6C4) \u2014 \uC778\uB514\uCF00\uC774\uD130 \uC704\uCE58\uC6A9\n isRefreshing: boolean; // onRefresh \uC2E4\uD589 \uC911 \uC5EC\uBD80 \u2014 \uC2A4\uD53C\uB108 \uD45C\uC2DC\uC6A9\n pullProgress: number; // \uC784\uACC4 \uAC70\uB9AC \uB300\uBE44 \uC9C4\uD589\uB960(0~1) \u2014 \uC778\uB514\uCF00\uC774\uD130 \uD68C\uC804/\uC0C9\uC0C1\uC6A9\n pullThreshold: number; // \uCD5C\uC885 \uC784\uACC4 \uAC70\uB9AC(px)\n}\n\n/** \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC81C\uC2A4\uCC98\uB97C \uAC10\uC9C0\uD558\uACE0 \uC0C1\uD0DC\uB97C \uBC18\uD658\uD55C\uB2E4. */\nexport const usePullToRefresh = ({\n containerRef,\n config,\n}: UsePullToRefreshProps): UsePullToRefreshResult => {\n // \uC124\uC815 \uAE30\uBCF8\uAC12\n const enabled = config.enabled ?? true; // \uAE30\uBCF8 \uD65C\uC131 (onRefresh \uAC00 \uC5C6\uC73C\uBA74 \uB3D9\uC791 \uC548 \uD568)\n const threshold = config.threshold ?? 80; // \uB193\uC558\uC744 \uB54C \uC0C8\uB85C\uACE0\uCE68\uC774 \uC2E4\uD589\uB418\uB294 \uC784\uACC4 \uB2F9\uAE40 \uAC70\uB9AC\n const maxDistance = config.maxDistance ?? threshold * 2; // \uC778\uB514\uCF00\uC774\uD130\uAC00 \uB530\uB77C\uC624\uB294 \uCD5C\uB300 \uAC70\uB9AC\n const onRefresh = config.onRefresh;\n\n const [pullDistance, setPullDistance] = useState(0);\n const [isRefreshing, setIsRefreshing] = useState(false);\n\n // \uD130\uCE58 \uCD94\uC801 \uC0C1\uD0DC (\uB80C\uB354\uC640 \uBB34\uAD00\uD558\uBBC0\uB85C ref \uB85C \uAD00\uB9AC)\n const startYRef = useRef(0);\n const startXRef = useRef(0);\n const trackingRef = useRef(false); // \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uB41C \uD130\uCE58 \uCD94\uC801 \uC911\n const pullingRef = useRef(false); // \uD558\uD5A5 \uB2F9\uAE40\uC73C\uB85C \uD655\uC815\uB428(\uC2A4\uD06C\uB864 \uB300\uC2E0 \uC778\uB514\uCF00\uC774\uD130 \uB3D9\uC791)\n const nativeBounceRef = useRef(false); // \uB2F9\uAE40 \uD655\uC815 \uC2DC\uC810\uC5D0 \uC774\uBBF8 \uB124\uC774\uD2F0\uBE0C \uB7EC\uBC84\uBC34\uB4DC(iOS)\uAC00 \uC2DC\uC791\uB41C \uC0C1\uD0DC\n const pullDistanceRef = useRef(0); // touchend \uC5D0\uC11C \uCD5C\uC2E0 \uAC70\uB9AC \uCC38\uC870\uC6A9\n const refreshingRef = useRef(false); // \uB9AC\uC2A4\uB108 \uC548\uC5D0\uC11C \uCD5C\uC2E0 \uC0C8\uB85C\uACE0\uCE68 \uC0C1\uD0DC \uCC38\uC870\uC6A9\n\n useEffect(() => {\n refreshingRef.current = isRefreshing;\n }, [isRefreshing]);\n\n useEffect(() => {\n const container = containerRef.current;\n const active = enabled && typeof onRefresh === \"function\";\n if (!container || !active) return;\n\n /** \uD130\uCE58 \uC9C0\uC810\uBD80\uD130 \uCEE8\uD14C\uC774\uB108\uAE4C\uC9C0\uC758 \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC870\uC0C1\uB4E4\uC774 \uC804\uBD80 \uB9E8 \uC704\uC778\uC9C0 \uD655\uC778\uD55C\uB2E4.\n * (\uB0B4\uBD80 \uBAA9\uB85D \uC2A4\uD06C\uB864\uB7EC\uAC00 \uC2A4\uD06C\uB864\uB41C \uC0C1\uD0DC\uC5D0\uC11C \uB2F9\uAE40\uC774 \uC624\uBC1C\uB3D9\uD558\uC9C0 \uC54A\uAC8C \uD55C\uB2E4) */\n const isTouchPathAtTop = (target: EventTarget | null): boolean => {\n let el = target instanceof Element ? target : null;\n while (el && el !== container) {\n if (\n el instanceof HTMLElement &&\n el.scrollHeight > el.clientHeight + 1 &&\n el.scrollTop > 0\n ) {\n return false;\n }\n el = el.parentElement;\n }\n return true;\n };\n\n /** \uD130\uCE58 \uC2DC\uC791 \u2014 \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uB41C \uB2E8\uC77C \uD130\uCE58\uB9CC \uCD94\uC801 \uD6C4\uBCF4\uB85C \uC0BC\uB294\uB2E4. */\n const handleTouchStart = (e: TouchEvent) => {\n // \uC774\uC804 \uC81C\uC2A4\uCC98\uC758 \uC794\uC874 \uC0C1\uD0DC\uB97C \uBB34\uC870\uAC74 \uCD08\uAE30\uD654\uD55C\uB2E4(v1.7.5 \uC218\uC815). \uD130\uCE58\uD588\uB358 \uC694\uC18C\uAC00\n // \uC81C\uC2A4\uCC98 \uB3C4\uC911 \uC5B8\uB9C8\uC6B4\uD2B8\uB418\uAC70\uB098(\uC2E4\uC2DC\uAC04 \uB9AC\uB80C\uB354 \uB4F1) touchend/touchcancel \uC774 \uC720\uC2E4\uB418\uBA74\n // (WebView \uC81C\uC2A4\uCC98 \uC544\uB808\uB098\uAC00 \uD130\uCE58\uB97C \uAC00\uB85C\uCC4C \uB54C\u00B7\uC911\uCCA9 \uC2A4\uD06C\uB864\uB7EC \uD658\uACBD\uC5D0\uC11C \uD754\uD568) touch\n // \uC774\uBCA4\uD2B8\uAC00 \uBD84\uB9AC\uB41C \uC6D0\uB798 \uD0C0\uAE43\uC5D0 \uACE0\uC815\uB418\uC5B4 touchend \uAC00 \uCEE8\uD14C\uC774\uB108\uAE4C\uC9C0 \uBC84\uBE14\uB9C1\uB418\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uB9AC\uC14B \uC5C6\uC774\uB294 \uCD94\uC801/\uB2F9\uAE40 \uD50C\uB798\uADF8\uAC00 \uB0A8\uC544 \uB2E4\uC74C \uC81C\uC2A4\uCC98\uC758 touchmove \uAC00 \uC804\uBD80 preventDefault\n // \uB418\uC5B4 \uC2A4\uD06C\uB864\uC774 \uACC4\uC18D \uB9C9\uD788\uACE0, \uC794\uC874 \uC2DC\uC791\uC88C\uD45C \uAE30\uC900 \uAC70\uB9AC\uB85C \uC624\uC791\uB3D9 \uC0C8\uB85C\uACE0\uCE68\uAE4C\uC9C0 \uB0AC\uB2E4.\n trackingRef.current = false;\n pullingRef.current = false;\n nativeBounceRef.current = false;\n\n if (refreshingRef.current) return;\n if (e.touches.length !== 1) return;\n // \uC815\uD655\uD788 \uB9E8 \uC704(scrollTop === 0)\uC5D0\uC11C \uC2DC\uC791\uD55C \uD130\uCE58\uB9CC \uCD94\uC801\uD55C\uB2E4.\n // scrollTop > 0 \uC740 \uC77C\uBC18 \uC2A4\uD06C\uB864\uC774\uACE0, scrollTop < 0 \uC740 iOS \uB7EC\uBC84\uBC34\uB4DC\uAC00\n // \uC9C4\uD589 \uC911\uC774\uB77C\uB294 \uB73B \u2014 \uBC14\uC6B4\uC2A4 \uC911 touchmove \uB97C preventDefault \uD558\uBA74 WebKit\n // \uC2A4\uD06C\uB864\uB7EC\uAC00 \uC601\uAD6C\uD788 \uBA48\uCD94\uB294(\uC774\uD6C4 \uBAA8\uB4E0 \uD130\uCE58 \uBB34\uC2DC) \uD504\uB9AC\uC988\uAC00 \uBC1C\uC0DD\uD55C\uB2E4.\n if (container.scrollTop !== 0) return;\n if (!isTouchPathAtTop(e.target)) return;\n trackingRef.current = true;\n startYRef.current = e.touches[0].clientY;\n startXRef.current = e.touches[0].clientX;\n };\n\n /** \uD130\uCE58 \uC774\uB3D9 \u2014 \uD558\uD5A5 \uC6B0\uC138 \uB2F9\uAE40\uC774 \uD655\uC815\uB418\uBA74 \uC2A4\uD06C\uB864\uC744 \uB9C9\uACE0 \uC778\uB514\uCF00\uC774\uD130\uB97C \uB530\uB77C \uB0B4\uB9B0\uB2E4. */\n const handleTouchMove = (e: TouchEvent) => {\n if (!trackingRef.current || refreshingRef.current) return;\n if (e.touches.length !== 1) return;\n\n const dy = e.touches[0].clientY - startYRef.current;\n const dx = e.touches[0].clientX - startXRef.current;\n\n if (!pullingRef.current) {\n // \uC544\uC9C1 \uBBF8\uD655\uC815 \u2014 \uC704\uB85C \uC2A4\uD06C\uB864\uD588\uAC70\uB098 \uC218\uD3C9 \uC81C\uC2A4\uCC98\uBA74 \uCD94\uC801 \uC885\uB8CC\n if (dy <= 0 || container.scrollTop > 0) {\n trackingRef.current = false;\n return;\n }\n // \uC218\uC9C1 \uC6B0\uC138(\uB300\uAC01\uC120 \uC2A4\uC640\uC774\uD504 \uC624\uBC1C\uB3D9 \uBC29\uC9C0) + \uCD5C\uC18C \uC774\uB3D9\uB7C9\uC744 \uB118\uC73C\uBA74 \uB2F9\uAE40 \uD655\uC815\n if (dy > 8 && dy > Math.abs(dx)) {\n pullingRef.current = true;\n // \uD655\uC815 \uC2DC\uC810\uC5D0 \uC774\uBBF8 \uB124\uC774\uD2F0\uBE0C \uB7EC\uBC84\uBC34\uB4DC\uAC00 \uC2DC\uC791\uB410\uC73C\uBA74(iOS: \uB2F9\uAE30\uB294 \uB3D9\uC548\n // scrollTop \uC774 \uC74C\uC218\uB85C \uB0B4\uB824\uAC04\uB2E4) preventDefault \uBAA8\uB4DC\uB85C \uB4E4\uC5B4\uAC00\uC9C0 \uC54A\uB294\uB2E4.\n // \uC9C4\uD589 \uC911\uC778 \uBC14\uC6B4\uC2A4\uB97C \uB3C4\uC911\uC5D0 \uCDE8\uC18C\uD558\uBA74 WebKit \uC2A4\uD06C\uB864\uB7EC\uAC00 \uD504\uB9AC\uC988\uB41C\uB2E4.\n // \uB300\uC2E0 \uB124\uC774\uD2F0\uBE0C \uBC14\uC6B4\uC2A4\uB97C \uADF8\uB300\uB85C \uB450\uACE0 \uAC70\uB9AC\uB9CC \uCD94\uC801\uD574, \uB193\uC744 \uB54C \uC784\uACC4\uB97C\n // \uB118\uC5C8\uC73C\uBA74 \uC0C8\uB85C\uACE0\uCE68\uD55C\uB2E4(\uC778\uB514\uCF00\uC774\uD130\uB3C4 \uB3D9\uC77C\uD558\uAC8C \uD45C\uC2DC).\n nativeBounceRef.current = container.scrollTop < 0;\n } else {\n return;\n }\n }\n\n // \uB2F9\uAE40 \uD655\uC815 \uAD6C\uAC04 \u2014 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864/\uC624\uBC84\uC2A4\uD06C\uB864\uC744 \uB9C9\uACE0 \uAC70\uB9AC(\uAC10\uC1E0 0.5)\uB97C \uAC31\uC2E0\uD55C\uB2E4.\n // (\uB124\uC774\uD2F0\uBE0C \uBC14\uC6B4\uC2A4 \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uCDE8\uC18C\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC704 \uD504\uB9AC\uC988 \uBC29\uC9C0)\n if (!nativeBounceRef.current && e.cancelable) e.preventDefault();\n const distance = Math.min(Math.max(dy, 0) * 0.5, maxDistance);\n pullDistanceRef.current = distance;\n setPullDistance(distance);\n };\n\n /** \uD130\uCE58 \uC885\uB8CC \u2014 \uC784\uACC4 \uAC70\uB9AC \uC774\uC0C1\uC774\uBA74 onRefresh \uC2E4\uD589, \uC544\uB2C8\uBA74 \uC6D0\uC704\uCE58. */\n const handleTouchEnd = () => {\n if (!trackingRef.current) return;\n trackingRef.current = false;\n\n if (!pullingRef.current) return;\n pullingRef.current = false;\n nativeBounceRef.current = false;\n\n const distance = pullDistanceRef.current;\n pullDistanceRef.current = 0;\n\n if (distance >= threshold && !refreshingRef.current) {\n // \uC0C8\uB85C\uACE0\uCE68 \uC2E4\uD589 \u2014 \uC644\uB8CC(Promise \uD574\uC18C)\uAE4C\uC9C0 \uC2A4\uD53C\uB108\uB97C \uC784\uACC4 \uC704\uCE58\uC5D0 \uC720\uC9C0\uD55C\uB2E4.\n setIsRefreshing(true);\n setPullDistance(threshold);\n Promise.resolve()\n .then(() => onRefresh())\n .catch(() => undefined)\n .finally(() => {\n setIsRefreshing(false);\n setPullDistance(0);\n });\n } else {\n setPullDistance(0);\n }\n };\n\n // preventDefault \uB97C \uC4F0\uBBC0\uB85C touchmove \uB294 passive: false \uB85C \uB4F1\uB85D\uD574\uC57C \uD55C\uB2E4.\n // \uB2E8 \uB9E8 \uC704\uC5D0 \uC788\uC744 \uB54C\uB9CC \uAC74\uB2E4(1.7.34) \u2014 passive: false \uB9AC\uC2A4\uB108\uAC00 \uC788\uC73C\uBA74 \uBE0C\uB77C\uC6B0\uC800\uB294 \uD130\uCE58\uB97C \uC2DC\uC791\uD560 \uB54C\uB9C8\uB2E4\n // \uBA54\uC778 \uC2A4\uB808\uB4DC\uAC00 \uADF8 \uB9AC\uC2A4\uB108\uB97C \uB3CC\uB9B4 \uB54C\uAE4C\uC9C0 \uC2A4\uD06C\uB864\uC744 \uBD99\uC7A1\uB294\uB2E4. \uBAA9\uB85D \uC911\uAC04\uC5D0\uC11C \uBA54\uC778 \uC2A4\uB808\uB4DC\uAC00 \uC7A0\uAE50 \uBC14\uC058\uBA74\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCDC4\uB2E4\uAC00 \uADF8\uB3D9\uC548 \uBBFC \uAC70\uB9AC\uB9CC\uD07C \uD55C \uBC88\uC5D0 \uD280\uC5C8\uB2E4(\uC548\uB4DC\uB85C\uC774\uB4DC \uC571 \uBA54\uC77C \uBAA9\uB85D, 2026-10-19 \u2014 117px).\n // \uB2F9\uAE40\uC740 \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uD55C \uD130\uCE58\uB9CC \uC4F0\uBBC0\uB85C, \uB0B4\uB824\uAC00 \uC788\uB294 \uB3D9\uC548\uC740 \uB9AC\uC2A4\uB108\uB97C \uB5BC\uC5B4 \uC2A4\uD06C\uB864\uC744 \uD569\uC131 \uCABD\uC5D0 \uB9E1\uAE34\uB2E4.\n let moveAttached = false;\n /** \uB2F9\uAE40\uC6A9 touchmove \uB9AC\uC2A4\uB108\uB97C \uAC74\uB2E4 */\n const attachMove = () => {\n if (moveAttached) return;\n container.addEventListener(\"touchmove\", handleTouchMove, {\n passive: false,\n });\n moveAttached = true;\n };\n /** \uB2F9\uAE40\uC6A9 touchmove \uB9AC\uC2A4\uB108\uB97C \uB5C0\uB2E4 */\n const detachMove = () => {\n if (!moveAttached) return;\n container.removeEventListener(\"touchmove\", handleTouchMove);\n moveAttached = false;\n };\n /** \uC2A4\uD06C\uB864 \uC704\uCE58\uC5D0 \uB9DE\uCDB0 \uAC74\uB2E4 \u00B7 \uB5C0\uB2E4 \u2014 \uC138\uB85C\uB85C \uAD74\uB7EC\uAC00\uB294 \uC0C1\uC790(\uC548\uCABD \uAC00\uC0C1 \uBAA9\uB85D \uD3EC\uD568)\uB9CC \uBCF8\uB2E4 */\n const syncMove = (e: Event) => {\n const t = e.target;\n if (!(t instanceof HTMLElement)) return;\n if (t.scrollHeight <= t.clientHeight + 1) return; // \uAC00\uB85C \uC2A4\uD06C\uB864 \uB4F1\n if (t.scrollTop <= 0) attachMove();\n else if (!trackingRef.current) detachMove();\n };\n attachMove();\n container.addEventListener(\"scroll\", syncMove, {\n passive: true,\n capture: true,\n });\n container.addEventListener(\"touchstart\", handleTouchStart, {\n passive: true,\n });\n container.addEventListener(\"touchend\", handleTouchEnd, {\n passive: true,\n });\n container.addEventListener(\"touchcancel\", handleTouchEnd, {\n passive: true,\n });\n\n return () => {\n container.removeEventListener(\"touchstart\", handleTouchStart);\n detachMove();\n container.removeEventListener(\"scroll\", syncMove, { capture: true });\n container.removeEventListener(\"touchend\", handleTouchEnd);\n container.removeEventListener(\"touchcancel\", handleTouchEnd);\n };\n }, [containerRef, enabled, onRefresh, threshold, maxDistance]);\n\n return {\n pullDistance,\n isRefreshing,\n pullProgress: Math.min(pullDistance / threshold, 1),\n pullThreshold: threshold,\n };\n};\n", "/**\n * useCoarsePointer.ts\n * \uC8FC \uC785\uB825\uC7A5\uCE58\uAC00 \uD130\uCE58(coarse pointer)\uC778\uC9C0 \uAC10\uC9C0\uD558\uB294 \uD6C5.\n *\n * \uD130\uCE58 \uC6B0\uC120 \uAE30\uAE30(\uBAA8\uBC14\uC77C/\uD0DC\uBE14\uB9BF)\uC5D0\uC11C\uB294 \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799/thumb\u00B7\uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uBD88\uD544\uC694\uD558\uACE0\n * \uC624\uD788\uB824 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\u00B7PTR \uC81C\uC2A4\uCC98\uC640 \uCDA9\uB3CC\uD560 \uC218 \uC788\uC5B4, \uC774 \uAC12\uC73C\uB85C \uCEE4\uC2A4\uD140 UI \uB97C \uB044\uB294 \uD310\uB2E8\uC5D0 \uC4F4\uB2E4.\n * `pointer: coarse` \uBBF8\uB514\uC5B4\uCFFC\uB9AC \uAE30\uC900(\uB9C8\uC6B0\uC2A4=fine \uB294 false). SSR \uB4F1 matchMedia \uBBF8\uC9C0\uC6D0 \uD658\uACBD\uC740 false.\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n */\n\nimport { useEffect, useState } from \"react\";\n\n/** \uC8FC \uC785\uB825\uC7A5\uCE58\uAC00 \uD130\uCE58(coarse pointer)\uBA74 true \uB97C \uBC18\uD658\uD55C\uB2E4. */\nexport function useCoarsePointer(): boolean {\n const [coarse, setCoarse] = useState<boolean>(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n return window.matchMedia(\"(pointer: coarse)\").matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n const mql = window.matchMedia(\"(pointer: coarse)\");\n /** \uBBF8\uB514\uC5B4\uCFFC\uB9AC \uB9E4\uCE58 \uBCC0\uD654\uB97C \uC0C1\uD0DC\uC5D0 \uBC18\uC601\uD55C\uB2E4. */\n const handleChange = (event: MediaQueryListEvent) => setCoarse(event.matches);\n // \uCD08\uAE30\uAC12 \uB3D9\uAE30\uD654(\uB9C8\uC6B4\uD2B8 \uC2DC\uC810\uC758 \uC2E4\uC81C \uAC12\uC73C\uB85C \uBCF4\uC815).\n setCoarse(mql.matches);\n // \uAD6C\uD615 \uC0AC\uD30C\uB9AC \uD638\uD658: addEventListener \uAC00 \uC5C6\uC73C\uBA74 addListener \uB85C \uD3F4\uBC31.\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", handleChange);\n return () => mql.removeEventListener(\"change\", handleChange);\n }\n mql.addListener(handleChange);\n return () => mql.removeListener(handleChange);\n }, []);\n\n return coarse;\n}\n"],
5
- "mappings": "AAwBA,OACI,UAAAA,EACA,aAAAC,EACA,YAAAC,EACA,eAAAC,EACA,WAAAC,GAEA,cAAAC,GACA,uBAAAC,GACA,mBAAAC,GACA,SAAAC,OACG,QCRP,IAAMC,GAA0B,CAE5B,SACA,WACA,QACA,SACA,YACA,eACA,kBAGA,qBACA,mBACA,yBACA,uBACA,iBACA,sBACA,oBACA,iBACA,wBACA,sBACA,gBACA,wBACA,sBACA,kBACA,iBACA,kBACA,iBACA,sBACA,iBACA,yBACA,uBACA,qBACA,iBACA,qBACA,sBACA,mBACA,gBACA,iBACA,yBAGA,YACA,0BACA,wBACA,mBACA,2BACA,aACA,sBACA,8BACA,sBACA,eACA,qBACA,aACA,mBACA,kBACA,sBACA,aACA,mBACA,oBACA,aACA,eACA,uBACA,YACA,oBACA,WACA,aACA,kBACA,gBACA,wBACA,UACA,eACA,uBACA,WACA,gBACA,cACA,cACA,YACA,aACA,kBACA,oBACA,eACA,eAGA,WACA,cACA,YACA,oBACA,oBACA,iBACA,YACA,cACA,iBACA,YACA,YACA,kBACA,cACA,iBACA,cACA,aACA,mBACA,aACA,YACA,WACA,mBACA,kBACA,aACA,qBACA,UACA,kBACA,eACA,gBACA,WAEA,WACA,eACA,iBACA,eACA,gBACA,kBACA,aACA,eACA,eACA,iBACA,cAGA,YACA,eACA,aACA,YACA,kBACA,oBACA,aACA,UACA,YACA,QACA,cAGA,gBACA,2BACA,aACA,2BACA,4BAGA,aACA,kBACA,mBACA,oBACA,mBACA,YACA,aACA,aAGA,uBACA,oBACA,gBACA,cACA,mBAGA,YACA,aACA,aACA,sBACA,YAGA,eACA,gBAGA,mBACA,8BACA,6BAGA,gBACA,wBACA,wBACA,iBACA,uBACA,gBAGA,cACA,mBACA,UACA,aACA,aACJ,EAQaC,GAAqB,CAC9BC,EACAC,IACU,CAOV,GALID,aAAmB,aAAeA,EAAQ,mBAK1CA,EAAQ,QAAQ,qCAAqC,EACrD,MAAO,GAGX,IAAME,EAAUF,EAAQ,QAAQ,YAAY,EAG5C,GAAI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",
6
- "names": ["useRef", "useEffect", "useState", "useCallback", "useMemo", "forwardRef", "useImperativeHandle", "useLayoutEffect", "useId", "DEFAULT_EXCLUDE_CLASSES", "isTextInputElement", "element", "config", "tagName", "type", "selector", "checkElementAndParents", "allExcludeClasses", "currentElement", "depth", "maxDepth", "cls", "toPixelLength", "value", "trimmed", "match", "parsed", "readShorthandPaddingBottom", "padding", "parts", "withShorthandPaddingBottomRemoved", "top", "right", "bottom", "left", "extractBottomPadding", "style", "hasLonghand", "hasShorthand", "rawBottom", "pixels", "nextStyle", "useEffect", "useRef", "useState", "usePullToRefresh", "containerRef", "config", "enabled", "threshold", "maxDistance", "onRefresh", "pullDistance", "setPullDistance", "isRefreshing", "setIsRefreshing", "startYRef", "startXRef", "trackingRef", "pullingRef", "nativeBounceRef", "pullDistanceRef", "refreshingRef", "container", "isTouchPathAtTop", "target", "el", "handleTouchStart", "e", "handleTouchMove", "dy", "dx", "distance", "handleTouchEnd", "moveAttached", "attachMove", "detachMove", "syncMove", "t", "useEffect", "useState", "useCoarsePointer", "coarse", "setCoarse", "mql", "handleChange", "event", "jsx", "jsxs", "DEFAULT_THUMB_CONFIG", "DEFAULT_TRACK_CONFIG", "DEFAULT_ARROWS_CONFIG", "DEFAULT_DRAG_SCROLL_CONFIG", "DEFAULT_AUTO_HIDE_CONFIG", "DEFAULT_PULL_TO_REFRESH_CONFIG", "OverlayScrollbar", "forwardRef", "className", "style", "containerStyle", "contentStyle", "children", "onScroll", "thumb", "track", "arrows", "dragScroll", "autoHide", "pullToRefresh", "showScrollbarProp", "showHorizontalScrollbarProp", "detectInnerScroll", "scrollTopFab", "ref", "isCoarsePointer", "useCoarsePointer", "showScrollbar", "showHorizontalScrollbar", "instanceClass", "useId", "fabEnabled", "fabConfig", "fabThreshold", "fabIdleOpacity", "fabHideDelay", "fabPosition", "fabOffset", "fabOffsetBottom", "fabBackground", "fabColor", "fabSize", "showScrollTopFab", "setShowScrollTopFab", "useState", "isFabPressed", "setIsFabPressed", "fabHideTimerRef", "useRef", "prevPropsRef", "useEffect", "wrapperRef", "containerRef", "contentRef", "scrollbarRef", "thumbRef", "cachedScrollContainerRef", "pullDistance", "isRefreshing", "pullProgress", "usePullToRefresh", "pullIndicatorColor", "pullIndicatorVisible", "scrollbarVisible", "setScrollbarVisible", "isDragging", "setIsDragging", "isThumbHovered", "setIsThumbHovered", "dragStart", "setDragStart", "thumbHeight", "setThumbHeight", "thumbTop", "setThumbTop", "hasScrollableContent", "setHasScrollableContent", "isHorizontalThumbHovered", "setIsHorizontalThumbHovered", "isDraggingHorizontal", "setIsDraggingHorizontal", "dragStartHorizontal", "setDragStartHorizontal", "thumbWidth", "setThumbWidth", "thumbLeft", "setThumbLeft", "hasHorizontalScrollableContent", "setHasHorizontalScrollableContent", "isDragScrollPendingRef", "isDragScrolling", "setIsDragScrolling", "isDragScrollingRef", "dragScrollStart", "setDragScrollStart", "activeArrow", "setActiveArrow", "hoveredArrow", "setHoveredArrow", "isInitialized", "setIsInitialized", "wrapperPaddingBottom", "setWrapperPaddingBottom", "wheelTimeoutRef", "isWheelScrollingRef", "hideTimeoutRef", "hoverEnterTimeoutRef", "wheelShowTimeoutRef", "finalThumbConfig", "useMemo", "baseColor", "finalTrackConfig", "finalArrowsConfig", "finalDragScrollConfig", "finalAutoHideConfig", "isInitialDelayActive", "setIsInitialDelayActive", "finalThumbWidth", "finalTrackWidth", "thumbMinHeight", "showArrows", "arrowStep", "maintainFocus", "useCallback", "activeElement", "useImperativeHandle", "options", "findScrollableElement", "cached", "childScrollableElements", "child", "element", "parent", "isNestedInAnotherScrollbar", "isScrollable", "clearHideTimer", "clearHoverEnterTimer", "clearWheelShowTimer", "handleWrapperMouseLeave", "setHideTimer", "delay", "updateScrollbar", "scrollableElement", "containerHeight", "contentHeight", "scrollTop", "containerWidth", "contentWidth", "scrollLeft", "hasVerticalOverflow", "hasHorizontalOverflow", "hasAnyOverflow", "wrapperPaddingTopBottom", "paddingBottom", "computedStyle", "paddingTop", "arrowSpace", "availableHeight", "scrollRatio", "calculatedThumbHeight", "scrollableHeight", "thumbScrollableHeight", "calculatedThumbTop", "horizontalScrollableWidth", "scrollRatioHorizontal", "calculatedThumbWidth", "thumbScrollableWidth", "calculatedThumbLeft", "handleThumbMouseDown", "event", "actualScrollContainer", "handleMouseMove", "deltaY", "scrollDelta", "newScrollTop", "handleMouseUp", "handleTrackClick", "rect", "clickY", "handleUpArrowClick", "handleDownArrowClick", "container", "maxScrollTop", "handleHorizontalThumbMouseDown", "handleHorizontalMouseMove", "scrollableWidth", "deltaX", "newScrollLeft", "handleHorizontalMouseUp", "handleHorizontalTrackClick", "clickX", "handleDragScrollStart", "target", "isTextInputElement", "active", "handleDragScrollMove", "handleDragScrollEnd", "wasDragScrolling", "handleScroll", "shouldShow", "handleWheel", "elementsToWatch", "handleKeyDown", "key", "scrollHeight", "clientHeight", "lineScrollStep", "newThumbTop", "timer", "useLayoutEffect", "updateScrollbarRef", "resizeFrameRef", "scheduleResizeUpdate", "resizeObserver", "elementsToObserve", "observer", "adjustedTrackWidth", "styleId", "existing", "sel", "normalizedContainerStyle", "normalizedContentStyle", "bottomSpacerHeight", "extractBottomPadding", "content", "OverlayScrollbar_default"]
4
+ "sourcesContent": ["/**\n * OverlayScrollbar.tsx\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useMemo,\n ReactNode,\n forwardRef,\n useImperativeHandle,\n useLayoutEffect,\n useId,\n} from \"react\";\nimport { isTextInputElement } from \"./utils/dragScrollUtils\";\nimport { extractBottomPadding } from \"./utils/bottomSpacerUtils\";\nimport { usePullToRefresh } from \"./hooks/usePullToRefresh\";\nimport { useCoarsePointer } from \"./hooks/useCoarsePointer\";\nimport type { PullToRefreshConfig } from \"./types\";\n\n// thumb \uAD00\uB828 \uC124\uC815\nexport interface ThumbConfig {\n width?: number; // \uC378\uC758 \uB108\uBE44 (\uAE30\uBCF8\uAC00: 8px)\n minHeight?: number; // \uC378\uC758 \uCD5C\uC18C \uB192\uC774 (\uAE30\uBCF8\uAC00: 50px)\n radius?: number; // \uC378\uC758 border-radius (\uAE30\uBCF8\uAC00: width / 2)\n color?: string; // \uC378 \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: \"#606060\")\n opacity?: number; // \uAE30\uBCF8 \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 0.6)\n hoverColor?: string; // \uD638\uBC84 \uC2DC \uC0C9\uC0C1 (\uAE30\uBCF8\uAC12: color \uB3D9\uC77C)\n hoverOpacity?: number; // \uD638\uBC84 \uC2DC \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 1.0)\n}\n\n// track \uAD00\uB828 \uC124\uC815\nexport interface TrackConfig {\n width?: number; // \uD638\uBC84 \uC601\uC5ED\uC778 \uD2B8\uB799\uC758 \uB108\uBE44 (\uAE30\uBCF8\uAC12: 16px)\n color?: string; // \uD2B8\uB799 \uBC30\uACBD \uC0C9\uC0C1 (\uAE30\uBCF8\uAC12: \"rgba(128, 128, 128, 0.1)\")\n visible?: boolean; // \uD2B8\uB799 \uBC30\uACBD \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n alignment?: \"default\" | \"outside\"; // \uD2B8\uB799 \uC815\uB82C (\uAE30\uBCF8\uAC12: \"outside\", \"default\"\uB294 \uCF58\uD150\uCE20 \uC548\uCABD\uC5D0 \uD45C\uC2DC)\n radius?: number; // \uD2B8\uB799 \uBC30\uACBD\uC758 border-radius (\uAE30\uBCF8\uAC12: thumb.radius \uB610\uB294 4px)\n margin?: number; // \uD2B8\uB799 \uC0C1\uD558 \uB9C8\uC9C4 (\uAE30\uBCF8\uAC12: 4px)\n overflowX?: boolean; // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n overflowY?: boolean; // \uC138\uB85C \uC2A4\uD06C\uB864\uBC14 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n}\n\n// arrows \uAD00\uB828 \uC124\uC815\nexport interface ArrowsConfig {\n visible?: boolean; // \uD654\uC0B4\uD45C \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC00: false)\n step?: number; // \uD654\uC0B4\uD45C \uD074\uB9AD\uC2DC \uC2A4\uD06C\uB864 \uC774\uB3D9 \uAC70\uB9AC (\uAE30\uBCF8\uAC00: 50px)\n color?: string; // \uD654\uC0B4\uD45C \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: \"#808080\")\n opacity?: number; // \uAE30\uBCF8 \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 0.6)\n hoverColor?: string; // \uD638\uBC84 \uC2DC \uC0C9\uC0C1 (\uAE30\uBCF8\uAC00: color \uB3D9\uC77C)\n hoverOpacity?: number; // \uD638\uBC84 \uC2DC \uD22C\uBA85\uB3C4 (\uAE30\uBCF8\uAC00: 1.0)\n}\n\n// \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uAD00\uB828 \uC124\uC815\nexport interface DragScrollConfig {\n enabled?: boolean; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n excludeClasses?: string[]; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uCD94\uAC00 \uD074\uB798\uC2A4\uB4E4 (\uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C \uD655\uC778, \uCD5C\uB300 5\uB2E8\uACC4)\n excludeSelectors?: string[]; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uCD94\uAC00 CSS \uC140\uB809\uD130\uB4E4 (element.matches() \uC0AC\uC6A9)\n}\n\n// \uC790\uB3D9 \uC228\uAE40 \uAD00\uB828 \uC124\uC815\nexport interface AutoHideConfig {\n enabled?: boolean; // \uC790\uB3D9 \uC228\uAE40 \uD65C\uC131\uD654 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n delay?: number; // \uAE30\uBCF8 \uC790\uB3D9 \uC228\uAE40 \uC2DC\uAC04 (\uAE30\uBCF8\uAC12: 1500ms)\n delayOnWheel?: number; // \uD720 \uC2A4\uD06C\uB864 \uD6C4 \uC790\uB3D9 \uC228\uAE40 \uC2DC\uAC04 (\uAE30\uBCF8\uAC00: 700ms)\n initialDelay?: number; // \uB9C8\uC6B4\uD2B8 \uD6C4 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC9C0\uC5F0 \uC2DC\uAC04 (\uAE30\uBCF8\uAC12: 200ms, 0\uBCF4\uB2E4 \uD06C\uBA74 \uCD08\uAE30 \uC2A4\uD06C\uB864 \uC2DC \uC2A4\uD06C\uB864\uBC14 \uC228\uAE40)\n}\n\nexport interface OverlayScrollbarProps {\n className?: string;\n style?: React.CSSProperties; // wrapper div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n containerStyle?: React.CSSProperties; // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n contentStyle?: React.CSSProperties; // \uB0B4\uBD80 content div\uC5D0 \uC801\uC6A9\uD560 \uC2A4\uD0C0\uC77C\n children: ReactNode;\n onScroll?: (event: Event) => void;\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\n thumb?: ThumbConfig; // \uC378 \uAD00\uB828 \uC124\uC815\n track?: TrackConfig; // \uD2B8\uB799 \uAD00\uB828 \uC124\uC815\n arrows?: ArrowsConfig; // \uD654\uC0B4\uD45C\uB4E4 \uAD00\uB828 \uC124\uC815\n dragScroll?: DragScrollConfig; // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uAD00\uB828 \uC124\uC815\n autoHide?: AutoHideConfig; // \uC790\uB3D9 \uC228\uAE40 \uAD00\uB828 \uC124\uC815\n pullToRefresh?: PullToRefreshConfig; // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uAD00\uB828 \uC124\uC815(\uD130\uCE58 \uC804\uC6A9)\n\n // \uAE30\uD0C0 \uC124\uC815\uB4E4\n showScrollbar?: boolean; // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n showHorizontalScrollbar?: boolean; // \uD558\uB2E8(\uAC00\uB85C) \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true)\n detectInnerScroll?: boolean; // children \uB0B4\uBD80\uC758 \uC2A4\uD06C\uB864 \uC694\uC18C \uAC10\uC9C0 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: false, \uAC00\uC0C1 \uD14C\uC774\uBE14 \uB4F1\uC5D0 \uC0AC\uC6A9)\n scrollTopFab?: boolean | ScrollTopFabConfig; // \"\uB9E8 \uC704\uB85C\" FAB \uD45C\uC2DC(\uAE30\uBCF8\uAC12: false). true \uBA74 \uAE30\uBCF8 \uC124\uC815\uC73C\uB85C, \uAC1D\uCCB4\uBA74 \uC138\uBD80 \uC124\uC815.\n}\n\n// \"\uB9E8 \uC704\uB85C\" FAB \uC124\uC815\nexport interface ScrollTopFabConfig {\n threshold?: number; // \uC774 \uAC12(px) \uC774\uC0C1 \uB0B4\uB824\uAC00\uBA74 FAB \uB97C \uD45C\uC2DC\uD55C\uB2E4 (\uAE30\uBCF8\uAC12: 600)\n idleOpacity?: number; // \uD3C9\uC0C1\uC2DC FAB \uBD88\uD22C\uBA85\uB3C4 \u2014 \uB0B4\uC6A9\uC744 \uAC00\uB9AC\uC9C0 \uC54A\uAC8C \uBC18\uD22C\uBA85 \uC720\uC9C0, \uB204\uB97C \uB54C\uB9CC 1 (\uAE30\uBCF8\uAC12: 0.45)\n scrollingOpacity?: number; // @deprecated idleOpacity \uB85C \uB300\uCCB4(\uD558\uC704\uD638\uD658\uC6A9 \uBCC4\uCE6D)\n hideDelay?: number; // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 \uC774 \uC2DC\uAC04(ms) \uD6C4 FAB \uB97C \uC790\uB3D9\uC73C\uB85C \uC228\uAE34\uB2E4 (\uAE30\uBCF8\uAC12: 1500, 0 \uC774\uBA74 \uC790\uB3D9\uC228\uAE40 \uC5C6\uC74C)\n position?: \"left\" | \"right\"; // FAB \uB97C \uC5B4\uB290 \uCABD \uD558\uB2E8\uC5D0 \uB458\uC9C0 (\uAE30\uBCF8\uAC12: \"right\")\n offset?: number; // \uD654\uBA74 \uAC00\uC7A5\uC790\uB9AC\uB85C\uBD80\uD130\uC758 \uC5EC\uBC31(px) (\uAE30\uBCF8\uAC12: 16)\n offsetBottom?: number; // \uC544\uB798\uCABD \uC5EC\uBC31(px)\uB9CC \uB530\uB85C \uC904 \uB54C (\uAE30\uBCF8\uAC12: offset). \uD558\uB2E8 \uBC14 \uC704\uB85C \uB744\uC6B0\uBA74\uC11C \uC606 \uC5EC\uBC31\uC740 \uADF8\uB300\uB85C \uB458 \uB54C \uC4F4\uB2E4.\n background?: string; // FAB \uBC30\uACBD\uC0C9 (\uAE30\uBCF8\uAC12: \"#1976d2\" \u2014 MUI \uAE30\uBCF8 primary)\n color?: string; // FAB \uC544\uC774\uCF58 \uC0C9 (\uAE30\uBCF8\uAC12: \"#ffffff\")\n size?: number; // FAB \uC9C0\uB984(px) (\uAE30\uBCF8\uAC12: 48)\n}\n\n// OverlayScrollbar\uAC00 \uB178\uCD9C\uD560 \uBA54\uC11C\uB4DC\uB4E4\nexport interface OverlayScrollbarRef {\n getScrollContainer: () => HTMLDivElement | null;\n scrollTo: (options: ScrollToOptions) => void;\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n}\n\n// \uAE30\uBCF8 \uC124\uC815 \uAC1D\uCCB4\uB4E4\uC744 \uCEF4\uD3EC\uB10C\uD2B8 \uC678\uBD80\uC5D0 \uC0C1\uC218\uB85C \uC120\uC5B8 (\uC7AC\uB80C\uB354\uB9C1 \uC2DC \uB3D9\uC77C\uD55C \uCC38\uC870 \uC720\uC9C0)\nconst DEFAULT_THUMB_CONFIG: ThumbConfig = {};\nconst DEFAULT_TRACK_CONFIG: TrackConfig = {};\nconst DEFAULT_ARROWS_CONFIG: ArrowsConfig = {};\nconst DEFAULT_DRAG_SCROLL_CONFIG: DragScrollConfig = {};\nconst DEFAULT_AUTO_HIDE_CONFIG: AutoHideConfig = {};\nconst DEFAULT_PULL_TO_REFRESH_CONFIG: PullToRefreshConfig = {};\n\nconst OverlayScrollbar = forwardRef<OverlayScrollbarRef, OverlayScrollbarProps>(\n (\n {\n className = \"\",\n style = {},\n containerStyle = {},\n contentStyle = {},\n children,\n onScroll,\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\n thumb = DEFAULT_THUMB_CONFIG,\n track = DEFAULT_TRACK_CONFIG,\n arrows = DEFAULT_ARROWS_CONFIG,\n dragScroll = DEFAULT_DRAG_SCROLL_CONFIG,\n autoHide = DEFAULT_AUTO_HIDE_CONFIG,\n pullToRefresh = DEFAULT_PULL_TO_REFRESH_CONFIG,\n\n // \uAE30\uD0C0 \uC124\uC815\uB4E4 (showScrollbar/showHorizontalScrollbar \uB294 \uD130\uCE58 \uAE30\uAE30\uC5D0\uC11C \uC544\uB798 \uD30C\uC0DD\uAC12\uC73C\uB85C \uAC15\uC81C off)\n showScrollbar: showScrollbarProp = true,\n showHorizontalScrollbar: showHorizontalScrollbarProp = true,\n detectInnerScroll = false,\n scrollTopFab = false,\n },\n ref,\n ) => {\n // \uD130\uCE58 \uC6B0\uC120 \uAE30\uAE30(\uBAA8\uBC14\uC77C/\uD0DC\uBE14\uB9BF) \uD310\uC815. \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uBC14\uB97C CSS \uB85C \uAC15\uC81C\uD574\uB3C4 \uBB34\uC2DC\uD558\uB294 \uC6F9\uBDF0\uAC00 \uC788\uC5B4,\n // \uD130\uCE58\uC5D0\uC11C\uB3C4 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774 \uC138\uB85C \uC2A4\uD06C\uB864\uBC14(\uC790\uCCB4 \uD2B8\uB799/thumb)\uB97C \uD45C\uC2DC\uD55C\uB2E4(\uD56D\uC0C1 \uBCF4\uC784 \uBCF4\uC7A5).\n // \uB2E4\uB9CC \u2460 \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14\uB294 \uB044\uACE0(\uD558\uB2E8 \uD68C\uC0C9\uC120 \uBC29\uC9C0) \u2461 \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC740 \uB048\uB2E4(\uC544\uB798 finalDragScrollConfig)\n // \u2014 \uD130\uCE58 \uC81C\uC2A4\uCC98\uAC00 \uCEE4\uC2A4\uD140 \uB4DC\uB798\uADF8 \uB85C\uC9C1\uC5D0 \uAC00\uB85C\uCC44\uC774\uC9C0 \uC54A\uAC8C.\n const isCoarsePointer = useCoarsePointer();\n // \uC138\uB85C \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14\uB294 \uC18C\uBE44\uCC98 \uAC12 \uADF8\uB300\uB85C(\uD130\uCE58\uC5D0\uC11C\uB3C4 \uD45C\uC2DC). \uAC00\uB85C\uB9CC \uD130\uCE58\uC5D0\uC11C \uAC15\uC81C off.\n const showScrollbar = showScrollbarProp;\n const showHorizontalScrollbar = isCoarsePointer ? false : showHorizontalScrollbarProp;\n\n // \uC778\uC2A4\uD134\uC2A4 \uACE0\uC720 \uD074\uB798\uC2A4 \u2014 \uC2A4\uD06C\uB864\uBC14 \uC228\uAE40/\uD45C\uC2DC CSS \uB97C \uC774 \uC778\uC2A4\uD134\uC2A4 \uCEE8\uD14C\uC774\uB108\uC5D0\uB9CC \uC801\uC6A9\uD574\n // \uC804\uC5ED `.overlay-scrollbar-container` \uC120\uD0DD\uC790\uAC00 \uD398\uC774\uC9C0\uC758 \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAE4C\uC9C0 \uC624\uC5FC\uC2DC\uD0A4\uC9C0 \uC54A\uAC8C \uD55C\uB2E4.\n // (fine/coarse \uD310\uC815\uC774 \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC11E\uC5EC\uB3C4 \uC11C\uB85C \uC2A4\uD06C\uB864\uBC14\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4.)\n const rawInstanceId = useId();\n const instanceClass = `os-${rawInstanceId.replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n // \"\uB9E8 \uC704\uB85C\" FAB \uC124\uC815\uC744 \uC815\uADDC\uD654\uD55C\uB2E4(boolean \uC774\uBA74 \uAE30\uBCF8\uAC12, \uAC1D\uCCB4\uBA74 \uBCD1\uD569).\n const fabEnabled = scrollTopFab !== false && scrollTopFab != null;\n const fabConfig = typeof scrollTopFab === \"object\" ? scrollTopFab : {};\n const fabThreshold = fabConfig.threshold ?? 600;\n const fabIdleOpacity = fabConfig.idleOpacity ?? fabConfig.scrollingOpacity ?? 0.45;\n const fabHideDelay = fabConfig.hideDelay ?? 1500;\n const fabPosition = fabConfig.position ?? \"right\";\n const fabOffset = fabConfig.offset ?? 16;\n // \uC544\uB798\uCABD \uC5EC\uBC31\uC740 \uB530\uB85C \uBC1B\uC744 \uC218 \uC788\uB2E4 \u2014 \uD558\uB2E8 \uB0B4\uBE44\uAC8C\uC774\uC158 \uBC14\uAC00 \uC788\uB294 \uD654\uBA74\uC5D0\uC11C \uC606 \uC5EC\uBC31\uC740 \uADF8\uB300\uB85C \uB450\uACE0 \uC704\uB85C\uB9CC \uC62C\uB9B0\uB2E4.\n const fabOffsetBottom = fabConfig.offsetBottom ?? fabOffset;\n const fabBackground = fabConfig.background ?? \"#1976d2\";\n const fabColor = fabConfig.color ?? \"#ffffff\";\n const fabSize = fabConfig.size ?? 48;\n // FAB \uB178\uCD9C \uC5EC\uBD80 \u2014 \uCEE8\uD14C\uC774\uB108 scrollTop \uC774 threshold \uB97C \uB118\uC73C\uBA74 \uD45C\uC2DC\uD55C\uB2E4.\n const [showScrollTopFab, setShowScrollTopFab] = useState(false);\n // FAB \uB97C \uB204\uB974\uACE0 \uC788\uB294 \uB3D9\uC548\uB9CC \uC120\uBA85\uD558\uAC8C \uBCF4\uC5EC\uC8FC\uAE30 \uC704\uD55C \uC0C1\uD0DC(\uADF8 \uC678\uC5D0\uB294 \uBC18\uD22C\uBA85 \uC720\uC9C0).\n const [isFabPressed, setIsFabPressed] = useState(false);\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 FAB \uB97C \uC790\uB3D9\uC73C\uB85C \uC228\uAE30\uB294 \uD0C0\uC774\uBA38(\uC2A4\uD06C\uB864\uB9C8\uB2E4 \uB9AC\uC14B).\n const fabHideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // props \uBCC0\uACBD \uCD94\uC801\uC6A9 ref\n const prevPropsRef = useRef<{\n children?: ReactNode;\n onScroll?: (event: Event) => void;\n showScrollbar?: boolean;\n showHorizontalScrollbar?: boolean;\n thumb?: ThumbConfig;\n track?: TrackConfig;\n arrows?: ArrowsConfig;\n dragScroll?: DragScrollConfig;\n autoHide?: AutoHideConfig;\n }>({});\n\n // \uB80C\uB354\uB9C1 \uC2DC \uC5B4\uB5A4 prop\uC774 \uBCC0\uACBD\uB418\uC5C8\uB294\uC9C0 \uCCB4\uD06C\n useEffect(() => {\n // \uD604\uC7AC props \uC800\uC7A5\n prevPropsRef.current = {\n children,\n onScroll,\n showScrollbar,\n showHorizontalScrollbar,\n thumb,\n track,\n arrows,\n dragScroll,\n autoHide,\n };\n });\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const scrollbarRef = useRef<HTMLDivElement>(null);\n const thumbRef = useRef<HTMLDivElement>(null);\n\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 \uCE90\uC2F1\uC6A9 ref (\uC131\uB2A5 \uCD5C\uC801\uD654)\n const cachedScrollContainerRef = useRef<HTMLElement | null>(null);\n\n // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \u2014 \uCEE8\uD14C\uC774\uB108\uAC00 \uB9E8 \uC704\uC77C \uB54C \uD558\uD5A5 \uD130\uCE58 \uB2F9\uAE40\uC744 \uAC10\uC9C0\uD574 onRefresh \uB97C \uC2E4\uD589\uD55C\uB2E4.\n const {\n pullDistance,\n isRefreshing,\n pullProgress,\n } = usePullToRefresh({ containerRef, config: pullToRefresh });\n const pullIndicatorColor = pullToRefresh.indicatorColor ?? \"#1976d2\";\n const pullIndicatorVisible = pullDistance > 0 || isRefreshing;\n\n // \uAE30\uBCF8 \uC0C1\uD0DC\uB4E4\n const [scrollbarVisible, setScrollbarVisible] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isThumbHovered, setIsThumbHovered] = useState(false);\n const [dragStart, setDragStart] = useState({ y: 0, scrollTop: 0 });\n const [thumbHeight, setThumbHeight] = useState(0);\n const [thumbTop, setThumbTop] = useState(0);\n const [hasScrollableContent, setHasScrollableContent] = useState(false);\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uC0C1\uD0DC\n const [isHorizontalThumbHovered, setIsHorizontalThumbHovered] =\n useState(false);\n const [isDraggingHorizontal, setIsDraggingHorizontal] = useState(false);\n const [dragStartHorizontal, setDragStartHorizontal] = useState({\n x: 0,\n scrollLeft: 0,\n });\n const [thumbWidth, setThumbWidth] = useState(0);\n const [thumbLeft, setThumbLeft] = useState(0);\n const [\n hasHorizontalScrollableContent,\n setHasHorizontalScrollableContent,\n ] = useState(false);\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC0C1\uD0DC\n const isDragScrollPendingRef = useRef(false);\n const [isDragScrolling, setIsDragScrolling] = useState(false);\n // \uB05D\uB0BC \uB54C\uC758 \uD310\uC815\uC6A9 \u2014 \uC9E7\uAC8C \uB04C\uACE0 \uBC14\uB85C \uB5BC\uBA74 mouseup \uC774 \uB9AC\uB80C\uB354\uBCF4\uB2E4 \uBA3C\uC800 \uC640 state \uB294 \uC544\uC9C1 false \uB77C,\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB97C \uAC78\uC9C0 \uC54A\uACE0 \uB05D\uB098 \uC2A4\uD06C\uB864\uBC14\uAC00 \uB0A8\uC558\uB2E4.\n const isDragScrollingRef = useRef(false);\n const [dragScrollStart, setDragScrollStart] = useState({\n x: 0,\n y: 0,\n scrollTop: 0,\n scrollLeft: 0,\n });\n const [activeArrow, setActiveArrow] = useState<\"up\" | \"down\" | null>(\n null,\n );\n const [hoveredArrow, setHoveredArrow] = useState<\"up\" | \"down\" | null>(\n null,\n );\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC2DC hover \uBC29\uC9C0\uC6A9\n const [isInitialized, setIsInitialized] = useState(false);\n\n // wrapper\uC758 padding-bottom \uC800\uC7A5\n const [wrapperPaddingBottom, setWrapperPaddingBottom] = useState(0);\n\n // \uD720 \uC2A4\uD06C\uB864 \uAC10\uC9C0\uC6A9\n const wheelTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n // state \uAC00 \uC544\uB2C8\uB77C ref \uB2E4 \u2014 state \uB85C \uB450\uBA74 \uAC12\uC774 \uBC14\uB014 \uB54C\uB9C8\uB2E4 \uC2A4\uD06C\uB864 \uB9AC\uC2A4\uB108 effect \uAC00 \uB2E4\uC2DC \uB3CC\uBA74\uC11C\n // \uBC29\uAE08 \uAC74 \uD720 \uD0C0\uC774\uBA38\uB97C \uC9C0\uC6CC, \uD720 \uD55C \uCE78\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14\uAC00 \uC548 \uB728\uACE0 \"\uD720 \uC911\" \uC0C1\uD0DC\uB3C4 \uD480\uB9AC\uC9C0 \uC54A\uC558\uB2E4.\n const isWheelScrollingRef = useRef(false);\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\n const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null,\n );\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 (\uB514\uBC14\uC6B4\uC2A4\uC6A9)\n const hoverEnterTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uD45C\uC2DC \uD0C0\uC774\uBA38 (\uB514\uBC14\uC6B4\uC2A4\uC6A9)\n const wheelShowTimeoutRef = useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n\n // \uADF8\uB8F9\uD654\uB41C \uC124\uC815 \uAC1D\uCCB4\uB4E4\uC5D0 \uAE30\uBCF8\uAC12 \uC124\uC815\n const finalThumbConfig = useMemo(() => {\n const baseColor = thumb.color ?? \"#606060\";\n return {\n width: thumb.width ?? 8,\n minHeight: thumb.minHeight ?? 50,\n radius: thumb.radius ?? (thumb.width ?? 8) / 2,\n color: baseColor,\n opacity: thumb.opacity ?? 0.6,\n hoverColor: thumb.hoverColor ?? baseColor,\n hoverOpacity: thumb.hoverOpacity ?? 1.0,\n };\n }, [thumb]);\n\n const finalTrackConfig = useMemo(\n () => ({\n width: track.width ?? 16,\n color: track.color ?? \"rgba(128, 128, 128, 0.1)\",\n visible: track.visible ?? true,\n alignment: track.alignment ?? \"outside\",\n radius: track.radius ?? finalThumbConfig.radius ?? 4,\n margin: track.margin ?? 4,\n overflowX: track.overflowX ?? true,\n overflowY: track.overflowY ?? true,\n }),\n [track, finalThumbConfig.radius],\n );\n\n const finalArrowsConfig = useMemo(() => {\n const baseColor = arrows.color ?? \"#808080\";\n return {\n visible: arrows.visible ?? false,\n step: arrows.step ?? 50,\n color: baseColor,\n opacity: arrows.opacity ?? 0.6,\n hoverColor: arrows.hoverColor ?? baseColor,\n hoverOpacity: arrows.hoverOpacity ?? 1.0,\n };\n }, [arrows]);\n\n const finalDragScrollConfig = useMemo(\n () => ({\n // \uD130\uCE58 \uAE30\uAE30\uB294 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uC774 \uC790\uC5F0\uC2A4\uB7EC\uC6B0\uBBC0\uB85C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uB048\uB2E4(\uC18C\uBE44\uCC98 prop \uBB34\uAD00).\n enabled: isCoarsePointer ? false : dragScroll.enabled ?? true,\n excludeClasses: dragScroll.excludeClasses ?? [],\n excludeSelectors: dragScroll.excludeSelectors ?? [],\n }),\n [dragScroll, isCoarsePointer],\n );\n\n const finalAutoHideConfig = useMemo(\n () => ({\n enabled: autoHide.enabled ?? true,\n delay: autoHide.delay ?? 1500,\n delayOnWheel: autoHide.delayOnWheel ?? 700,\n initialDelay: autoHide.initialDelay ?? 200,\n }),\n [autoHide],\n );\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC2DC \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uC9C0\uC5F0 \uC0C1\uD0DC\n const [isInitialDelayActive, setIsInitialDelayActive] = useState(\n () => (autoHide.initialDelay ?? 200) > 0,\n );\n\n // \uD638\uD658\uC131\uC744 \uC704\uD55C \uBCC0\uC218\uB4E4 (\uC790\uC8FC \uC0AC\uC6A9\uB418\uB294 \uBCC0\uC218\uB4E4\uB9CC \uC720\uC9C0)\n const finalThumbWidth = finalThumbConfig.width;\n const finalTrackWidth = finalTrackConfig.width;\n const thumbMinHeight = finalThumbConfig.minHeight;\n const showArrows = finalArrowsConfig.visible;\n const arrowStep = finalArrowsConfig.step;\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 \uD568\uC218 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n const maintainFocus = useCallback(() => {\n if (!containerRef.current) return;\n\n // \uD604\uC7AC \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C \uD655\uC778\n const activeElement = document.activeElement;\n\n // \uC624\uBC84\uB808\uC774 \uC2A4\uD06C\uB864\uBC14 \uB0B4\uBD80\uC5D0 \uC774\uBBF8 \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C\uAC00 \uC788\uC73C\uBA74 \uC2A4\uD0B5\n if (\n activeElement &&\n containerRef.current.contains(activeElement) &&\n activeElement !== containerRef.current\n ) {\n return;\n }\n\n // \uD3EC\uCEE4\uC2A4\uB41C \uC694\uC18C\uAC00 \uC5C6\uAC70\uB098 \uC678\uBD80\uC5D0 \uC788\uC73C\uBA74 \uCEE8\uD14C\uC774\uB108\uC5D0 \uD3EC\uCEE4\uC2A4\n containerRef.current.focus();\n }, []);\n\n // ref\uB97C \uD1B5\uD574 \uC678\uBD80\uC5D0\uC11C \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC5D0 \uC811\uADFC\uD560 \uC218 \uC788\uB3C4\uB85D \uD568\n useImperativeHandle(\n ref,\n () => ({\n getScrollContainer: () => containerRef.current,\n scrollTo: (options: ScrollToOptions) => {\n if (containerRef.current) {\n containerRef.current.scrollTo(options);\n }\n },\n get scrollTop() {\n return containerRef.current?.scrollTop || 0;\n },\n get scrollHeight() {\n return containerRef.current?.scrollHeight || 0;\n },\n get clientHeight() {\n return containerRef.current?.clientHeight || 0;\n },\n }),\n [],\n );\n\n // \uC2E4\uC81C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30 (\uCE90\uC2F1 \uCD5C\uC801\uD654)\n const findScrollableElement = useCallback((): HTMLElement | null => {\n // \uCE90\uC2DC\uB41C \uC694\uC18C\uAC00 \uC5EC\uC804\uD788 \uC720\uD6A8\uD55C\uC9C0 \uD655\uC778\n if (cachedScrollContainerRef.current) {\n const cached = cachedScrollContainerRef.current;\n // DOM\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uACE0 \uC5EC\uC804\uD788 \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C\uC9C0 \uD655\uC778\n if (\n document.contains(cached) &&\n (cached.scrollHeight > cached.clientHeight + 2 ||\n cached.scrollWidth > cached.clientWidth + 2)\n ) {\n return cached;\n }\n // \uCE90\uC2DC \uBB34\uD6A8\uD654\n cachedScrollContainerRef.current = null;\n }\n\n if (!containerRef.current) {\n return null;\n }\n\n // \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uC758 \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC5EC\uBD80 \uD655\uC778\n if (\n containerRef.current.scrollHeight >\n containerRef.current.clientHeight + 2 ||\n containerRef.current.scrollWidth >\n containerRef.current.clientWidth + 2\n ) {\n cachedScrollContainerRef.current = containerRef.current;\n return containerRef.current;\n }\n\n // detectInnerScroll \uC635\uC158\uC774 \uD65C\uC131\uD654\uB41C \uACBD\uC6B0\uC5D0\uB9CC children \uB0B4\uBD80\uC758 \uC2A4\uD06C\uB864 \uC694\uC18C \uCC3E\uAE30\n // (\uAC00\uC0C1 \uD14C\uC774\uBE14 \uB4F1 \uB0B4\uBD80\uC5D0\uC11C \uC2A4\uD06C\uB864\uC744 \uCC98\uB9AC\uD558\uB294 \uACBD\uC6B0\uC5D0 \uC0AC\uC6A9)\n if (!detectInnerScroll) {\n return null;\n }\n\n // children \uC694\uC18C\uC5D0\uC11C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30\n // \uC911\uCCA9\uB41C OverlayScrollbar\uC758 \uC601\uC5ED\uC740 \uC81C\uC678 (\uB2E4\uB978 OverlayScrollbar\uC758 container\uB294 \uC2A4\uD0B5)\n const childScrollableElements =\n containerRef.current.querySelectorAll(\n '[data-virtuoso-scroller], [style*=\"overflow\"], .virtuoso-scroller, [style*=\"overflow: auto\"], [style*=\"overflow:auto\"]',\n );\n\n for (const child of childScrollableElements) {\n const element = child as HTMLElement;\n\n // \uC774 \uC694\uC18C\uAC00 \uB2E4\uB978 OverlayScrollbar\uC758 container\uC778\uC9C0 \uD655\uC778\n // (\uC790\uC2E0\uC758 containerRef\uB294 \uC544\uB2C8\uC5B4\uC57C \uD558\uACE0, overlay-scrollbar-container \uD074\uB798\uC2A4\uB97C \uAC00\uC9C4 \uACBD\uC6B0)\n if (\n element !== containerRef.current &&\n element.classList.contains(\"overlay-scrollbar-container\")\n ) {\n // \uC911\uCCA9\uB41C OverlayScrollbar\uC758 container\uC774\uBBC0\uB85C \uC2A4\uD0B5\n continue;\n }\n\n // \uC774 \uC694\uC18C\uC758 \uBD80\uBAA8 \uC911\uC5D0 \uB2E4\uB978 OverlayScrollbar container\uAC00 \uC788\uB294\uC9C0 \uD655\uC778\n let parent: HTMLElement | null = element.parentElement;\n let isNestedInAnotherScrollbar = false;\n\n while (parent && parent !== containerRef.current) {\n if (\n parent.classList.contains(\n \"overlay-scrollbar-container\",\n ) &&\n parent !== containerRef.current\n ) {\n // \uB2E4\uB978 OverlayScrollbar \uB0B4\uBD80\uC758 \uC694\uC18C\uC774\uBBC0\uB85C \uC2A4\uD0B5\n isNestedInAnotherScrollbar = true;\n break;\n }\n parent = parent.parentElement;\n }\n\n if (isNestedInAnotherScrollbar) {\n continue;\n }\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C\uC778\uC9C0 \uD655\uC778\n if (\n element.scrollHeight > element.clientHeight + 2 ||\n element.scrollWidth > element.clientWidth + 2\n ) {\n cachedScrollContainerRef.current = element;\n return element;\n }\n }\n\n return null;\n }, []);\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC5EC\uBD80 \uCCB4\uD06C\n const isScrollable = useCallback(() => {\n return findScrollableElement() !== null;\n }, [findScrollableElement]);\n\n // \uD0C0\uC774\uBA38 \uC815\uB9AC\n const clearHideTimer = useCallback(() => {\n if (hideTimeoutRef.current) {\n clearTimeout(hideTimeoutRef.current);\n hideTimeoutRef.current = null;\n }\n }, []);\n\n const clearHoverEnterTimer = useCallback(() => {\n if (hoverEnterTimeoutRef.current) {\n clearTimeout(hoverEnterTimeoutRef.current);\n hoverEnterTimeoutRef.current = null;\n }\n }, []);\n\n const clearWheelShowTimer = useCallback(() => {\n if (wheelShowTimeoutRef.current) {\n clearTimeout(wheelShowTimeoutRef.current);\n wheelShowTimeoutRef.current = null;\n }\n }, []);\n\n // \uC2A4\uD06C\uB864 \uC601\uC5ED \uBC14\uAE65\uC73C\uB85C \uB098\uAC00\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uC989\uC2DC \uC228\uAE34\uB2E4.\n const handleWrapperMouseLeave = useCallback(() => {\n clearHoverEnterTimer();\n\n if (\n isDragging ||\n isDraggingHorizontal ||\n isDragScrolling ||\n isDragScrollPendingRef.current\n ) {\n return;\n }\n\n clearHideTimer();\n setScrollbarVisible(false);\n }, [\n clearHideTimer,\n clearHoverEnterTimer,\n isDragging,\n isDraggingHorizontal,\n isDragScrolling,\n ]);\n\n // \uC2A4\uD06C\uB864\uBC14 \uC228\uAE30\uAE30 \uD0C0\uC774\uBA38\n const setHideTimer = useCallback(\n (delay: number) => {\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uC73C\uBA74 \uD0C0\uC774\uBA38\uB97C \uC124\uC815\uD558\uC9C0 \uC54A\uC74C\n if (!finalAutoHideConfig.enabled) {\n return;\n }\n clearHideTimer();\n hideTimeoutRef.current = setTimeout(() => {\n setScrollbarVisible(false);\n hideTimeoutRef.current = null;\n }, delay);\n },\n [clearHideTimer, finalAutoHideConfig.enabled],\n );\n\n // \uC2A4\uD06C\uB864\uBC14 \uC704\uCE58 \uBC0F \uD06C\uAE30 \uC5C5\uB370\uC774\uD2B8\n const updateScrollbar = useCallback(() => {\n // \uC6B0\uC120 \uAC10\uC9C0 \uB85C\uC9C1\uC744 \uC0AC\uC6A9\uD558\uACE0, \uC2E4\uD328 \uC2DC container\uB97C fallback\uC73C\uB85C \uC0AC\uC6A9\uD55C\uB2E4.\n // horizontal-only \uCF00\uC774\uC2A4\uC5D0\uC11C \uAC10\uC9C0 \uD0C0\uC774\uBC0D \uC774\uC288\uAC00 \uBC1C\uC0DD\uD574\uB3C4 \uAC00\uB85C \uC624\uBC84\uD50C\uB85C\uC6B0 \uACC4\uC0B0\uC744 \uC720\uC9C0\uD55C\uB2E4.\n const scrollableElement =\n findScrollableElement() ?? containerRef.current;\n if (!scrollableElement) {\n // \uCE21\uC815 \uB300\uC0C1\uC774 \uC5C6\uC73C\uBA74 \uC228\uAE40\n setScrollbarVisible(false);\n setHasScrollableContent(false);\n setHasHorizontalScrollableContent(false);\n clearHideTimer();\n return;\n }\n\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uACE0 \uCD08\uAE30 \uC9C0\uC5F0\uC774 \uB05D\uB0AC\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uD56D\uC0C1 \uD45C\uC2DC\n if (!finalAutoHideConfig.enabled && !isInitialDelayActive) {\n setScrollbarVisible(true);\n clearHideTimer();\n }\n\n const containerHeight = scrollableElement.clientHeight;\n const contentHeight = scrollableElement.scrollHeight;\n const scrollTop = scrollableElement.scrollTop;\n\n const containerWidth = scrollableElement.clientWidth;\n const contentWidth = scrollableElement.scrollWidth;\n const scrollLeft = scrollableElement.scrollLeft;\n const hasVerticalOverflow = contentHeight - containerHeight > 0;\n const hasHorizontalOverflow = contentWidth - containerWidth > 0;\n const hasAnyOverflow = hasVerticalOverflow || hasHorizontalOverflow;\n\n // \uCD95\uBCC4 \uC624\uBC84\uD50C\uB85C\uC6B0 \uC0C1\uD0DC \uBC18\uC601\n setHasScrollableContent(hasVerticalOverflow);\n setHasHorizontalScrollableContent(hasHorizontalOverflow);\n\n // \uC624\uBC84\uD50C\uB85C\uC6B0\uAC00 \uC5C6\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uC228\uAE34\uB2E4.\n if (!hasAnyOverflow) {\n setScrollbarVisible(false);\n clearHideTimer();\n }\n\n // wrapper\uC758 \uD328\uB529 \uACC4\uC0B0 (\uC0C1\uD558 \uD328\uB529\uB9CC \uD544\uC694)\n let wrapperPaddingTopBottom = 0;\n let paddingBottom = 0;\n if (wrapperRef.current) {\n const computedStyle = window.getComputedStyle(\n wrapperRef.current,\n );\n const paddingTop = parseFloat(computedStyle.paddingTop) || 0;\n paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;\n wrapperPaddingTopBottom = paddingTop + paddingBottom;\n setWrapperPaddingBottom(paddingBottom);\n }\n\n if (hasVerticalOverflow) {\n // \uD654\uC0B4\uD45C\uC640 \uAC04\uACA9 \uACF5\uAC04 \uACC4\uC0B0 (\uD654\uC0B4\uD45C + \uC704\uC544\uB798 \uB9C8\uC9C4, \uD654\uC0B4\uD45C \uC5C6\uC5B4\uB3C4 \uC704\uC544\uB798 \uB9C8\uC9C4)\n const arrowSpace = showArrows\n ? finalThumbWidth * 2 + finalTrackConfig.margin * 4\n : finalTrackConfig.margin * 2;\n\n // \uC378 \uB192\uC774 \uACC4\uC0B0 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uCD5C\uC18C \uB192\uC774 \uC0AC\uC6A9, \uD654\uC0B4\uD45C \uACF5\uAC04 \uC81C\uC678, wrapper \uD328\uB529 \uCD94\uAC00)\n const availableHeight =\n containerHeight - arrowSpace + wrapperPaddingTopBottom;\n const scrollRatio = containerHeight / contentHeight;\n const calculatedThumbHeight = Math.max(\n availableHeight * scrollRatio,\n thumbMinHeight,\n );\n\n // \uC378 \uC704\uCE58 \uACC4\uC0B0 (\uD654\uC0B4\uD45C\uC640 \uAC04\uACA9 \uACF5\uAC04 \uC81C\uC678)\n const scrollableHeight = contentHeight - containerHeight;\n const thumbScrollableHeight =\n availableHeight - calculatedThumbHeight;\n const calculatedThumbTop =\n scrollableHeight > 0\n ? (scrollTop / scrollableHeight) * thumbScrollableHeight\n : 0;\n\n setThumbHeight(calculatedThumbHeight);\n setThumbTop(calculatedThumbTop);\n } else {\n setThumbHeight(0);\n setThumbTop(0);\n }\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uACC4\uC0B0\n const horizontalScrollableWidth = contentWidth - containerWidth;\n if (horizontalScrollableWidth > 0) {\n const scrollRatioHorizontal = containerWidth / contentWidth;\n const calculatedThumbWidth = Math.max(\n containerWidth * scrollRatioHorizontal,\n 50, // \uCD5C\uC18C \uB108\uBE44\n );\n\n const thumbScrollableWidth =\n containerWidth - calculatedThumbWidth;\n const calculatedThumbLeft =\n horizontalScrollableWidth > 0\n ? (scrollLeft / horizontalScrollableWidth) *\n thumbScrollableWidth\n : 0;\n\n setThumbWidth(calculatedThumbWidth);\n setThumbLeft(calculatedThumbLeft);\n } else {\n setThumbWidth(0);\n setThumbLeft(0);\n }\n }, [\n findScrollableElement,\n clearHideTimer,\n showArrows,\n finalThumbWidth,\n thumbMinHeight,\n finalAutoHideConfig.enabled,\n isInitialDelayActive,\n ]);\n\n // \uC378 \uB4DC\uB798\uADF8 \uC2DC\uC791\n const handleThumbMouseDown = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n setIsDragging(true);\n setDragStart({\n y: event.clientY,\n scrollTop: actualScrollContainer.scrollTop,\n });\n\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [findScrollableElement, clearHideTimer, maintainFocus],\n );\n\n // \uC378 \uB4DC\uB798\uADF8 \uC911\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!isDragging) return;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerHeight = actualScrollContainer.clientHeight;\n const contentHeight = actualScrollContainer.scrollHeight;\n const scrollableHeight = contentHeight - containerHeight;\n\n const deltaY = event.clientY - dragStart.y;\n const thumbScrollableHeight = containerHeight - thumbHeight;\n const scrollDelta =\n (deltaY / thumbScrollableHeight) * scrollableHeight;\n\n const newScrollTop = Math.max(\n 0,\n Math.min(\n scrollableHeight,\n dragStart.scrollTop + scrollDelta,\n ),\n );\n\n actualScrollContainer.scrollTop = newScrollTop;\n updateScrollbar();\n },\n [\n isDragging,\n dragStart,\n thumbHeight,\n updateScrollbar,\n findScrollableElement,\n ],\n );\n\n // \uC378 \uB4DC\uB798\uADF8 \uC885\uB8CC\n const handleMouseUp = useCallback(() => {\n setIsDragging(false);\n if (isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay); // \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uD2B8\uB799 \uD074\uB9AD\uC73C\uB85C \uC2A4\uD06C\uB864 \uC810\uD504\n const handleTrackClick = useCallback(\n (event: React.MouseEvent) => {\n if (!scrollbarRef.current) {\n return;\n }\n\n const scrollbar = scrollbarRef.current;\n const rect = scrollbar.getBoundingClientRect();\n const clickY = event.clientY - rect.top;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerHeight = actualScrollContainer.clientHeight;\n const contentHeight = actualScrollContainer.scrollHeight;\n\n const scrollRatio = clickY / containerHeight;\n const newScrollTop =\n scrollRatio * (contentHeight - containerHeight);\n\n actualScrollContainer.scrollTop = Math.max(\n 0,\n Math.min(contentHeight - containerHeight, newScrollTop),\n );\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n finalAutoHideConfig.delay,\n findScrollableElement,\n maintainFocus,\n ],\n );\n\n // \uC704\uCABD \uD654\uC0B4\uD45C \uD074\uB9AD \uD578\uB4E4\uB7EC\n const handleUpArrowClick = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n if (!containerRef.current) return;\n\n const newScrollTop = Math.max(\n 0,\n containerRef.current.scrollTop - arrowStep,\n );\n\n containerRef.current.scrollTop = newScrollTop;\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n arrowStep,\n finalAutoHideConfig.delay,\n maintainFocus,\n ],\n );\n\n // \uC544\uB798\uCABD \uD654\uC0B4\uD45C \uD074\uB9AD \uD578\uB4E4\uB7EC\n const handleDownArrowClick = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n if (!containerRef.current || !contentRef.current) return;\n\n const container = containerRef.current;\n const content = contentRef.current;\n const maxScrollTop =\n content.scrollHeight - container.clientHeight;\n const newScrollTop = Math.min(\n maxScrollTop,\n container.scrollTop + arrowStep,\n );\n\n container.scrollTop = newScrollTop;\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n arrowStep,\n finalAutoHideConfig.delay,\n maintainFocus,\n ],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC2DC\uC791\n const handleHorizontalThumbMouseDown = useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n setIsDraggingHorizontal(true);\n setDragStartHorizontal({\n x: event.clientX,\n scrollLeft: actualScrollContainer.scrollLeft,\n });\n\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [findScrollableElement, clearHideTimer, maintainFocus],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC911\n const handleHorizontalMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!isDraggingHorizontal) return;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerWidth = actualScrollContainer.clientWidth;\n const contentWidth = actualScrollContainer.scrollWidth;\n const scrollableWidth = contentWidth - containerWidth;\n\n const deltaX = event.clientX - dragStartHorizontal.x;\n const thumbScrollableWidth = containerWidth - thumbWidth;\n const scrollDelta =\n (deltaX / thumbScrollableWidth) * scrollableWidth;\n\n const newScrollLeft = Math.max(\n 0,\n Math.min(\n scrollableWidth,\n dragStartHorizontal.scrollLeft + scrollDelta,\n ),\n );\n\n actualScrollContainer.scrollLeft = newScrollLeft;\n updateScrollbar();\n },\n [\n isDraggingHorizontal,\n dragStartHorizontal,\n thumbWidth,\n updateScrollbar,\n findScrollableElement,\n ],\n );\n\n // \uAC00\uB85C \uC378 \uB4DC\uB798\uADF8 \uC885\uB8CC\n const handleHorizontalMouseUp = useCallback(() => {\n setIsDraggingHorizontal(false);\n if (isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay); // \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uAC00\uB85C \uD2B8\uB799 \uD074\uB9AD\uC73C\uB85C \uC2A4\uD06C\uB864 \uC810\uD504\n const handleHorizontalTrackClick = useCallback(\n (event: React.MouseEvent) => {\n if (!scrollbarRef.current) {\n return;\n }\n\n const scrollbar = scrollbarRef.current;\n const rect = scrollbar.getBoundingClientRect();\n const clickX = event.clientX - rect.left;\n\n const actualScrollContainer = findScrollableElement();\n if (!actualScrollContainer) {\n return;\n }\n\n const containerWidth = actualScrollContainer.clientWidth;\n const contentWidth = actualScrollContainer.scrollWidth;\n\n const scrollRatio = clickX / containerWidth;\n const newScrollLeft =\n scrollRatio * (contentWidth - containerWidth);\n\n actualScrollContainer.scrollLeft = Math.max(\n 0,\n Math.min(contentWidth - containerWidth, newScrollLeft),\n );\n updateScrollbar();\n\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n\n // \uD3EC\uCEE4\uC2A4 \uC720\uC9C0 (\uD0A4\uBCF4\uB4DC \uC785\uB825\uC774 \uACC4\uC18D \uC791\uB3D9\uD558\uB3C4\uB85D)\n maintainFocus();\n },\n [\n updateScrollbar,\n setHideTimer,\n finalAutoHideConfig.delay,\n findScrollableElement,\n maintainFocus,\n ],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC2DC\uC791\n const handleDragScrollStart = useCallback(\n (event: React.MouseEvent) => {\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uBE44\uD65C\uC131\uD654\uB41C \uACBD\uC6B0\n if (!finalDragScrollConfig.enabled) return;\n\n // \uB354 \uAC00\uAE4C\uC6B4(\uC548\uCABD) OverlayScrollbar \uC778\uC2A4\uD134\uC2A4\uAC00 \uC774\uBBF8 \uC774 press \uB85C \uB4DC\uB798\uADF8\uB97C\n // \uC2DC\uC791\uD588\uC73C\uBA74(\uC2A4\uD06C\uB864 \uAC00\uB2A5 \u2192 preventDefault \uD638\uCD9C) \uBC14\uAE65 \uC778\uC2A4\uD134\uC2A4\uB294 \uC591\uBCF4\uD55C\uB2E4.\n // \uC548\uCABD \uC778\uC2A4\uD134\uC2A4\uAC00 \uC2A4\uD06C\uB864\uD560 \uAC8C \uC5C6\uC73C\uBA74 preventDefault \uC5C6\uC774 \uBC18\uD658\uD558\uBBC0\uB85C\n // \uC790\uC5F0\uC2A4\uB7FD\uAC8C \uBC14\uAE65 \uC778\uC2A4\uD134\uC2A4\uB85C \uB4DC\uB798\uADF8\uAC00 \uC804\uD30C\uB41C\uB2E4.\n if (event.defaultPrevented) return;\n\n // \uD14D\uC2A4\uD2B8 \uC785\uB825 \uC694\uC18C\uB098 \uC81C\uC678 \uB300\uC0C1\uC774\uBA74 \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uD558\uC9C0 \uC54A\uC74C\n const target = event.target as Element;\n\n // React \uD3EC\uD138\uB85C \uB80C\uB354\uB41C \uC790\uC2DD(\uC911\uCCA9 \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB4F1)\uC758 press \uB294 React \uD2B8\uB9AC\uB85C\n // \uBC84\uBE14\uB9C1\uB418\uC5B4 \uC774 \uD578\uB4E4\uB7EC\uAE4C\uC9C0 \uB3C4\uB2EC\uD558\uC9C0\uB9CC DOM \uC0C1\uC73C\uB85C\uB294 \uC774 \uCEE8\uD14C\uC774\uB108 \uBC16\uC774\uB2E4.\n // \uC2E4\uC81C DOM \uCEE8\uD14C\uC774\uB108 \uB0B4\uBD80 press \uB9CC \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uB300\uC0C1\uC73C\uB85C \uC0BC\uB294\uB2E4.\n // (\uD3EC\uD138 \uC704 \uB2E4\uC774\uC5BC\uB85C\uADF8\uC5D0 \uC2A4\uD06C\uB864\uC774 \uC5C6\uC5B4\uB3C4 \uC544\uB798 \uB2E4\uC774\uC5BC\uB85C\uADF8\uB85C \uC804\uD30C\uD558\uC9C0 \uC54A\uB294\uB2E4)\n const container = containerRef.current;\n if (!container || !container.contains(target)) return;\n\n if (isTextInputElement(target, finalDragScrollConfig)) {\n return;\n }\n\n // \uC624\uB978\uCABD \uD074\uB9AD\uC774\uB098 \uD720 \uD074\uB9AD\uC740 \uC81C\uC678\n if (event.button !== 0) return;\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n // \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC601\uC5ED\uC774 \uC544\uB2C8\uBA74 \uC81C\uC678\n if (\n scrollableElement.scrollHeight <=\n scrollableElement.clientHeight &&\n scrollableElement.scrollWidth <=\n scrollableElement.clientWidth\n )\n return;\n\n event.preventDefault();\n // preventDefault \uB294 \uBE0C\uB77C\uC6B0\uC800\uC758 \"\uBE48 \uACF3\uC744 \uB204\uB974\uBA74 \uD3EC\uCEE4\uC2A4 \uD574\uC81C\" \uB3C4 \uB9C9\uB294\uB2E4 \u2014 \uC785\uB825\uCE78\uC5D0 \uD3EC\uCEE4\uC2A4\uAC00\n // \uB0A8\uC544 \uBE48 \uACF3\uC744 \uB20C\uB7EC\uB3C4 \uD3EC\uCEE4\uC2A4 \uD14C\uB450\uB9AC\uAC00 \uD480\uB9AC\uC9C0 \uC54A\uC558\uB2E4. \uB204\uB978 \uACF3 \uBC16\uC5D0 \uC788\uB294 \uD3EC\uCEE4\uC2A4\uB294 \uC9C1\uC811 \uD47C\uB2E4.\n const active = document.activeElement;\n if (\n active instanceof HTMLElement &&\n active !== document.body &&\n !active.contains(target)\n ) {\n active.blur();\n }\n isDragScrollPendingRef.current = true;\n setDragScrollStart({\n x: event.clientX,\n y: event.clientY,\n scrollTop: scrollableElement.scrollTop,\n scrollLeft: scrollableElement.scrollLeft || 0,\n });\n\n // \uC2A4\uD06C\uB864\uBC14\uB294 \uC2E4\uC81C \uB4DC\uB798\uADF8\uAC00 \uBC1C\uC0DD\uD560 \uB54C \uD45C\uC2DC (handleDragScrollMove\uC5D0\uC11C \uCC98\uB9AC)\n // \u26A0\uFE0F \uC5EC\uAE30\uC11C \uC228\uAE40 \uD0C0\uC774\uBA38\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uB204\uB974\uAE30\uB9CC \uD558\uACE0 \uB04C\uC9C0 \uC54A\uC73C\uBA74(\uADF8\uB0E5 \uD074\uB9AD) \uB2E4\uC2DC \uAC78\uC5B4 \uC8FC\uB294 \uACF3\uC774\n // \uC5C6\uC5B4, \uC2A4\uD06C\uB864 \uC9C1\uD6C4 \uBCF8\uBB38\uC744 \uD074\uB9AD\uD558\uBA74 \uC2A4\uD06C\uB864\uBC14\uAC00 \uC0AC\uB77C\uC9C0\uC9C0 \uC54A\uACE0 \uACC4\uC18D \uB0A8\uC558\uB2E4.\n },\n [finalDragScrollConfig, isTextInputElement, findScrollableElement],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC911\n const handleDragScrollMove = useCallback(\n (event: MouseEvent) => {\n if (!isDragScrollPendingRef.current && !isDragScrolling) return;\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n const deltaX = dragScrollStart.x - event.clientX;\n const deltaY = dragScrollStart.y - event.clientY;\n\n // 5px \uC774\uC0C1 \uC6C0\uC9C1\uC600\uC744 \uB54C\uB9CC \uC2E4\uC81C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC2DC\uC791\uD55C\uB2E4.\n if (Math.abs(deltaY) < 5 && Math.abs(deltaX) < 5) {\n return;\n }\n\n if (isDragScrollPendingRef.current) {\n isDragScrollPendingRef.current = false;\n isDragScrollingRef.current = true;\n setIsDragScrolling(true);\n }\n\n // \uC2E4\uC81C \uB4DC\uB798\uADF8\uAC00 \uBC1C\uC0DD\uD588\uC73C\uBBC0\uB85C \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n setScrollbarVisible(true);\n\n // \uC138\uB85C \uC2A4\uD06C\uB864 \uCC98\uB9AC\n const newScrollTop = Math.max(\n 0,\n Math.min(\n scrollableElement.scrollHeight -\n scrollableElement.clientHeight,\n dragScrollStart.scrollTop + deltaY,\n ),\n );\n\n // \uAC00\uB85C \uC2A4\uD06C\uB864 \uCC98\uB9AC\n const newScrollLeft = Math.max(\n 0,\n Math.min(\n scrollableElement.scrollWidth -\n scrollableElement.clientWidth,\n dragScrollStart.scrollLeft + deltaX,\n ),\n );\n\n scrollableElement.scrollTop = newScrollTop;\n scrollableElement.scrollLeft = newScrollLeft;\n updateScrollbar();\n },\n [\n isDragScrolling,\n dragScrollStart,\n findScrollableElement,\n updateScrollbar,\n ],\n );\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC885\uB8CC\n const handleDragScrollEnd = useCallback(() => {\n const wasDragScrolling = isDragScrollingRef.current;\n // \uC2E4\uC81C\uB85C \uB04C\uC5B4 \uC2A4\uD06C\uB864\uD588\uC73C\uBA74 \uC190\uC744 \uB5C0 \uC790\uB9AC\uC758 \uD074\uB9AD\uC744 \uD55C \uBC88 \uC0BC\uD0A8\uB2E4(1.7.36) \u2014 mouseup \uB4A4 \uBE0C\uB77C\uC6B0\uC800\uAC00 click \uC744 \uC3F4\n // \uB04C\uAE30\uB97C \uC2DC\uC791\uD55C \uC904(\uBA54\uC77C \uBAA9\uB85D\uC758 \uD55C \uD1B5 \uB4F1)\uC774 \uC5F4\uB838\uB2E4. 1.7.35 \uB294 \uC774 \uCEF4\uD3EC\uB10C\uD2B8\uAC00 \uC4F0\uC9C0 \uC54A\uB294 hooks/useDragScroll \uC5D0\uB9CC \uB123\uC5B4 \uB2FF\uC9C0 \uC54A\uC558\uB2E4.\n // \uD074\uB9AD\uC774 \uC548 \uC624\uB294 \uACBD\uC6B0(\uB2E4\uB978 \uC694\uC18C \uC704\uC5D0\uC11C \uB5CC)\uB97C \uC704\uD574 \uB2E4\uC74C \uCC28\uB840\uC5D0 \uD47C\uB2E4 \u2014 click \uC740 mouseup \uACFC \uAC19\uC740 \uCC28\uB840\uC5D0 \uC640\uC11C \uADF8 \uC804\uC5D0 \uC7A1\uD78C\uB2E4.\n if (wasDragScrolling) {\n const swallow = (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n window.removeEventListener(\"click\", swallow, true);\n };\n window.addEventListener(\"click\", swallow, true);\n window.setTimeout(() => window.removeEventListener(\"click\", swallow, true), 0);\n }\n isDragScrollPendingRef.current = false;\n isDragScrollingRef.current = false;\n setIsDragScrolling(false);\n\n if (wasDragScrolling && isScrollable()) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }, [isScrollable, setHideTimer, finalAutoHideConfig.delay]);\n\n // \uC2A4\uD06C\uB864 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 (externalScrollContainer \uC6B0\uC120 \uC0AC\uC6A9)\n useEffect(() => {\n const handleScroll = (event: Event) => {\n updateScrollbar();\n\n // \"\uB9E8 \uC704\uB85C\" FAB \uB178\uCD9C \uC5EC\uBD80 \uAC31\uC2E0(\uD65C\uC131 \uC2DC\uC5D0\uB9CC).\n // \uC784\uACC4\uAC12\uC744 \uB118\uC73C\uBA74 **\uBC18\uD22C\uBA85 \uC0C1\uD0DC\uB85C \uACC4\uC18D \uD45C\uC2DC**\uD558\uACE0, \uC2A4\uD06C\uB864\uC774 \uBA48\uCD94\uBA74 fabHideDelay \uD6C4 \uD398\uC774\uB4DC\uC544\uC6C3\uD55C\uB2E4.\n // \uC120\uBA85\uD574\uC9C0\uB294 \uAC74 \uB20C\uB800\uC744 \uB54C(isFabPressed)\uBFD0\uC774\uB2E4 \u2014 \uBA48\uCD9C \uB54C\uB9C8\uB2E4 \uC120\uBA85\u2194\uBC18\uD22C\uBA85\uC774 \uC624\uAC00\uBA74 \uAE5C\uBE61\uC784\uC774 \uC2EC\uD558\uB2E4.\n // \uBE0C\uB77C\uC6B0\uC800\uC5D0 \"\uC2A4\uD06C\uB864 \uC885\uB8CC\" \uC774\uBCA4\uD2B8\uAC00 \uC5C6\uC5B4 \uC815\uC9C0 \uD310\uC815\uC740 \uB9C8\uC9C0\uB9C9 \uC2A4\uD06C\uB864 \uAE30\uC900 \uD0C0\uC774\uBA38\uB85C \uD55C\uB2E4.\n if (fabEnabled) {\n const top = (event.target as HTMLElement | null)?.scrollTop ?? 0;\n const shouldShow = top > fabThreshold;\n setShowScrollTopFab(shouldShow);\n if (fabHideTimerRef.current) {\n clearTimeout(fabHideTimerRef.current);\n fabHideTimerRef.current = null;\n }\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCD98 \uB4A4 fabHideDelay \uD6C4 \uC790\uB3D9\uC73C\uB85C \uC228\uAE34\uB2E4(\uC2A4\uD06C\uB864\uB9C8\uB2E4 \uB9AC\uC14B).\n if (shouldShow && fabHideDelay > 0) {\n fabHideTimerRef.current = setTimeout(() => {\n fabHideTimerRef.current = null;\n setShowScrollTopFab(false);\n }, fabHideDelay);\n }\n }\n\n // \uCD08\uAE30 \uC9C0\uC5F0 \uC911\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC74C\n if (isInitialDelayActive) {\n if (onScroll) {\n onScroll(event);\n }\n return;\n }\n\n // \uC2A4\uD06C\uB864 \uC911\uC5D0\uB294 \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n clearHideTimer();\n setScrollbarVisible(true);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uB514\uBC14\uC6B4\uC2A4 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC2E4\uC81C \uC2A4\uD06C\uB864 \uBC1C\uC0DD \uC2DC)\n clearWheelShowTimer();\n\n // \uD720 \uC2A4\uD06C\uB864 \uC911\uC774\uBA74 \uBE60\uB978 \uC228\uAE40, \uC544\uB2C8\uBA74 \uAE30\uBCF8 \uC228\uAE40 \uC2DC\uAC04 \uC801\uC6A9\n const delay = isWheelScrollingRef.current\n ? finalAutoHideConfig.delayOnWheel\n : finalAutoHideConfig.delay;\n setHideTimer(delay);\n\n if (onScroll) {\n onScroll(event);\n }\n };\n\n const handleWheel = () => {\n // \uD720 \uC2A4\uD06C\uB864 \uC0C1\uD0DC \uD45C\uC2DC\n isWheelScrollingRef.current = true;\n\n // \uAE30\uC874 \uD720 \uD0C0\uC774\uBA38 \uC81C\uAC70\n if (wheelTimeoutRef.current) {\n clearTimeout(wheelTimeoutRef.current);\n }\n\n // 300ms \uD6C4 \uD720 \uC2A4\uD06C\uB864 \uC0C1\uD0DC \uD574\uC81C (\uD720 \uC2A4\uD06C\uB864\uC774 \uB05D\uB0AC\uB2E4\uACE0 \uAC04\uC8FC)\n wheelTimeoutRef.current = setTimeout(() => {\n isWheelScrollingRef.current = false;\n }, 300);\n\n // \uD720 \uC774\uBCA4\uD2B8 \uC2DC 50ms \uB514\uBC14\uC6B4\uC2A4 \uC801\uC6A9 (\uC2E4\uC81C \uC2A4\uD06C\uB864 \uBC1C\uC0DD \uC2DC handleScroll\uC5D0\uC11C \uCDE8\uC18C\uB428)\n clearWheelShowTimer();\n wheelShowTimeoutRef.current = setTimeout(() => {\n setScrollbarVisible(true);\n wheelShowTimeoutRef.current = null;\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC \uD6C4 \uC790\uB3D9 \uC228\uAE40 \uD0C0\uC774\uBA38 \uC124\uC815\n setHideTimer(finalAutoHideConfig.delayOnWheel);\n }, 50);\n };\n\n const elementsToWatch: HTMLElement[] = [];\n\n // \uC2E4\uC81C \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC694\uC18C \uCC3E\uAE30\n const scrollableElement = findScrollableElement();\n if (scrollableElement) {\n elementsToWatch.push(scrollableElement);\n }\n\n // fallback: \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uC640 children \uC694\uC18C\uB3C4 \uAC10\uC9C0\n const container = containerRef.current;\n if (container && !scrollableElement) {\n elementsToWatch.push(container);\n\n // children \uC694\uC18C\uB4E4\uC758 \uC2A4\uD06C\uB864\uB3C4 \uAC10\uC9C0 (\uC911\uCCA9\uB41C OverlayScrollbar \uC81C\uC678)\n const childScrollableElements = container.querySelectorAll(\n '[data-virtuoso-scroller], [style*=\"overflow\"], .virtuoso-scroller, [style*=\"overflow: auto\"], [style*=\"overflow:auto\"]',\n );\n childScrollableElements.forEach((child) => {\n const element = child as HTMLElement;\n\n // \uB2E4\uB978 OverlayScrollbar\uC758 container\uB294 \uC81C\uC678\n if (\n element !== container &&\n element.classList.contains(\n \"overlay-scrollbar-container\",\n )\n ) {\n return;\n }\n\n // \uBD80\uBAA8 \uC911\uC5D0 \uB2E4\uB978 OverlayScrollbar container\uAC00 \uC788\uC73C\uBA74 \uC81C\uC678\n let parent: HTMLElement | null = element.parentElement;\n while (parent && parent !== container) {\n if (\n parent.classList.contains(\n \"overlay-scrollbar-container\",\n ) &&\n parent !== container\n ) {\n return; // \uC911\uCCA9\uB41C OverlayScrollbar \uB0B4\uBD80\uC774\uBBC0\uB85C \uC81C\uC678\n }\n parent = parent.parentElement;\n }\n\n elementsToWatch.push(element);\n });\n }\n\n // \uBAA8\uB4E0 \uC694\uC18C\uC5D0 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 \uB4F1\uB85D\n elementsToWatch.forEach((element) => {\n element.addEventListener(\"scroll\", handleScroll, {\n passive: true,\n });\n element.addEventListener(\"wheel\", handleWheel, {\n passive: true,\n });\n });\n\n return () => {\n // \uBAA8\uB4E0 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 \uC81C\uAC70\n elementsToWatch.forEach((element) => {\n element.removeEventListener(\"scroll\", handleScroll);\n element.removeEventListener(\"wheel\", handleWheel);\n });\n };\n }, [\n findScrollableElement,\n updateScrollbar,\n onScroll,\n clearHideTimer,\n setHideTimer,\n finalAutoHideConfig,\n isInitialDelayActive,\n fabEnabled,\n fabThreshold,\n fabHideDelay,\n ]);\n\n // \uC5B8\uB9C8\uC6B4\uD2B8 \uC2DC FAB \uC790\uB3D9\uC228\uAE40\u00B7\uD720 \uD0C0\uC774\uBA38\uB97C \uC815\uB9AC\uD55C\uB2E4.\n // \uD720 \uD0C0\uC774\uBA38\uB97C \uB9AC\uC2A4\uB108 effect \uC815\uB9AC\uC5D0\uC11C \uC9C0\uC6B0\uC9C0 \uC54A\uB294 \uC774\uC720: \uADF8 effect \uB294 onScroll \uAC19\uC740 prop \uC774 \uBC14\uB014 \uB54C\uB9C8\uB2E4\n // \uB2E4\uC2DC \uB3C4\uB294\uB370, \uADF8\uB54C\uB9C8\uB2E4 \uC9C0\uC6B0\uBA74 \uAC78\uC5B4 \uB454 \uD45C\uC2DC\u00B7\"\uD720 \uC911\" \uD574\uC81C\uAC00 \uC0AC\uB77C\uC9C4\uB2E4.\n useEffect(\n () => () => {\n if (fabHideTimerRef.current) {\n clearTimeout(fabHideTimerRef.current);\n }\n if (wheelTimeoutRef.current) {\n clearTimeout(wheelTimeoutRef.current);\n }\n if (wheelShowTimeoutRef.current) {\n clearTimeout(wheelShowTimeoutRef.current);\n }\n },\n []\n );\n\n // \uD0A4\uBCF4\uB4DC \uB124\uBE44\uAC8C\uC774\uC158 \uD578\uB4E4\uB7EC (\uBC29\uD5A5\uD0A4, PageUp/PageDown/Home/End)\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const target = event.target;\n if (event.defaultPrevented) {\n return;\n }\n\n if (\n target instanceof Element &&\n isTextInputElement(target, finalDragScrollConfig)\n ) {\n return;\n }\n\n const scrollableElement = findScrollableElement();\n if (!scrollableElement) return;\n\n const { key } = event;\n const { scrollTop, scrollHeight, clientHeight } =\n scrollableElement;\n const maxScrollTop = scrollHeight - clientHeight;\n\n // \uD55C \uC904 \uC2A4\uD06C\uB864 \uB2E8\uC704 (rowHeight \uB610\uB294 \uAE30\uBCF8\uAC12)\n const lineScrollStep = 50;\n\n let newScrollTop: number | null = null;\n\n switch (key) {\n case \"ArrowUp\":\n event.preventDefault();\n newScrollTop = Math.max(0, scrollTop - lineScrollStep);\n break;\n case \"ArrowDown\":\n event.preventDefault();\n newScrollTop = Math.min(\n maxScrollTop,\n scrollTop + lineScrollStep,\n );\n break;\n case \"PageUp\":\n event.preventDefault();\n newScrollTop = Math.max(0, scrollTop - clientHeight);\n break;\n case \"PageDown\":\n event.preventDefault();\n newScrollTop = Math.min(\n maxScrollTop,\n scrollTop + clientHeight,\n );\n break;\n case \"Home\":\n event.preventDefault();\n newScrollTop = 0;\n break;\n case \"End\":\n event.preventDefault();\n newScrollTop = maxScrollTop;\n break;\n default:\n return;\n }\n\n if (newScrollTop !== null) {\n // \uC378 \uC704\uCE58\uB97C \uBA3C\uC800 \uC5C5\uB370\uC774\uD2B8\n const scrollRatio = newScrollTop / maxScrollTop;\n const arrowSpace = showArrows\n ? finalThumbWidth * 2 + finalTrackConfig.margin * 4\n : finalTrackConfig.margin * 2;\n const availableHeight = clientHeight - arrowSpace;\n const scrollableThumbHeight = availableHeight - thumbHeight;\n const newThumbTop = scrollableThumbHeight * scrollRatio;\n\n setThumbTop(newThumbTop);\n\n // \uC2A4\uD06C\uB864 \uC704\uCE58\uB97C \uC989\uC2DC \uBCC0\uACBD (\uC560\uB2C8\uBA54\uC774\uC158 \uC5C6\uC74C)\n scrollableElement.scrollTop = newScrollTop;\n\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC\n clearHideTimer();\n setScrollbarVisible(true);\n setHideTimer(finalAutoHideConfig.delay);\n }\n };\n\n const container = containerRef.current;\n if (container) {\n container.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n }\n }, [\n findScrollableElement,\n showArrows,\n finalThumbWidth,\n finalTrackConfig.margin,\n thumbHeight,\n clearHideTimer,\n setHideTimer,\n finalAutoHideConfig.delay,\n finalDragScrollConfig,\n ]);\n\n // \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864 \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n document.addEventListener(\"mousemove\", handleDragScrollMove);\n document.addEventListener(\"mouseup\", handleDragScrollEnd);\n return () => {\n document.removeEventListener(\"mousemove\", handleDragScrollMove);\n document.removeEventListener(\"mouseup\", handleDragScrollEnd);\n };\n }, [isDragScrolling, handleDragScrollMove, handleDragScrollEnd]);\n\n // \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n if (isDragging) {\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseup\", handleMouseUp);\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseup\", handleMouseUp);\n };\n }\n }, [isDragging, handleMouseMove, handleMouseUp]);\n\n // \uAC00\uB85C \uC804\uC5ED \uB9C8\uC6B0\uC2A4 \uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108\n useEffect(() => {\n if (isDraggingHorizontal) {\n document.addEventListener(\n \"mousemove\",\n handleHorizontalMouseMove,\n );\n document.addEventListener(\"mouseup\", handleHorizontalMouseUp);\n return () => {\n document.removeEventListener(\n \"mousemove\",\n handleHorizontalMouseMove,\n );\n document.removeEventListener(\n \"mouseup\",\n handleHorizontalMouseUp,\n );\n };\n }\n }, [\n isDraggingHorizontal,\n handleHorizontalMouseMove,\n handleHorizontalMouseUp,\n ]);\n\n // \uCD08\uAE30 \uC2A4\uD06C\uB864\uBC14 \uC5C5\uB370\uC774\uD2B8\n useEffect(() => {\n // \uC989\uC2DC \uC5C5\uB370\uC774\uD2B8\n updateScrollbar();\n // \uC57D\uAC04\uC758 \uC9C0\uC5F0 \uD6C4\uC5D0\uB3C4 \uC5C5\uB370\uC774\uD2B8 (DOM\uC774 \uC644\uC804\uD788 \uB80C\uB354\uB9C1\uB41C \uD6C4)\n const timer = setTimeout(() => {\n updateScrollbar();\n }, 100);\n return () => clearTimeout(timer);\n }, [updateScrollbar]);\n\n // \uCEF4\uD3EC\uB10C\uD2B8 \uCD08\uAE30\uD654 \uC644\uB8CC \uD45C\uC2DC (hover \uC774\uBCA4\uD2B8 \uD65C\uC131\uD654\uC6A9)\n useLayoutEffect(() => {\n setIsInitialized(true);\n // \uCD08\uAE30\uD654 \uC9C1\uD6C4 \uC2A4\uD06C\uB864\uBC14 \uC5C5\uB370\uC774\uD2B8 (\uC378 \uB192\uC774 \uC815\uD655\uD558\uAC8C \uACC4\uC0B0)\n updateScrollbar();\n // \uC790\uB3D9 \uC228\uAE40\uC774 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uACE0 \uCD08\uAE30 \uC9C0\uC5F0\uC774 \uB05D\uB0AC\uC73C\uBA74 \uC2A4\uD06C\uB864\uBC14\uB97C \uD56D\uC0C1 \uD45C\uC2DC\n if (\n !finalAutoHideConfig.enabled &&\n !isInitialDelayActive &&\n isScrollable()\n ) {\n setScrollbarVisible(true);\n }\n }, [\n isScrollable,\n updateScrollbar,\n finalAutoHideConfig.enabled,\n isInitialDelayActive,\n ]);\n\n // \uCD08\uAE30 \uB9C8\uC6B4\uD2B8 \uC9C0\uC5F0 \uD0C0\uC774\uBA38\n useEffect(() => {\n if (finalAutoHideConfig.initialDelay > 0) {\n const timer = setTimeout(() => {\n setIsInitialDelayActive(false);\n }, finalAutoHideConfig.initialDelay);\n return () => clearTimeout(timer);\n }\n }, [finalAutoHideConfig.initialDelay]);\n\n // \uD06C\uAE30 \uBCC0\uACBD \uC54C\uB9BC\uC740 \uD55C \uD654\uBA74 \uADF8\uB9BC\uC5D0 \uD55C \uBC88\uB9CC \uBC18\uC601\uD55C\uB2E4(1.7.31 \uBC30\uD3EC\uBCF8\uC5D0\uC11C \uC62E\uAE40 \u2014 \uC800\uC7A5\uC18C\uC5D0 \uBE60\uC838 \uC788\uC5C8\uB2E4).\n // ResizeObserver \uCF5C\uBC31 \uC548\uC5D0\uC11C \uACE7\uBC14\uB85C \uC7AC\uBA74 \"ResizeObserver loop\" \uACBD\uACE0\uC640 \uC5F0\uC18D \uC7AC\uACC4\uC0B0\uC774 \uC0DD\uAE34\uB2E4.\n const updateScrollbarRef = useRef(updateScrollbar);\n updateScrollbarRef.current = updateScrollbar;\n const resizeFrameRef = useRef<number | null>(null);\n const scheduleResizeUpdate = useCallback(() => {\n if (resizeFrameRef.current !== null) return;\n resizeFrameRef.current = requestAnimationFrame(() => {\n resizeFrameRef.current = null;\n updateScrollbarRef.current();\n });\n }, []);\n useEffect(\n () => () => {\n if (resizeFrameRef.current !== null) {\n cancelAnimationFrame(resizeFrameRef.current);\n resizeFrameRef.current = null;\n }\n },\n [],\n );\n\n // Resize observer\uB85C \uD06C\uAE30 \uBCC0\uACBD \uAC10\uC9C0\n useEffect(() => {\n const resizeObserver = new ResizeObserver(() => {\n scheduleResizeUpdate();\n });\n\n const elementsToObserve: HTMLElement[] = [];\n\n // \uB0B4\uBD80 \uCEE8\uD14C\uC774\uB108\uB4E4 \uAD00\uCC30\n if (containerRef.current) {\n elementsToObserve.push(containerRef.current);\n }\n if (contentRef.current) {\n elementsToObserve.push(contentRef.current);\n }\n\n // \uCE90\uC2DC\uB41C \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uB3C4 \uAD00\uCC30\n if (\n cachedScrollContainerRef.current &&\n document.contains(cachedScrollContainerRef.current)\n ) {\n elementsToObserve.push(cachedScrollContainerRef.current);\n }\n\n // \uBAA8\uB4E0 \uC694\uC18C\uB4E4 \uAD00\uCC30 \uC2DC\uC791\n elementsToObserve.forEach((element) => {\n resizeObserver.observe(element);\n });\n\n return () => resizeObserver.disconnect();\n }, [updateScrollbar, scheduleResizeUpdate]);\n\n // MutationObserver\uB85C DOM \uBCC0\uACBD \uAC10\uC9C0\n useEffect(() => {\n if (!containerRef.current) {\n return;\n }\n\n const observer = new MutationObserver(() => {\n // \uCE90\uC2DC \uCD08\uAE30\uD654\uD558\uC5EC \uC0C8\uB85C\uC6B4 \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 \uAC10\uC9C0\n cachedScrollContainerRef.current = null;\n updateScrollbar();\n });\n\n observer.observe(containerRef.current, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"style\"],\n });\n\n return () => observer.disconnect();\n }, [updateScrollbar]);\n\n // trackWidth\uAC00 thumbWidth\uBCF4\uB2E4 \uC791\uC73C\uBA74 thumbWidth\uC640 \uAC19\uAC8C \uC124\uC815\n const adjustedTrackWidth = Math.max(finalTrackWidth, finalThumbWidth);\n\n // PTR \uC2A4\uD53C\uB108 keyframes\u00B7editor-content \uC2A4\uD06C\uB864\uBC14\uB294 \uC785\uB825\uC7A5\uCE58\uC640 \uBB34\uAD00\uD558\uAC8C \uC804\uC5ED 1\uD68C\uB9CC \uC8FC\uC785\uD55C\uB2E4.\n useEffect(() => {\n const styleId = \"overlay-scrollbar-shared\";\n if (document.getElementById(styleId)) {\n return; // \uC774\uBBF8 \uC788\uC73C\uBA74 \uC7AC\uC8FC\uC785\uD558\uC9C0 \uC54A\uB294\uB2E4(\uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uB123\uC5B4\uB460).\n }\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n /* ehfuse-editor-content\uB294 \uC2A4\uD06C\uB864\uBC14 \uC720\uC9C0 */\n .overlay-scrollbar-container .ehfuse-editor-content {\n scrollbar-width: thin !important;\n -ms-overflow-style: auto !important;\n overflow: auto !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar {\n display: block !important;\n width: 8px !important;\n height: 8px !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-track {\n display: block !important;\n background: #f1f1f1 !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-thumb {\n display: block !important;\n background: #c1c1c1 !important;\n border-radius: 4px !important;\n }\n .overlay-scrollbar-container .ehfuse-editor-content::-webkit-scrollbar-thumb:hover {\n background: #a1a1a1 !important;\n }\n /* \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC2A4\uD53C\uB108 \uD68C\uC804 */\n @keyframes overlay-scrollbar-ptr-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `;\n document.head.appendChild(style);\n // \uACF5\uC720 keyframes \uB294 \uC81C\uAC70\uD558\uC9C0 \uC54A\uB294\uB2E4(\uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uAC00 \uACC4\uC18D \uCC38\uC870\uD560 \uC218 \uC788\uC74C).\n }, []);\n\n // \uC778\uC2A4\uD134\uC2A4\uBCC4 \uC2A4\uD06C\uB864\uBC14 CSS \uB97C \uC774 \uC778\uC2A4\uD134\uC2A4 \uCEE8\uD14C\uC774\uB108 \uD074\uB798\uC2A4(instanceClass)\uC5D0\uB9CC \uC8FC\uC785\uD55C\uB2E4.\n // \uC804\uC5ED `.overlay-scrollbar-container` \uC120\uD0DD\uC790\uB97C \uC4F0\uC9C0 \uC54A\uC73C\uBBC0\uB85C \uB2E4\uB978 \uC778\uC2A4\uD134\uC2A4\uC758 \uC2A4\uD06C\uB864\uBC14\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4.\n useEffect(() => {\n const styleId = `overlay-scrollbar-instance-${instanceClass}`;\n const existing = document.getElementById(styleId);\n if (existing) existing.remove();\n\n const style = document.createElement(\"style\");\n style.id = styleId;\n const sel = `.${instanceClass}`;\n // fine/coarse \uBAA8\uB450 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\uBC14\uB294 \uC228\uAE34\uB2E4 \u2014 \uD45C\uC2DC\uB418\uB294 \uC2A4\uD06C\uB864\uBC14\uB294 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774(\uC790\uCCB4 \uD2B8\uB799/thumb)\uBFD0.\n // (\uD130\uCE58\uC5D0\uC11C\uB3C4 \uCEE4\uC2A4\uD140 \uC624\uBC84\uB808\uC774\uB97C \uCF1C\uBBC0\uB85C \uB124\uC774\uD2F0\uBE0C\uB97C \uADF8\uB9B4 \uD544\uC694\uAC00 \uC5C6\uACE0, \uB458\uC774 \uACB9\uCE58\uBA74 \uC9C0\uC800\uBD84\uD558\uB2E4.)\n style.textContent = `\n ${sel} {\n scrollbar-width: none !important;\n -ms-overflow-style: none !important;\n }\n ${sel}::-webkit-scrollbar {\n display: none !important;\n width: 0 !important;\n height: 0 !important;\n }\n ${sel}::-webkit-scrollbar-track { display: none !important; }\n ${sel}::-webkit-scrollbar-thumb { display: none !important; }`;\n document.head.appendChild(style);\n\n return () => {\n document.getElementById(styleId)?.remove();\n };\n }, [instanceClass]);\n\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 padding-bottom \uC740 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uBE0C\uB77C\uC6B0\uC800\uAC00 \uBB34\uC2DC\uD558\uBBC0\uB85C,\n // container/content \uC5D0 \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uAC77\uC5B4\uB0B4 spacer \uB192\uC774\uB85C \uD569\uC0B0\uD55C\uB2E4.\n const { normalizedContainerStyle, normalizedContentStyle, bottomSpacerHeight } =\n useMemo(() => {\n const container = extractBottomPadding(containerStyle);\n const content = extractBottomPadding(contentStyle);\n return {\n normalizedContainerStyle: container.style,\n normalizedContentStyle: content.style,\n bottomSpacerHeight:\n container.spacerHeight + content.spacerHeight,\n };\n }, [containerStyle, contentStyle]);\n\n return (\n <div\n ref={wrapperRef}\n className={`overlay-scrollbar-wrapper ${className}`}\n onMouseLeave={handleWrapperMouseLeave}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n position: \"relative\",\n minHeight: 0, // shrink \uAC00\uB2A5\uD558\uB3C4\uB85D\n height: \"100%\", // \uBD80\uBAA8\uC758 \uC804\uCCB4 \uB192\uC774 \uC0AC\uC6A9\n flex: \"1 1 0%\", // \uAE30\uBCF8\uC801\uC73C\uB85C flex item\uC73C\uB85C \uB3D9\uC791\n overflow: \"visible\", // content \uADF8\uB9BC\uC790\uC640 outside track\uC774 \uC0C1\uC704 wrapper\uC5D0\uC11C \uC798\uB9AC\uC9C0 \uC54A\uB3C4\uB85D \uD5C8\uC6A9\n ...style, // \uC0AC\uC6A9\uC790\uAC00 flex\uB97C override \uD560 \uC218 \uC788\uB3C4\uB85D style\uC744 \uB4A4\uC5D0 \uBC30\uCE58\n }}\n >\n {/* \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC778\uB514\uCF00\uC774\uD130 \u2014 \uB2F9\uAE40 \uAC70\uB9AC\uB97C \uB530\uB77C \uB0B4\uB824\uC624\uACE0, \uC2E4\uD589 \uC911\uC5D4 \uC2A4\uD53C\uB108\uB85C \uD68C\uC804 */}\n {pullIndicatorVisible && (\n <div\n className=\"overlay-scrollbar-pull-indicator\"\n style={{\n position: \"absolute\",\n top: 0,\n left: \"50%\",\n transform: `translate(-50%, ${pullDistance - 68}px)`,\n transition: isRefreshing\n ? \"transform 0.15s ease-out\"\n : pullDistance === 0\n ? \"transform 0.2s ease-out\"\n : \"none\",\n width: 60,\n height: 60,\n borderRadius: \"50%\",\n background: \"#ffffff\",\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.25)\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n zIndex: 1001,\n pointerEvents: \"none\",\n opacity: isRefreshing ? 1 : Math.min(pullProgress * 1.5, 1),\n }}\n >\n {isRefreshing ? (\n // \uC0C8\uB85C\uACE0\uCE68 \uC2E4\uD589 \uC911 \u2014 \uC2A4\uD53C\uB108\n <svg\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 24 24\"\n style={{\n animation:\n \"overlay-scrollbar-ptr-spin 0.8s linear infinite\",\n }}\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n fill=\"none\"\n stroke={pullIndicatorColor}\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeDasharray=\"42\"\n strokeDashoffset=\"14\"\n />\n </svg>\n ) : (\n // \uB2F9\uAE30\uB294 \uC911 \u2014 \uC9C4\uD589\uB960\uC5D0 \uB530\uB77C \uD68C\uC804\uD558\uB294 \uC0C8\uB85C\uACE0\uCE68 \uD654\uC0B4\uD45C\n <svg\n width=\"36\"\n height=\"36\"\n viewBox=\"0 0 24 24\"\n style={{\n transform: `rotate(${pullProgress * 270}deg)`,\n opacity: pullProgress >= 1 ? 1 : 0.6,\n }}\n >\n <path\n d=\"M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"\n fill={pullIndicatorColor}\n />\n </svg>\n )}\n </div>\n )}\n\n {/* \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 */}\n <div\n ref={containerRef}\n // \uACF5\uC6A9 \uD074\uB798\uC2A4 + \uC778\uC2A4\uD134\uC2A4 \uACE0\uC720 \uD074\uB798\uC2A4. \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC/\uC228\uAE40 CSS \uB294 \uC778\uC2A4\uD134\uC2A4 \uD074\uB798\uC2A4\uC5D0\uB9CC \uAC78\uB9B0\uB2E4.\n className={`overlay-scrollbar-container ${instanceClass}`}\n tabIndex={-1} // \uD0A4\uBCF4\uB4DC \uD3EC\uCEE4\uC2A4 \uAC00\uB2A5\uD558\uAC8C \uD568\n onMouseDown={handleDragScrollStart}\n style={{\n display: \"flex\",\n // \u26A0\uFE0F flexDirection \uC744 column \uC73C\uB85C \uBC14\uAFB8\uC9C0 \uC54A\uB294\uB2E4.\n // row \uB77C\uC11C content \uC758 flex \uB294 **\uAC00\uB85C**\uB9CC \uC81C\uC5B4\uD55C\uB2E4. column \uC73C\uB85C \uBC14\uAFB8\uBA74 \uADF8 flex \uAC00\n // \uC138\uB85C\uB97C \uC7A1\uC544 \uCF58\uD150\uCE20 \uB192\uC774\uAC00 \uCEE8\uD14C\uC774\uB108\uC5D0 \uACE0\uC815\uB418\uACE0 scrollHeight == clientHeight \uAC00 \uB418\uC5B4\n // \uC2A4\uD06C\uB864\uC774 \uC8FD\uB294\uB2E4. (\uD558\uB2E8 spacer \uB294 content \uC548 \uB9C8\uC9C0\uB9C9 \uC790\uC2DD\uC73C\uB85C \uBC30\uCE58\uD55C\uB2E4)\n width: \"100%\", // \uBA85\uC2DC\uC801 \uB108\uBE44 \uC124\uC815\n flex: \"1 1 auto\", // flex item\uC73C\uB85C \uC124\uC815\uD558\uC5EC \uB192\uC774\uB97C \uC790\uB3D9\uC73C\uB85C \uACC4\uC0B0\n minHeight: 0, // \uCD5C\uC18C \uB192\uC774 \uBCF4\uC7A5\n overflow: \"auto\", // \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864 \uAE30\uB2A5 \uC720\uC9C0\n // \uC2A4\uD06C\uB864\uBC14 \uD45C\uC2DC/\uC228\uAE40\uC740 \uC778\uC2A4\uD134\uC2A4 \uD074\uB798\uC2A4 CSS(useEffect)\uAC00 \uB2F4\uB2F9\uD55C\uB2E4(\uC778\uB77C\uC778\uC73C\uB85C \uAC15\uC81C\uD558\uC9C0 \uC54A\uC74C).\n // \uD0A4\uBCF4\uB4DC \uD3EC\uCEE4\uC2A4 \uC2A4\uD0C0\uC77C (\uC811\uADFC\uC131)\n outline: \"none\", // \uAE30\uBCF8 \uC544\uC6C3\uB77C\uC778 \uC81C\uAC70\n userSelect: isDragScrolling ? \"none\" : \"auto\", // \uC2E4\uC81C \uB4DC\uB798\uADF8 \uC911 \uD14D\uC2A4\uD2B8 \uC120\uD0DD \uBC29\uC9C0\n ...normalizedContainerStyle, // \uC0AC\uC6A9\uC790 \uC815\uC758 \uC2A4\uD0C0\uC77C(\uD558\uB2E8 padding \uC740 spacer \uB85C \uBD84\uB9AC)\n }}\n >\n <div\n ref={contentRef}\n className=\"overlay-scrollbar-content\"\n style={{\n // \u26A0\uFE0F basis \uB294 auto(\uB0B4\uC6A9 \uAE30\uC900). \"1 1 0%\" \uB85C \uB450\uBA74 basis 0 \uC774\uB77C \uC548\uCABD flex:1 \uC790\uC2DD\uC774\n // \uB0A8\uB294 \uACF5\uAC04\uC744 \uB2E4 \uBA39\uC5B4 \uD558\uB2E8 spacer \uB97C \uBC00\uC5B4\uB0B4\uACE0, \uCF58\uD150\uCE20\uAC00 \uB118\uCCD0\uB3C4 \uB192\uC774\uAC00 \uC548 \uB298\uC5B4\uB09C\uB2E4.\n flex: \"1 1 auto\", // grow \uD558\uB418 \uB0B4\uC6A9 \uB192\uC774\uB97C \uAE30\uC900\uC73C\uB85C \uC7A1\uB294\uB2E4\n display: \"flex\", // flex \uCEE8\uD14C\uC774\uB108\uB85C \uC124\uC815\n flexDirection: \"column\", // \uC138\uB85C \uBC29\uD5A5 \uC815\uB82C\n boxSizing: \"border-box\",\n ...normalizedContentStyle, // \uC0AC\uC6A9\uC790 \uC815\uC758 \uC2A4\uD0C0\uC77C(\uD558\uB2E8 padding \uC740 spacer \uB85C \uBD84\uB9AC)\n }}\n >\n {children}\n {bottomSpacerHeight > 0 && (\n // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 padding-bottom \uC740 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uBB34\uC2DC\uB418\uBBC0\uB85C,\n // \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uC2E4\uC81C \uB192\uC774\uB97C \uAC00\uC9C4 spacer \uB85C \uB300\uCCB4\uD55C\uB2E4.\n // content \uB294 column flex \uB77C \uC774 \uC790\uC2DD\uC774 \uB9C8\uC9C0\uB9C9 \uC904\uB85C \uC313\uC5EC \uC2A4\uD06C\uB864 \uC601\uC5ED\uC744 \uB298\uB9B0\uB2E4.\n <div\n className=\"overlay-scrollbar-bottom-spacer\"\n aria-hidden=\"true\"\n style={{ flex: \"0 0 auto\", height: bottomSpacerHeight }}\n />\n )}\n </div>\n </div>\n\n {/* \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 */}\n {showScrollbar &&\n hasScrollableContent &&\n (finalTrackConfig.overflowY ?? true) && (\n <div\n ref={scrollbarRef}\n className=\"overlay-scrollbar-track\"\n onMouseEnter={() => {\n // \uD130\uCE58\uC758 \uD0ED\uC740 mouseenter \uB9CC \uD749\uB0B4 \uB0B4\uACE0 mouseleave \uB294 \uB2E4\uB978 \uACF3\uC744 \uB204\uB97C \uB54C\uAE4C\uC9C0 \uC624\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uD638\uBC84\uB85C \uBD99\uC7A1\uC73C\uBA74 \uC624\uB978\uCABD \uB05D\uC744 \uD55C \uBC88 \uB20C\uB800\uC744 \uB54C \uC2A4\uD06C\uB864\uBC14\uAC00 \uACC4\uC18D \uB0A8\uB294\uB2E4.\n if (isCoarsePointer) return;\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB294 \uC989\uC2DC \uCDE8\uC18C\n clearHideTimer();\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uC124\uC815 (100ms \uD6C4 \uD45C\uC2DC)\n hoverEnterTimeoutRef.current = setTimeout(\n () => {\n setScrollbarVisible(true);\n hoverEnterTimeoutRef.current = null;\n },\n 100,\n );\n }}\n onMouseLeave={() => {\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC9C0\uB098\uAC00\uAE30\uB9CC \uD55C \uACBD\uC6B0)\n clearHoverEnterTimer();\n\n if (!isDragging) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }}\n style={{\n position: \"absolute\",\n top: 0,\n right: 0,\n width: `${adjustedTrackWidth}px`,\n height: \"100%\",\n opacity: scrollbarVisible ? 1 : 0,\n transition: \"opacity 0.2s ease-in-out\",\n cursor: \"pointer\",\n zIndex: 1000,\n pointerEvents: \"auto\",\n }}\n >\n {/* \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799 \uBC30\uACBD */}\n {finalTrackConfig.visible && (\n <div\n className=\"overlay-scrollbar-track-background\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n handleTrackClick(e);\n }}\n style={{\n position: \"absolute\",\n top: showArrows\n ? `${\n finalThumbConfig.width +\n finalTrackConfig.margin * 2\n }px`\n : `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment ===\n \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbConfig.width) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbConfig.width}px`,\n height: showArrows\n ? `calc(100% - ${\n finalThumbConfig.width * 2 +\n finalTrackConfig.margin * 4\n }px)`\n : `calc(100% - ${\n finalTrackConfig.margin * 2\n }px)`,\n backgroundColor: finalTrackConfig.color,\n borderRadius: `${finalTrackConfig.radius}px`,\n cursor: \"pointer\",\n }}\n />\n )}\n\n {/* \uC2A4\uD06C\uB864\uBC14 \uC378 */}\n <div\n ref={thumbRef}\n className=\"overlay-scrollbar-thumb\"\n onMouseDown={handleThumbMouseDown}\n onMouseEnter={() => setIsThumbHovered(true)}\n onMouseLeave={() => setIsThumbHovered(false)}\n style={{\n position: \"absolute\",\n top: `${\n (showArrows\n ? finalThumbWidth +\n finalTrackConfig.margin * 2\n : finalTrackConfig.margin) +\n thumbTop\n }px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${Math.max(\n thumbHeight,\n thumbMinHeight,\n )}px`,\n backgroundColor:\n isThumbHovered || isDragging\n ? finalThumbConfig.hoverColor\n : finalThumbConfig.color,\n opacity:\n isThumbHovered || isDragging\n ? finalThumbConfig.hoverOpacity\n : finalThumbConfig.opacity,\n borderRadius: `${finalThumbConfig.radius}px`,\n cursor: \"pointer\",\n transition:\n \"background-color 0.2s ease-in-out, opacity 0.2s ease-in-out\",\n }}\n />\n </div>\n )}\n\n {/* \uC704\uCABD \uD654\uC0B4\uD45C \uBC84\uD2BC */}\n {showScrollbar && hasScrollableContent && showArrows && (\n <div\n className=\"overlay-scrollbar-up-arrow\"\n onClick={handleUpArrowClick}\n onMouseEnter={() => setHoveredArrow(\"up\")}\n onMouseLeave={() => setHoveredArrow(null)}\n style={{\n position: \"absolute\",\n top: `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${finalThumbWidth}px`,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${Math.max(\n finalThumbWidth * 0.75,\n 8,\n )}px`,\n color:\n hoveredArrow === \"up\"\n ? finalArrowsConfig.hoverColor\n : finalArrowsConfig.color,\n userSelect: \"none\",\n zIndex: 1001,\n opacity: scrollbarVisible\n ? hoveredArrow === \"up\"\n ? finalArrowsConfig.hoverOpacity\n : finalArrowsConfig.opacity\n : 0,\n transition:\n \"opacity 0.2s ease-in-out, color 0.15s ease-in-out\",\n }}\n >\n \u25B2\n </div>\n )}\n\n {/* \uC544\uB798\uCABD \uD654\uC0B4\uD45C \uBC84\uD2BC */}\n {showScrollbar && hasScrollableContent && showArrows && (\n <div\n className=\"overlay-scrollbar-down-arrow\"\n onClick={handleDownArrowClick}\n onMouseEnter={() => setHoveredArrow(\"down\")}\n onMouseLeave={() => setHoveredArrow(null)}\n style={{\n position: \"absolute\",\n bottom: `${finalTrackConfig.margin}px`,\n right:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`, // \uD2B8\uB799 \uC815\uB82C\n width: `${finalThumbWidth}px`,\n height: `${finalThumbWidth}px`,\n cursor: \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: `${Math.max(\n finalThumbWidth * 0.75,\n 8,\n )}px`,\n color:\n hoveredArrow === \"down\"\n ? finalArrowsConfig.hoverColor\n : finalArrowsConfig.color,\n userSelect: \"none\",\n zIndex: 1001,\n opacity: scrollbarVisible\n ? hoveredArrow === \"down\"\n ? finalArrowsConfig.hoverOpacity\n : finalArrowsConfig.opacity\n : 0,\n transition:\n \"opacity 0.2s ease-in-out, color 0.15s ease-in-out\",\n }}\n >\n \u25BC\n </div>\n )}\n\n {/* \uAC00\uB85C \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 */}\n {showScrollbar &&\n showHorizontalScrollbar &&\n hasHorizontalScrollableContent &&\n (finalTrackConfig.overflowX ?? true) && (\n <div\n className=\"overlay-scrollbar-horizontal-track\"\n onMouseEnter={() => {\n // \uD130\uCE58\uC758 \uD0ED\uC740 mouseenter \uB9CC \uD749\uB0B4 \uB0B4\uACE0 mouseleave \uB294 \uB2E4\uB978 \uACF3\uC744 \uB204\uB97C \uB54C\uAE4C\uC9C0 \uC624\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uD638\uBC84\uB85C \uBD99\uC7A1\uC73C\uBA74 \uC624\uB978\uCABD \uB05D\uC744 \uD55C \uBC88 \uB20C\uB800\uC744 \uB54C \uC2A4\uD06C\uB864\uBC14\uAC00 \uACC4\uC18D \uB0A8\uB294\uB2E4.\n if (isCoarsePointer) return;\n\n // \uC228\uAE40 \uD0C0\uC774\uBA38\uB294 \uC989\uC2DC \uCDE8\uC18C\n clearHideTimer();\n\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uC124\uC815 (100ms \uD6C4 \uD45C\uC2DC)\n hoverEnterTimeoutRef.current = setTimeout(\n () => {\n setScrollbarVisible(true);\n hoverEnterTimeoutRef.current = null;\n },\n 100,\n );\n }}\n onMouseLeave={() => {\n // \uD638\uBC84 \uC9C4\uC785 \uD0C0\uC774\uBA38 \uCDE8\uC18C (\uC9C0\uB098\uAC00\uAE30\uB9CC \uD55C \uACBD\uC6B0)\n clearHoverEnterTimer();\n\n if (!isDraggingHorizontal) {\n setHideTimer(finalAutoHideConfig.delay);\n }\n }}\n style={{\n position: \"absolute\",\n bottom: `${wrapperPaddingBottom}px`,\n left: 0,\n width: \"100%\",\n height: `${adjustedTrackWidth}px`,\n opacity: scrollbarVisible ? 1 : 0,\n transition: \"opacity 0.2s ease-in-out\",\n cursor: \"pointer\",\n zIndex: 1000,\n pointerEvents: \"auto\",\n }}\n >\n {/* \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799 \uBC30\uACBD */}\n {finalTrackConfig.visible && (\n <div\n className=\"overlay-scrollbar-horizontal-track-background\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n handleHorizontalTrackClick(e);\n }}\n style={{\n position: \"absolute\",\n bottom:\n finalTrackConfig.alignment ===\n \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`,\n left: `${finalTrackConfig.margin}px`,\n right: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `${\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px`\n : `${adjustedTrackWidth}px`\n : `${finalTrackConfig.margin}px`,\n height: `${finalThumbWidth}px`,\n backgroundColor: finalTrackConfig.color,\n borderRadius: `${finalTrackConfig.radius}px`,\n cursor: \"pointer\",\n }}\n />\n )}\n\n {/* \uAC00\uB85C \uC2A4\uD06C\uB864\uBC14 \uC378 */}\n <div\n className=\"overlay-scrollbar-horizontal-thumb\"\n onMouseDown={handleHorizontalThumbMouseDown}\n onMouseEnter={() =>\n setIsHorizontalThumbHovered(true)\n }\n onMouseLeave={() =>\n setIsHorizontalThumbHovered(false)\n }\n style={{\n position: \"absolute\",\n bottom:\n finalTrackConfig.alignment === \"outside\"\n ? \"0px\"\n : `${\n (adjustedTrackWidth -\n finalThumbWidth) /\n 2\n }px`,\n left: `${\n finalTrackConfig.margin + thumbLeft\n }px`,\n right: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `${\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px`\n : `${adjustedTrackWidth}px`\n : \"auto\",\n maxWidth: hasScrollableContent\n ? finalTrackConfig.alignment ===\n \"outside\"\n ? `calc(100% - ${\n finalTrackConfig.margin +\n thumbLeft +\n adjustedTrackWidth -\n finalTrackConfig.margin\n }px)`\n : `calc(100% - ${\n finalTrackConfig.margin +\n thumbLeft +\n adjustedTrackWidth\n }px)`\n : \"none\",\n width: `${Math.max(thumbWidth, 50)}px`,\n height: `${finalThumbWidth}px`,\n backgroundColor:\n isHorizontalThumbHovered ||\n isDraggingHorizontal\n ? finalThumbConfig.hoverColor\n : finalThumbConfig.color,\n opacity:\n isHorizontalThumbHovered ||\n isDraggingHorizontal\n ? finalThumbConfig.hoverOpacity\n : finalThumbConfig.opacity,\n borderRadius: `${finalThumbConfig.radius}px`,\n cursor: \"pointer\",\n transition:\n \"background-color 0.2s ease-in-out, opacity 0.2s ease-in-out\",\n }}\n />\n </div>\n )}\n\n {/* \"\uB9E8 \uC704\uB85C\" FAB \u2014 \uC2A4\uD06C\uB864\uC744 threshold \uC774\uC0C1 \uB0B4\uB9AC\uBA74 \uB098\uD0C0\uB098\uACE0, \uB204\uB974\uBA74 \uCEE8\uD14C\uC774\uB108\uB97C \uB9E8 \uC704\uB85C \uB418\uB3CC\uB9B0\uB2E4.\n MUI \uB4F1 \uC678\uBD80 \uC758\uC874\uC131 \uC5C6\uC774 \uC21C\uC218 button + \uC778\uB77C\uC778 SVG \uB85C \uADF8\uB9B0\uB2E4. */}\n {fabEnabled && (\n <button\n type=\"button\"\n aria-label=\"\uB9E8 \uC704\uB85C\"\n onClick={() => {\n setIsFabPressed(false);\n // \uC2E4\uC81C\uB85C \uAD74\uB7EC\uAC00\uB294 \uC0C1\uC790\uB97C \uC62C\uB9B0\uB2E4 \u2014 detectInnerScroll(\uAC00\uC0C1 \uD45C \uB4F1)\uC774\uBA74 \uC548\uCABD \uC694\uC18C\uAC00 \uAD74\uB7EC\uAC00\uC11C,\n // \uBC14\uAE65 \uCEE8\uD14C\uC774\uB108\uB9CC \uC62C\uB9AC\uBA74 \uC544\uBB34 \uC77C\uB3C4 \uC5C6\uC5C8\uB2E4(1.7.33). \uB178\uCD9C \uD310\uC815(handleScroll)\uB3C4 \uAC19\uC740 \uC694\uC18C \uAE30\uC900\uC774\uB2E4.\n (findScrollableElement() ?? containerRef.current)?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n // \uB204\uB974\uB294 \uB3D9\uC548\uB9CC \uC120\uBA85\uD574\uC9C4\uB2E4(\uADF8 \uC678\uC5D0\uB294 \uBC18\uD22C\uBA85 \uC720\uC9C0).\n onPointerDown={() => setIsFabPressed(true)}\n onPointerUp={() => setIsFabPressed(false)}\n onPointerLeave={() => setIsFabPressed(false)}\n onPointerCancel={() => setIsFabPressed(false)}\n style={{\n position: \"absolute\",\n [fabPosition]: fabOffset,\n bottom: fabOffsetBottom,\n zIndex: 1200,\n width: fabSize,\n height: fabSize,\n padding: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n border: \"none\",\n borderRadius: \"50%\",\n backgroundColor: fabBackground,\n color: fabColor,\n boxShadow: \"0 3px 8px rgba(15, 23, 42, 0.28)\",\n cursor: \"pointer\",\n // \uD45C\uC2DC \uC911\uC5D0\uB294 \uBC18\uD22C\uBA85\uC744 \uACC4\uC18D \uC720\uC9C0\uD574 \uB0B4\uC6A9\uC744 \uAC00\uB9AC\uC9C0 \uC54A\uACE0, \uB204\uB97C \uB54C\uB9CC \uC120\uBA85\uD574\uC9C4\uB2E4.\n opacity: showScrollTopFab ? (isFabPressed ? 1 : fabIdleOpacity) : 0,\n pointerEvents: showScrollTopFab ? \"auto\" : \"none\",\n // \uD06C\uAE30/\uC774\uB3D9 \uC5C6\uC774 \uD398\uC774\uB4DC\uC778\u00B7\uC544\uC6C3\uB9CC \uD55C\uB2E4.\n // \uD45C\uC2DC \uC911(\uBC18\uD22C\uBA85\u2194\uC120\uBA85)\uC740 \uC989\uAC01\uC801\uC73C\uB85C, \uC790\uB3D9\uC228\uAE40 \uD398\uC774\uB4DC\uC544\uC6C3\uC740 \uBD80\uB4DC\uB7FD\uAC8C(0.45s).\n transition: showScrollTopFab ? \"opacity 0.15s ease\" : \"opacity 0.45s ease\",\n }}\n >\n <svg\n width={Math.round(fabSize * 0.5)}\n height={Math.round(fabSize * 0.5)}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.4\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {/* \uC704\uCABD \uD654\uC0B4\uD45C(\u2191) \u2014 \uAEBE\uC1E0(^)\uBCF4\uB2E4 \"\uB9E8 \uC704\uB85C\" \uC758\uBBF8\uAC00 \uBD84\uBA85\uD558\uB2E4. */}\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"5\" />\n <polyline points=\"5 12 12 5 19 12\" />\n </svg>\n </button>\n )}\n </div>\n );\n },\n);\n\nexport default OverlayScrollbar;\nexport { OverlayScrollbar };\n", "/**\n * MIT License\n *\n * Copyright (c) 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { DragScrollConfig } from \"../types\";\n\n// \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uD074\uB798\uC2A4\uB4E4 (\uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C\uC5D0\uC11C \uD655\uC778)\nconst DEFAULT_EXCLUDE_CLASSES = [\n // \uAE30\uBCF8 \uC785\uB825 \uC694\uC18C\uB4E4\n \"editor\",\n \"textarea\",\n \"input\",\n \"select\",\n \"textfield\",\n \"form-control\",\n \"contenteditable\",\n\n // Material-UI \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"MuiInputBase-input\",\n \"MuiSelect-select\",\n \"MuiOutlinedInput-input\",\n \"MuiFilledInput-input\",\n \"MuiInput-input\",\n \"MuiFormControl-root\",\n \"MuiTextField-root\",\n \"MuiSelect-root\",\n \"MuiOutlinedInput-root\",\n \"MuiFilledInput-root\",\n \"MuiInput-root\",\n \"MuiAutocomplete-input\",\n \"MuiDatePicker-input\",\n \"MuiSlider-thumb\",\n \"MuiSlider-rail\",\n \"MuiSlider-track\",\n \"MuiSlider-mark\",\n \"MuiSlider-markLabel\",\n \"MuiSlider-root\",\n \"MuiSlider-colorPrimary\",\n \"MuiSlider-sizeMedium\",\n \"MuiIconButton-root\",\n \"MuiButton-root\",\n \"MuiButtonBase-root\",\n \"MuiTouchRipple-root\",\n \"MuiCheckbox-root\",\n \"MuiRadio-root\",\n \"MuiSwitch-root\",\n \"PrivateSwitchBase-root\",\n\n // Ant Design \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"ant-input\",\n \"ant-input-affix-wrapper\",\n \"ant-input-group-addon\",\n \"ant-input-number\",\n \"ant-input-number-handler\",\n \"ant-select\",\n \"ant-select-selector\",\n \"ant-select-selection-search\",\n \"ant-select-dropdown\",\n \"ant-cascader\",\n \"ant-cascader-input\",\n \"ant-picker\",\n \"ant-picker-input\",\n \"ant-time-picker\",\n \"ant-calendar-picker\",\n \"ant-slider\",\n \"ant-slider-track\",\n \"ant-slider-handle\",\n \"ant-switch\",\n \"ant-checkbox\",\n \"ant-checkbox-wrapper\",\n \"ant-radio\",\n \"ant-radio-wrapper\",\n \"ant-rate\",\n \"ant-upload\",\n \"ant-upload-drag\",\n \"ant-form-item\",\n \"ant-form-item-control\",\n \"ant-btn\",\n \"ant-dropdown\",\n \"ant-dropdown-trigger\",\n \"ant-menu\",\n \"ant-menu-item\",\n \"ant-tooltip\",\n \"ant-popover\",\n \"ant-modal\",\n \"ant-drawer\",\n \"ant-tree-select\",\n \"ant-auto-complete\",\n \"ant-mentions\",\n \"ant-transfer\",\n\n // Shadcn/ui \uCEF4\uD3EC\uB10C\uD2B8\uB4E4\n \"ui-input\",\n \"ui-textarea\",\n \"ui-select\",\n \"ui-select-trigger\",\n \"ui-select-content\",\n \"ui-select-item\",\n \"ui-button\",\n \"ui-checkbox\",\n \"ui-radio-group\",\n \"ui-switch\",\n \"ui-slider\",\n \"ui-range-slider\",\n \"ui-calendar\",\n \"ui-date-picker\",\n \"ui-combobox\",\n \"ui-command\",\n \"ui-command-input\",\n \"ui-popover\",\n \"ui-dialog\",\n \"ui-sheet\",\n \"ui-dropdown-menu\",\n \"ui-context-menu\",\n \"ui-menubar\",\n \"ui-navigation-menu\",\n \"ui-form\",\n \"ui-form-control\",\n \"ui-form-item\",\n \"ui-form-field\",\n \"ui-label\",\n // Radix UI \uAE30\uBCF8 \uD074\uB798\uC2A4\uB4E4 (Shadcn \uAE30\uBC18)\n \"radix-ui\",\n \"radix-select\",\n \"radix-dropdown\",\n \"radix-dialog\",\n \"radix-popover\",\n \"radix-accordion\",\n \"radix-tabs\",\n \"radix-slider\",\n \"radix-switch\",\n \"radix-checkbox\",\n \"radix-radio\",\n\n // Quill Editor\n \"ql-editor\",\n \"ql-container\",\n \"ql-toolbar\",\n \"ql-picker\",\n \"ql-picker-label\",\n \"ql-picker-options\",\n \"ql-formats\",\n \"ql-snow\",\n \"ql-bubble\",\n \"quill\",\n \"quilleditor\",\n\n // Monaco Editor\n \"monaco-editor\",\n \"monaco-editor-background\",\n \"view-lines\",\n \"decorationsOverviewRuler\",\n \"monaco-scrollable-element\",\n\n // CodeMirror\n \"CodeMirror\",\n \"CodeMirror-code\",\n \"CodeMirror-lines\",\n \"CodeMirror-scroll\",\n \"CodeMirror-sizer\",\n \"cm-editor\",\n \"cm-focused\",\n \"cm-content\",\n\n // TinyMCE\n \"tox-editor-container\",\n \"tox-editor-header\",\n \"tox-edit-area\",\n \"tox-tinymce\",\n \"mce-content-body\",\n\n // CKEditor\n \"ck-editor\",\n \"ck-content\",\n \"ck-toolbar\",\n \"ck-editor__editable\",\n \"ck-widget\",\n\n // Slate.js\n \"slate-editor\",\n \"slate-content\",\n\n // Draft.js\n \"DraftEditor-root\",\n \"DraftEditor-editorContainer\",\n \"public-DraftEditor-content\",\n\n // EhfuseEditor\n \"ehfuse-editor\",\n \"ehfuse-editor-wrapper\",\n \"ehfuse-editor-content\",\n \"ehfuse-toolbar\",\n \"ehfuse-toolbar-group\",\n \"ehfuse-cursor\",\n\n // \uAE30\uD0C0 \uC5D0\uB514\uD130\uB4E4\n \"text-editor\",\n \"rich-text-editor\",\n \"wysiwyg\",\n \"ace_editor\",\n \"ace_content\",\n];\n\n/**\n * \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uC694\uC18C\uB4E4\uC778\uC9C0 \uD655\uC778\n */\n/**\n * \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uC694\uC18C\uB4E4\uC778\uC9C0 \uD655\uC778\n */\nexport const isTextInputElement = (\n element: Element,\n config?: DragScrollConfig\n): boolean => {\n // \uC0C1\uC18D\uB41C contenteditable(\uC790\uC2DD\uB9CC \uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC5D0\uB514\uD130 \uB4F1) \u2014 getAttribute\uB9CC\uC73C\uB85C\uB294 \uB193\uCE68\n if (element instanceof HTMLElement && element.isContentEditable) {\n return true;\n }\n\n // \uB124\uC774\uD2F0\uBE0C input/textarea\uAC00 \uC544\uB2CC ARIA \uD14D\uC2A4\uD2B8 \uC785\uB825\n if (element.closest('[role=\"textbox\"],[role=\"searchbox\"]')) {\n return true;\n }\n\n const tagName = element.tagName.toLowerCase();\n // \uC2A4\uD06C\uB864 \uC804\uC6A9 \uD0A4(Home/End \uB4F1)\uB97C \uBE0C\uB77C\uC6B0\uC800\uC5D0 \uB9E1\uACA8\uC57C \uD558\uB294 \uC785\uB825\uB9CC \uC81C\uC678.\n // \uC608\uC804\uC5D0\uB294 \uC77C\uBD80 type\uB9CC \uD5C8\uC6A9\uD574 date \uB4F1\uC5D0\uC11C \uC2A4\uD06C\uB864 \uAC00\uB85C\uCC44\uAE30\uAC00 \uBC1C\uC0DD\uD560 \uC218 \uC788\uC5C8\uC74C.\n if (tagName === \"input\") {\n const type = (\n (element as HTMLInputElement).type || \"text\"\n ).toLowerCase();\n const typesWithoutCaretLikeKeyboardBehavior = [\n \"button\",\n \"submit\",\n \"reset\",\n \"image\",\n \"hidden\",\n ];\n return !typesWithoutCaretLikeKeyboardBehavior.includes(type);\n }\n\n // textarea, select, \uD3B8\uC9D1 \uAC00\uB2A5\uD55C \uC694\uC18C\uB4E4\n if ([\"textarea\", \"select\", \"button\"].includes(tagName)) {\n return true;\n }\n\n // SVG \uC694\uC18C\uB4E4 (\uC544\uC774\uCF58\uB4E4)\n if (\n [\n \"svg\",\n \"path\",\n \"circle\",\n \"rect\",\n \"line\",\n \"polygon\",\n \"polyline\",\n ].includes(tagName)\n ) {\n return true;\n }\n\n // contenteditable \uC18D\uC131\uC774 \uC788\uB294 \uC694\uC18C\n if (element.getAttribute(\"contenteditable\") === \"true\") {\n return true;\n }\n\n // \uCD94\uAC00 \uC140\uB809\uD130 \uCCB4\uD06C\n if (config?.excludeSelectors) {\n for (const selector of config.excludeSelectors) {\n if (element.matches(selector)) {\n return true;\n }\n }\n }\n\n return checkElementAndParents(element, config);\n};\n\n/**\n * \uC790\uC2E0 \uB610\uB294 \uBD80\uBAA8 \uC694\uC18C\uB4E4\uC744 \uD655\uC778\uD558\uC5EC \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC744 \uC81C\uC678\uD560 \uC694\uC18C\uC778\uC9C0 \uD310\uB2E8\n */\nconst checkElementAndParents = (\n element: Element,\n config?: DragScrollConfig\n): boolean => {\n // \uD45C\uC2DC \uC18D\uC131\uC73C\uB85C \uB048 \uC601\uC5ED \u2014 \uC904 \uC790\uCCB4\uB97C \uB04C\uC5B4 \uC21C\uC11C\uB97C \uBC14\uAFB8\uB294 \uBAA9\uB85D(dnd-kit \uB4F1)\uC740 \uB04C\uAE30\uAC00 \uACE7 \uC21C\uC11C \uC62E\uAE30\uAE30\uB77C \uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uAC19\uC774 \uB3CC\uBA74 \uC548 \uB41C\uB2E4.\n // \uD074\uB798\uC2A4 \uBAA9\uB85D\uC5D0 \uC5C6\uB294 \uC694\uC18C\uB3C4 \uC18C\uBE44\uCC98\uAC00 data-drag-scroll=\"off\" \uB9CC \uBD99\uC774\uBA74 \uBE60\uC9C4\uB2E4(\uAE4A\uC774 \uC81C\uD55C \uC5C6\uC774 \uC870\uC0C1\uAE4C\uC9C0).\n if (element.closest('[data-drag-scroll=\"off\"]')) {\n return true;\n }\n\n // \uBAA8\uB4E0 \uC81C\uC678 \uD074\uB798\uC2A4\uB4E4 \uD569\uCE58\uAE30 (\uAE30\uBCF8 \uD074\uB798\uC2A4 + \uC0AC\uC6A9\uC790 \uCD94\uAC00 \uD074\uB798\uC2A4)\n const allExcludeClasses = [\n ...DEFAULT_EXCLUDE_CLASSES,\n ...(config?.excludeClasses || []),\n ];\n\n let currentElement: Element | null = element;\n let depth = 0;\n const maxDepth = 5; // \uCD5C\uB300 5\uB2E8\uACC4\uAE4C\uC9C0 \uBD80\uBAA8 \uC694\uC18C \uD655\uC778\n\n while (currentElement && depth <= maxDepth) {\n // \uD604\uC7AC \uC694\uC18C\uAC00 \uC81C\uC678 \uD074\uB798\uC2A4\uB97C \uAC00\uC9C0\uACE0 \uC788\uB294\uC9C0 \uD655\uC778\n if (\n allExcludeClasses.some((cls) =>\n currentElement!.classList.contains(cls)\n )\n ) {\n return true;\n }\n\n // \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB8E8\uD2B8\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC911\uB2E8\n if (currentElement.classList.contains(\"MuiDialogContent-root\")) {\n break;\n }\n\n currentElement = currentElement.parentElement;\n depth++;\n }\n\n return false;\n};\n\n/**\n * \uB2E4\uB978 \uB2E4\uC774\uC5BC\uB85C\uADF8\uB098 \uBAA8\uB2EC\uC774 \uC0C1\uC704\uC5D0 \uC788\uB294\uC9C0 \uD655\uC778\n */\nexport const hasUpperModal = (scrollContainer: Element): boolean => {\n const currentDialog = scrollContainer.closest(\n \".MuiDialog-root\"\n ) as HTMLElement;\n if (!currentDialog) return false;\n\n const allModals = document.querySelectorAll(\n \".MuiDialog-root, .MuiModal-root, .MuiPopover-root\"\n );\n const currentZIndex = parseInt(\n window.getComputedStyle(currentDialog).zIndex || \"0\"\n );\n\n for (const modal of allModals) {\n if (modal !== currentDialog) {\n const modalElement = modal as HTMLElement;\n const modalZIndex = parseInt(\n window.getComputedStyle(modalElement).zIndex || \"0\"\n );\n\n if (\n modalZIndex > currentZIndex &&\n modalElement.style.display !== \"none\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n};\n\n/**\n * \uD074\uB9AD\uD55C \uC694\uC18C\uAC00 \uB2E4\uB978 \uB2E4\uC774\uC5BC\uB85C\uADF8 \uB0B4\uBD80\uC778\uC9C0 \uD655\uC778\n */\nexport const isClickInOtherDialog = (\n clickedElement: Element,\n currentDialog: Element | null\n): boolean => {\n const parentDialog = clickedElement.closest(\n \".MuiDialog-root, .MuiPopover-root, .MuiModal-root\"\n );\n return parentDialog !== null && parentDialog !== currentDialog;\n};\n", "import type React from \"react\";\n\n/**\n * \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uC758 `padding-bottom` \uC740 \uB0B4\uC6A9\uC774 \uB118\uCE60 \uB54C \uBE0C\uB77C\uC6B0\uC800\uAC00 \uC2A4\uD06C\uB864 \uAC00\uB2A5 \uC601\uC5ED(scrollHeight)\uC5D0\n * \uD3EC\uD568\uC2DC\uD0A4\uC9C0 \uC54A\uB294\uB2E4. `padding-top` \uC740 \uBC18\uC601\uB418\uC9C0\uB9CC bottom \uB9CC \uBB34\uC2DC\uB418\uB294 \uBE14\uB85D/\uD50C\uB809\uC2A4 \uC624\uBC84\uD50C\uB85C \uB3D9\uC791\uC774\uB77C,\n * \uC0AC\uC6A9\uC790\uAC00 `containerStyle`/`contentStyle` \uC5D0 `paddingBottom` \uC744 \uC918\uB3C4 \uC2A4\uD06C\uB864 \uB05D\uC5D0\uC11C \uC5EC\uBC31\uC774 \uC0AC\uB77C\uC9C4\uB2E4.\n *\n * \uADF8\uB798\uC11C \uC774 \uCEF4\uD3EC\uB10C\uD2B8\uB294 \uC9C0\uC815\uB41C \uD558\uB2E8 padding \uC744 \uC2A4\uD0C0\uC77C\uC5D0\uC11C \uAC77\uC5B4\uB0B4\uACE0, \uAC19\uC740 \uB192\uC774\uC758 spacer \uC5D8\uB9AC\uBA3C\uD2B8\uB97C\n * content \uB9E8 \uB05D\uC5D0 \uB80C\uB354\uD574 \uB3D9\uC77C\uD55C \uC5EC\uBC31\uC744 \uC2E4\uC81C \uB808\uC774\uC544\uC6C3 \uB192\uC774\uB85C \uB9CC\uB4E4\uC5B4 \uC900\uB2E4.\n */\n\n/** CSS \uAE38\uC774 \uAC12\uC744 px \uC22B\uC790\uB85C \uBC14\uAFBC\uB2E4. px/\uC22B\uC790\uB9CC \uC9C0\uC6D0\uD558\uACE0 \uADF8 \uC678 \uB2E8\uC704(%, em \uB4F1)\uB294 null \uC744 \uBC18\uD658\uD55C\uB2E4. */\nexport const toPixelLength = (\n value: string | number | undefined,\n): number | null => {\n if (value === undefined || value === null) return null;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n\n const trimmed = value.trim();\n if (trimmed === \"\") return null;\n\n // \uC22B\uC790\uB9CC \uC788\uAC70\uB098 px \uB2E8\uC704\uC778 \uACBD\uC6B0\uB9CC \uC548\uC804\uD558\uAC8C \uBCC0\uD658\uD55C\uB2E4.\n const match = /^(-?\\d*\\.?\\d+)(px)?$/.exec(trimmed);\n if (!match) return null;\n\n const parsed = Number.parseFloat(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n};\n\n/** \uCD95\uC57D\uD615 `padding` \uBB38\uC790\uC5F4\uC5D0\uC11C bottom \uAC12\uC744 \uCD94\uCD9C\uD55C\uB2E4(1~4\uAC1C \uAC12 \uADDC\uCE59). */\nconst readShorthandPaddingBottom = (\n padding: string | number | undefined,\n): string | number | undefined => {\n if (padding === undefined || padding === null) return undefined;\n // \uC22B\uC790 \uCD95\uC57D\uD615\uC740 \uB124 \uBC29\uD5A5\uC774 \uAC19\uC740 \uAC12\uC774\uB2E4.\n if (typeof padding === \"number\") return padding;\n\n const parts = padding.trim().split(/\\s+/);\n if (parts.length === 0 || parts[0] === \"\") return undefined;\n // padding: a | a b | a b c | a b c d \u2192 bottom \uC740 \uAC01\uAC01 a | a | c | c\n if (parts.length === 1) return parts[0];\n if (parts.length === 2) return parts[0];\n return parts[2];\n};\n\n/** \uCD95\uC57D\uD615 `padding` \uBB38\uC790\uC5F4\uC5D0\uC11C bottom \uB9CC 0 \uC73C\uB85C \uBC14\uAFBC\uB2E4. */\nconst withShorthandPaddingBottomRemoved = (\n padding: string | number,\n): string => {\n const parts =\n typeof padding === \"number\"\n ? [`${padding}px`]\n : padding.trim().split(/\\s+/);\n\n // \uAC01 \uCD95\uC57D\uD615\uC744 4\uAC12 \uD615\uD0DC\uB85C \uD3BC\uCE5C \uB4A4 bottom \uB9CC 0 \uC73C\uB85C \uB9CC\uB4E0\uB2E4.\n const [top, right = top, bottom = top, left = right] = parts;\n void bottom;\n return `${top} ${right} 0 ${left}`;\n};\n\n/** paddingBottom \uC744 \uAC77\uC5B4\uB0B8 \uC2A4\uD0C0\uC77C\uACFC, spacer \uB85C \uC62E\uAE38 \uB192\uC774(px)\uB97C \uD568\uAED8 \uBC18\uD658\uD55C\uB2E4. */\nexport interface ExtractedBottomPadding {\n style: React.CSSProperties | undefined; // \uD558\uB2E8 padding \uC744 \uC81C\uAC70\uD55C \uC2A4\uD0C0\uC77C\n spacerHeight: number; // spacer \uB85C \uB9CC\uB4E4\uC5B4\uC57C \uD560 \uB192\uC774(px, 0 \uC774\uBA74 \uBD88\uD544\uC694)\n}\n\n/**\n * \uC2A4\uD0C0\uC77C\uC5D0\uC11C \uD558\uB2E8 padding \uC744 \uBD84\uB9AC\uD55C\uB2E4.\n * px \uB85C \uD658\uC0B0 \uAC00\uB2A5\uD55C \uAC12\uB9CC spacer \uB85C \uC62E\uAE30\uACE0, %/em \uAC19\uC774 \uD658\uC0B0 \uBD88\uAC00\uD55C \uAC12\uC740 \uC6D0\uB798 \uC2A4\uD0C0\uC77C\uC5D0 \uADF8\uB300\uB85C \uB454\uB2E4.\n */\nexport const extractBottomPadding = (\n style: React.CSSProperties | undefined,\n): ExtractedBottomPadding => {\n if (!style) return { style, spacerHeight: 0 };\n\n const hasLonghand = style.paddingBottom !== undefined;\n const hasShorthand = style.padding !== undefined;\n if (!hasLonghand && !hasShorthand) return { style, spacerHeight: 0 };\n\n // longhand(paddingBottom)\uAC00 \uCD95\uC57D\uD615(padding)\uBCF4\uB2E4 \uC6B0\uC120\uD55C\uB2E4.\n const rawBottom = hasLonghand\n ? style.paddingBottom\n : readShorthandPaddingBottom(style.padding);\n\n const pixels = toPixelLength(rawBottom as string | number | undefined);\n // \uD658\uC0B0 \uBD88\uAC00\uD558\uAC70\uB098 0 \uC774\uD558\uBA74 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uB294\uB2E4(\uC0AC\uC6A9\uC790 \uC758\uB3C4 \uBCF4\uC874).\n if (pixels === null || pixels <= 0) return { style, spacerHeight: 0 };\n\n const nextStyle: React.CSSProperties = { ...style };\n if (hasShorthand && style.padding !== undefined) {\n nextStyle.padding = withShorthandPaddingBottomRemoved(\n style.padding as string | number,\n );\n }\n if (hasLonghand) {\n nextStyle.paddingBottom = 0;\n }\n\n return { style: nextStyle, spacerHeight: pixels };\n};\n", "/**\n * usePullToRefresh.ts\n * \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68(pull-to-refresh) \uB85C\uC9C1\n *\n * \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108\uAC00 \uB9E8 \uC704(scrollTop <= 0)\uC5D0 \uC788\uC744 \uB54C \uC544\uB798\uB85C \uB2F9\uAE30\uB294 \uD130\uCE58 \uC81C\uC2A4\uCC98\uB97C \uAC10\uC9C0\uD574,\n * \uC784\uACC4 \uAC70\uB9AC \uC774\uC0C1 \uB2F9\uAE34 \uC0C1\uD0DC\uC5D0\uC11C \uC190\uC744 \uB5BC\uBA74 onRefresh \uCF5C\uBC31\uC744 \uC2E4\uD589\uD55C\uB2E4.\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n */\n\nimport { useEffect, useRef, useState, RefObject } from \"react\";\nimport { PullToRefreshConfig } from \"../types\";\n\ninterface UsePullToRefreshProps {\n containerRef: RefObject<HTMLDivElement | null>; // \uC2A4\uD06C\uB864 \uCEE8\uD14C\uC774\uB108 ref\n config: PullToRefreshConfig; // \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC124\uC815\n}\n\ninterface UsePullToRefreshResult {\n pullDistance: number; // \uD604\uC7AC \uB2F9\uAE40 \uAC70\uB9AC(px, \uAC10\uC1E0 \uC801\uC6A9 \uD6C4) \u2014 \uC778\uB514\uCF00\uC774\uD130 \uC704\uCE58\uC6A9\n isRefreshing: boolean; // onRefresh \uC2E4\uD589 \uC911 \uC5EC\uBD80 \u2014 \uC2A4\uD53C\uB108 \uD45C\uC2DC\uC6A9\n pullProgress: number; // \uC784\uACC4 \uAC70\uB9AC \uB300\uBE44 \uC9C4\uD589\uB960(0~1) \u2014 \uC778\uB514\uCF00\uC774\uD130 \uD68C\uC804/\uC0C9\uC0C1\uC6A9\n pullThreshold: number; // \uCD5C\uC885 \uC784\uACC4 \uAC70\uB9AC(px)\n}\n\n/** \uB2F9\uACA8\uC11C \uC0C8\uB85C\uACE0\uCE68 \uC81C\uC2A4\uCC98\uB97C \uAC10\uC9C0\uD558\uACE0 \uC0C1\uD0DC\uB97C \uBC18\uD658\uD55C\uB2E4. */\nexport const usePullToRefresh = ({\n containerRef,\n config,\n}: UsePullToRefreshProps): UsePullToRefreshResult => {\n // \uC124\uC815 \uAE30\uBCF8\uAC12\n const enabled = config.enabled ?? true; // \uAE30\uBCF8 \uD65C\uC131 (onRefresh \uAC00 \uC5C6\uC73C\uBA74 \uB3D9\uC791 \uC548 \uD568)\n const threshold = config.threshold ?? 80; // \uB193\uC558\uC744 \uB54C \uC0C8\uB85C\uACE0\uCE68\uC774 \uC2E4\uD589\uB418\uB294 \uC784\uACC4 \uB2F9\uAE40 \uAC70\uB9AC\n const maxDistance = config.maxDistance ?? threshold * 2; // \uC778\uB514\uCF00\uC774\uD130\uAC00 \uB530\uB77C\uC624\uB294 \uCD5C\uB300 \uAC70\uB9AC\n const onRefresh = config.onRefresh;\n\n const [pullDistance, setPullDistance] = useState(0);\n const [isRefreshing, setIsRefreshing] = useState(false);\n\n // \uD130\uCE58 \uCD94\uC801 \uC0C1\uD0DC (\uB80C\uB354\uC640 \uBB34\uAD00\uD558\uBBC0\uB85C ref \uB85C \uAD00\uB9AC)\n const startYRef = useRef(0);\n const startXRef = useRef(0);\n const trackingRef = useRef(false); // \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uB41C \uD130\uCE58 \uCD94\uC801 \uC911\n const pullingRef = useRef(false); // \uD558\uD5A5 \uB2F9\uAE40\uC73C\uB85C \uD655\uC815\uB428(\uC2A4\uD06C\uB864 \uB300\uC2E0 \uC778\uB514\uCF00\uC774\uD130 \uB3D9\uC791)\n const nativeBounceRef = useRef(false); // \uB2F9\uAE40 \uD655\uC815 \uC2DC\uC810\uC5D0 \uC774\uBBF8 \uB124\uC774\uD2F0\uBE0C \uB7EC\uBC84\uBC34\uB4DC(iOS)\uAC00 \uC2DC\uC791\uB41C \uC0C1\uD0DC\n const pullDistanceRef = useRef(0); // touchend \uC5D0\uC11C \uCD5C\uC2E0 \uAC70\uB9AC \uCC38\uC870\uC6A9\n const refreshingRef = useRef(false); // \uB9AC\uC2A4\uB108 \uC548\uC5D0\uC11C \uCD5C\uC2E0 \uC0C8\uB85C\uACE0\uCE68 \uC0C1\uD0DC \uCC38\uC870\uC6A9\n\n useEffect(() => {\n refreshingRef.current = isRefreshing;\n }, [isRefreshing]);\n\n useEffect(() => {\n const container = containerRef.current;\n const active = enabled && typeof onRefresh === \"function\";\n if (!container || !active) return;\n\n /** \uD130\uCE58 \uC9C0\uC810\uBD80\uD130 \uCEE8\uD14C\uC774\uB108\uAE4C\uC9C0\uC758 \uC2A4\uD06C\uB864 \uAC00\uB2A5\uD55C \uC870\uC0C1\uB4E4\uC774 \uC804\uBD80 \uB9E8 \uC704\uC778\uC9C0 \uD655\uC778\uD55C\uB2E4.\n * (\uB0B4\uBD80 \uBAA9\uB85D \uC2A4\uD06C\uB864\uB7EC\uAC00 \uC2A4\uD06C\uB864\uB41C \uC0C1\uD0DC\uC5D0\uC11C \uB2F9\uAE40\uC774 \uC624\uBC1C\uB3D9\uD558\uC9C0 \uC54A\uAC8C \uD55C\uB2E4) */\n const isTouchPathAtTop = (target: EventTarget | null): boolean => {\n let el = target instanceof Element ? target : null;\n while (el && el !== container) {\n if (\n el instanceof HTMLElement &&\n el.scrollHeight > el.clientHeight + 1 &&\n el.scrollTop > 0\n ) {\n return false;\n }\n el = el.parentElement;\n }\n return true;\n };\n\n /** \uD130\uCE58 \uC2DC\uC791 \u2014 \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uB41C \uB2E8\uC77C \uD130\uCE58\uB9CC \uCD94\uC801 \uD6C4\uBCF4\uB85C \uC0BC\uB294\uB2E4. */\n const handleTouchStart = (e: TouchEvent) => {\n // \uC774\uC804 \uC81C\uC2A4\uCC98\uC758 \uC794\uC874 \uC0C1\uD0DC\uB97C \uBB34\uC870\uAC74 \uCD08\uAE30\uD654\uD55C\uB2E4(v1.7.5 \uC218\uC815). \uD130\uCE58\uD588\uB358 \uC694\uC18C\uAC00\n // \uC81C\uC2A4\uCC98 \uB3C4\uC911 \uC5B8\uB9C8\uC6B4\uD2B8\uB418\uAC70\uB098(\uC2E4\uC2DC\uAC04 \uB9AC\uB80C\uB354 \uB4F1) touchend/touchcancel \uC774 \uC720\uC2E4\uB418\uBA74\n // (WebView \uC81C\uC2A4\uCC98 \uC544\uB808\uB098\uAC00 \uD130\uCE58\uB97C \uAC00\uB85C\uCC4C \uB54C\u00B7\uC911\uCCA9 \uC2A4\uD06C\uB864\uB7EC \uD658\uACBD\uC5D0\uC11C \uD754\uD568) touch\n // \uC774\uBCA4\uD2B8\uAC00 \uBD84\uB9AC\uB41C \uC6D0\uB798 \uD0C0\uAE43\uC5D0 \uACE0\uC815\uB418\uC5B4 touchend \uAC00 \uCEE8\uD14C\uC774\uB108\uAE4C\uC9C0 \uBC84\uBE14\uB9C1\uB418\uC9C0 \uC54A\uB294\uB2E4 \u2014\n // \uB9AC\uC14B \uC5C6\uC774\uB294 \uCD94\uC801/\uB2F9\uAE40 \uD50C\uB798\uADF8\uAC00 \uB0A8\uC544 \uB2E4\uC74C \uC81C\uC2A4\uCC98\uC758 touchmove \uAC00 \uC804\uBD80 preventDefault\n // \uB418\uC5B4 \uC2A4\uD06C\uB864\uC774 \uACC4\uC18D \uB9C9\uD788\uACE0, \uC794\uC874 \uC2DC\uC791\uC88C\uD45C \uAE30\uC900 \uAC70\uB9AC\uB85C \uC624\uC791\uB3D9 \uC0C8\uB85C\uACE0\uCE68\uAE4C\uC9C0 \uB0AC\uB2E4.\n trackingRef.current = false;\n pullingRef.current = false;\n nativeBounceRef.current = false;\n\n if (refreshingRef.current) return;\n if (e.touches.length !== 1) return;\n // \uC815\uD655\uD788 \uB9E8 \uC704(scrollTop === 0)\uC5D0\uC11C \uC2DC\uC791\uD55C \uD130\uCE58\uB9CC \uCD94\uC801\uD55C\uB2E4.\n // scrollTop > 0 \uC740 \uC77C\uBC18 \uC2A4\uD06C\uB864\uC774\uACE0, scrollTop < 0 \uC740 iOS \uB7EC\uBC84\uBC34\uB4DC\uAC00\n // \uC9C4\uD589 \uC911\uC774\uB77C\uB294 \uB73B \u2014 \uBC14\uC6B4\uC2A4 \uC911 touchmove \uB97C preventDefault \uD558\uBA74 WebKit\n // \uC2A4\uD06C\uB864\uB7EC\uAC00 \uC601\uAD6C\uD788 \uBA48\uCD94\uB294(\uC774\uD6C4 \uBAA8\uB4E0 \uD130\uCE58 \uBB34\uC2DC) \uD504\uB9AC\uC988\uAC00 \uBC1C\uC0DD\uD55C\uB2E4.\n if (container.scrollTop !== 0) return;\n if (!isTouchPathAtTop(e.target)) return;\n trackingRef.current = true;\n startYRef.current = e.touches[0].clientY;\n startXRef.current = e.touches[0].clientX;\n };\n\n /** \uD130\uCE58 \uC774\uB3D9 \u2014 \uD558\uD5A5 \uC6B0\uC138 \uB2F9\uAE40\uC774 \uD655\uC815\uB418\uBA74 \uC2A4\uD06C\uB864\uC744 \uB9C9\uACE0 \uC778\uB514\uCF00\uC774\uD130\uB97C \uB530\uB77C \uB0B4\uB9B0\uB2E4. */\n const handleTouchMove = (e: TouchEvent) => {\n if (!trackingRef.current || refreshingRef.current) return;\n if (e.touches.length !== 1) return;\n\n const dy = e.touches[0].clientY - startYRef.current;\n const dx = e.touches[0].clientX - startXRef.current;\n\n if (!pullingRef.current) {\n // \uC544\uC9C1 \uBBF8\uD655\uC815 \u2014 \uC704\uB85C \uC2A4\uD06C\uB864\uD588\uAC70\uB098 \uC218\uD3C9 \uC81C\uC2A4\uCC98\uBA74 \uCD94\uC801 \uC885\uB8CC\n if (dy <= 0 || container.scrollTop > 0) {\n trackingRef.current = false;\n return;\n }\n // \uC218\uC9C1 \uC6B0\uC138(\uB300\uAC01\uC120 \uC2A4\uC640\uC774\uD504 \uC624\uBC1C\uB3D9 \uBC29\uC9C0) + \uCD5C\uC18C \uC774\uB3D9\uB7C9\uC744 \uB118\uC73C\uBA74 \uB2F9\uAE40 \uD655\uC815\n if (dy > 8 && dy > Math.abs(dx)) {\n pullingRef.current = true;\n // \uD655\uC815 \uC2DC\uC810\uC5D0 \uC774\uBBF8 \uB124\uC774\uD2F0\uBE0C \uB7EC\uBC84\uBC34\uB4DC\uAC00 \uC2DC\uC791\uB410\uC73C\uBA74(iOS: \uB2F9\uAE30\uB294 \uB3D9\uC548\n // scrollTop \uC774 \uC74C\uC218\uB85C \uB0B4\uB824\uAC04\uB2E4) preventDefault \uBAA8\uB4DC\uB85C \uB4E4\uC5B4\uAC00\uC9C0 \uC54A\uB294\uB2E4.\n // \uC9C4\uD589 \uC911\uC778 \uBC14\uC6B4\uC2A4\uB97C \uB3C4\uC911\uC5D0 \uCDE8\uC18C\uD558\uBA74 WebKit \uC2A4\uD06C\uB864\uB7EC\uAC00 \uD504\uB9AC\uC988\uB41C\uB2E4.\n // \uB300\uC2E0 \uB124\uC774\uD2F0\uBE0C \uBC14\uC6B4\uC2A4\uB97C \uADF8\uB300\uB85C \uB450\uACE0 \uAC70\uB9AC\uB9CC \uCD94\uC801\uD574, \uB193\uC744 \uB54C \uC784\uACC4\uB97C\n // \uB118\uC5C8\uC73C\uBA74 \uC0C8\uB85C\uACE0\uCE68\uD55C\uB2E4(\uC778\uB514\uCF00\uC774\uD130\uB3C4 \uB3D9\uC77C\uD558\uAC8C \uD45C\uC2DC).\n nativeBounceRef.current = container.scrollTop < 0;\n } else {\n return;\n }\n }\n\n // \uB2F9\uAE40 \uD655\uC815 \uAD6C\uAC04 \u2014 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864/\uC624\uBC84\uC2A4\uD06C\uB864\uC744 \uB9C9\uACE0 \uAC70\uB9AC(\uAC10\uC1E0 0.5)\uB97C \uAC31\uC2E0\uD55C\uB2E4.\n // (\uB124\uC774\uD2F0\uBE0C \uBC14\uC6B4\uC2A4 \uBAA8\uB4DC\uC5D0\uC11C\uB294 \uCDE8\uC18C\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC704 \uD504\uB9AC\uC988 \uBC29\uC9C0)\n if (!nativeBounceRef.current && e.cancelable) e.preventDefault();\n const distance = Math.min(Math.max(dy, 0) * 0.5, maxDistance);\n pullDistanceRef.current = distance;\n setPullDistance(distance);\n };\n\n /** \uD130\uCE58 \uC885\uB8CC \u2014 \uC784\uACC4 \uAC70\uB9AC \uC774\uC0C1\uC774\uBA74 onRefresh \uC2E4\uD589, \uC544\uB2C8\uBA74 \uC6D0\uC704\uCE58. */\n const handleTouchEnd = () => {\n if (!trackingRef.current) return;\n trackingRef.current = false;\n\n if (!pullingRef.current) return;\n pullingRef.current = false;\n nativeBounceRef.current = false;\n\n const distance = pullDistanceRef.current;\n pullDistanceRef.current = 0;\n\n if (distance >= threshold && !refreshingRef.current) {\n // \uC0C8\uB85C\uACE0\uCE68 \uC2E4\uD589 \u2014 \uC644\uB8CC(Promise \uD574\uC18C)\uAE4C\uC9C0 \uC2A4\uD53C\uB108\uB97C \uC784\uACC4 \uC704\uCE58\uC5D0 \uC720\uC9C0\uD55C\uB2E4.\n setIsRefreshing(true);\n setPullDistance(threshold);\n Promise.resolve()\n .then(() => onRefresh())\n .catch(() => undefined)\n .finally(() => {\n setIsRefreshing(false);\n setPullDistance(0);\n });\n } else {\n setPullDistance(0);\n }\n };\n\n // preventDefault \uB97C \uC4F0\uBBC0\uB85C touchmove \uB294 passive: false \uB85C \uB4F1\uB85D\uD574\uC57C \uD55C\uB2E4.\n // \uB2E8 \uB9E8 \uC704\uC5D0 \uC788\uC744 \uB54C\uB9CC \uAC74\uB2E4(1.7.34) \u2014 passive: false \uB9AC\uC2A4\uB108\uAC00 \uC788\uC73C\uBA74 \uBE0C\uB77C\uC6B0\uC800\uB294 \uD130\uCE58\uB97C \uC2DC\uC791\uD560 \uB54C\uB9C8\uB2E4\n // \uBA54\uC778 \uC2A4\uB808\uB4DC\uAC00 \uADF8 \uB9AC\uC2A4\uB108\uB97C \uB3CC\uB9B4 \uB54C\uAE4C\uC9C0 \uC2A4\uD06C\uB864\uC744 \uBD99\uC7A1\uB294\uB2E4. \uBAA9\uB85D \uC911\uAC04\uC5D0\uC11C \uBA54\uC778 \uC2A4\uB808\uB4DC\uAC00 \uC7A0\uAE50 \uBC14\uC058\uBA74\n // \uC2A4\uD06C\uB864\uC774 \uBA48\uCDC4\uB2E4\uAC00 \uADF8\uB3D9\uC548 \uBBFC \uAC70\uB9AC\uB9CC\uD07C \uD55C \uBC88\uC5D0 \uD280\uC5C8\uB2E4(\uC548\uB4DC\uB85C\uC774\uB4DC \uC571 \uBA54\uC77C \uBAA9\uB85D, 2026-10-19 \u2014 117px).\n // \uB2F9\uAE40\uC740 \uB9E8 \uC704\uC5D0\uC11C \uC2DC\uC791\uD55C \uD130\uCE58\uB9CC \uC4F0\uBBC0\uB85C, \uB0B4\uB824\uAC00 \uC788\uB294 \uB3D9\uC548\uC740 \uB9AC\uC2A4\uB108\uB97C \uB5BC\uC5B4 \uC2A4\uD06C\uB864\uC744 \uD569\uC131 \uCABD\uC5D0 \uB9E1\uAE34\uB2E4.\n let moveAttached = false;\n /** \uB2F9\uAE40\uC6A9 touchmove \uB9AC\uC2A4\uB108\uB97C \uAC74\uB2E4 */\n const attachMove = () => {\n if (moveAttached) return;\n container.addEventListener(\"touchmove\", handleTouchMove, {\n passive: false,\n });\n moveAttached = true;\n };\n /** \uB2F9\uAE40\uC6A9 touchmove \uB9AC\uC2A4\uB108\uB97C \uB5C0\uB2E4 */\n const detachMove = () => {\n if (!moveAttached) return;\n container.removeEventListener(\"touchmove\", handleTouchMove);\n moveAttached = false;\n };\n /** \uC2A4\uD06C\uB864 \uC704\uCE58\uC5D0 \uB9DE\uCDB0 \uAC74\uB2E4 \u00B7 \uB5C0\uB2E4 \u2014 \uC138\uB85C\uB85C \uAD74\uB7EC\uAC00\uB294 \uC0C1\uC790(\uC548\uCABD \uAC00\uC0C1 \uBAA9\uB85D \uD3EC\uD568)\uB9CC \uBCF8\uB2E4 */\n const syncMove = (e: Event) => {\n const t = e.target;\n if (!(t instanceof HTMLElement)) return;\n if (t.scrollHeight <= t.clientHeight + 1) return; // \uAC00\uB85C \uC2A4\uD06C\uB864 \uB4F1\n if (t.scrollTop <= 0) attachMove();\n else if (!trackingRef.current) detachMove();\n };\n attachMove();\n container.addEventListener(\"scroll\", syncMove, {\n passive: true,\n capture: true,\n });\n container.addEventListener(\"touchstart\", handleTouchStart, {\n passive: true,\n });\n container.addEventListener(\"touchend\", handleTouchEnd, {\n passive: true,\n });\n container.addEventListener(\"touchcancel\", handleTouchEnd, {\n passive: true,\n });\n\n return () => {\n container.removeEventListener(\"touchstart\", handleTouchStart);\n detachMove();\n container.removeEventListener(\"scroll\", syncMove, { capture: true });\n container.removeEventListener(\"touchend\", handleTouchEnd);\n container.removeEventListener(\"touchcancel\", handleTouchEnd);\n };\n }, [containerRef, enabled, onRefresh, threshold, maxDistance]);\n\n return {\n pullDistance,\n isRefreshing,\n pullProgress: Math.min(pullDistance / threshold, 1),\n pullThreshold: threshold,\n };\n};\n", "/**\n * useCoarsePointer.ts\n * \uC8FC \uC785\uB825\uC7A5\uCE58\uAC00 \uD130\uCE58(coarse pointer)\uC778\uC9C0 \uAC10\uC9C0\uD558\uB294 \uD6C5.\n *\n * \uD130\uCE58 \uC6B0\uC120 \uAE30\uAE30(\uBAA8\uBC14\uC77C/\uD0DC\uBE14\uB9BF)\uC5D0\uC11C\uB294 \uCEE4\uC2A4\uD140 \uC2A4\uD06C\uB864\uBC14 \uD2B8\uB799/thumb\u00B7\uB4DC\uB798\uADF8 \uC2A4\uD06C\uB864\uC774 \uBD88\uD544\uC694\uD558\uACE0\n * \uC624\uD788\uB824 \uB124\uC774\uD2F0\uBE0C \uC2A4\uD06C\uB864\u00B7PTR \uC81C\uC2A4\uCC98\uC640 \uCDA9\uB3CC\uD560 \uC218 \uC788\uC5B4, \uC774 \uAC12\uC73C\uB85C \uCEE4\uC2A4\uD140 UI \uB97C \uB044\uB294 \uD310\uB2E8\uC5D0 \uC4F4\uB2E4.\n * `pointer: coarse` \uBBF8\uB514\uC5B4\uCFFC\uB9AC \uAE30\uC900(\uB9C8\uC6B0\uC2A4=fine \uB294 false). SSR \uB4F1 matchMedia \uBBF8\uC9C0\uC6D0 \uD658\uACBD\uC740 false.\n *\n * @copyright 2025 KIM YOUNG JIN (ehfuse@gmail.com)\n */\n\nimport { useEffect, useState } from \"react\";\n\n/** \uC8FC \uC785\uB825\uC7A5\uCE58\uAC00 \uD130\uCE58(coarse pointer)\uBA74 true \uB97C \uBC18\uD658\uD55C\uB2E4. */\nexport function useCoarsePointer(): boolean {\n const [coarse, setCoarse] = useState<boolean>(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return false;\n }\n return window.matchMedia(\"(pointer: coarse)\").matches;\n });\n\n useEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n const mql = window.matchMedia(\"(pointer: coarse)\");\n /** \uBBF8\uB514\uC5B4\uCFFC\uB9AC \uB9E4\uCE58 \uBCC0\uD654\uB97C \uC0C1\uD0DC\uC5D0 \uBC18\uC601\uD55C\uB2E4. */\n const handleChange = (event: MediaQueryListEvent) => setCoarse(event.matches);\n // \uCD08\uAE30\uAC12 \uB3D9\uAE30\uD654(\uB9C8\uC6B4\uD2B8 \uC2DC\uC810\uC758 \uC2E4\uC81C \uAC12\uC73C\uB85C \uBCF4\uC815).\n setCoarse(mql.matches);\n // \uAD6C\uD615 \uC0AC\uD30C\uB9AC \uD638\uD658: addEventListener \uAC00 \uC5C6\uC73C\uBA74 addListener \uB85C \uD3F4\uBC31.\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", handleChange);\n return () => mql.removeEventListener(\"change\", handleChange);\n }\n mql.addListener(handleChange);\n return () => mql.removeListener(handleChange);\n }, []);\n\n return coarse;\n}\n"],
5
+ "mappings": "AAwBA,OACI,UAAAA,EACA,aAAAC,EACA,YAAAC,EACA,eAAAC,EACA,WAAAC,GAEA,cAAAC,GACA,uBAAAC,GACA,mBAAAC,GACA,SAAAC,OACG,QCRP,IAAMC,GAA0B,CAE5B,SACA,WACA,QACA,SACA,YACA,eACA,kBAGA,qBACA,mBACA,yBACA,uBACA,iBACA,sBACA,oBACA,iBACA,wBACA,sBACA,gBACA,wBACA,sBACA,kBACA,iBACA,kBACA,iBACA,sBACA,iBACA,yBACA,uBACA,qBACA,iBACA,qBACA,sBACA,mBACA,gBACA,iBACA,yBAGA,YACA,0BACA,wBACA,mBACA,2BACA,aACA,sBACA,8BACA,sBACA,eACA,qBACA,aACA,mBACA,kBACA,sBACA,aACA,mBACA,oBACA,aACA,eACA,uBACA,YACA,oBACA,WACA,aACA,kBACA,gBACA,wBACA,UACA,eACA,uBACA,WACA,gBACA,cACA,cACA,YACA,aACA,kBACA,oBACA,eACA,eAGA,WACA,cACA,YACA,oBACA,oBACA,iBACA,YACA,cACA,iBACA,YACA,YACA,kBACA,cACA,iBACA,cACA,aACA,mBACA,aACA,YACA,WACA,mBACA,kBACA,aACA,qBACA,UACA,kBACA,eACA,gBACA,WAEA,WACA,eACA,iBACA,eACA,gBACA,kBACA,aACA,eACA,eACA,iBACA,cAGA,YACA,eACA,aACA,YACA,kBACA,oBACA,aACA,UACA,YACA,QACA,cAGA,gBACA,2BACA,aACA,2BACA,4BAGA,aACA,kBACA,mBACA,oBACA,mBACA,YACA,aACA,aAGA,uBACA,oBACA,gBACA,cACA,mBAGA,YACA,aACA,aACA,sBACA,YAGA,eACA,gBAGA,mBACA,8BACA,6BAGA,gBACA,wBACA,wBACA,iBACA,uBACA,gBAGA,cACA,mBACA,UACA,aACA,aACJ,EAQaC,GAAqB,CAC9BC,EACAC,IACU,CAOV,GALID,aAAmB,aAAeA,EAAQ,mBAK1CA,EAAQ,QAAQ,qCAAqC,EACrD,MAAO,GAGX,IAAME,EAAUF,EAAQ,QAAQ,YAAY,EAG5C,GAAIE,IAAY,QAAS,CACrB,IAAMC,GACDH,EAA6B,MAAQ,QACxC,YAAY,EAQd,MAAO,CAPuC,CAC1C,SACA,SACA,QACA,QACA,QACJ,EAC8C,SAASG,CAAI,CAC/D,CAuBA,GApBI,CAAC,WAAY,SAAU,QAAQ,EAAE,SAASD,CAAO,GAMjD,CACI,MACA,OACA,SACA,OACA,OACA,UACA,UACJ,EAAE,SAASA,CAAO,GAMlBF,EAAQ,aAAa,iBAAiB,IAAM,OAC5C,MAAO,GAIX,GAAIC,GAAQ,kBACR,QAAWG,KAAYH,EAAO,iBAC1B,GAAID,EAAQ,QAAQI,CAAQ,EACxB,MAAO,GAKnB,OAAOC,GAAuBL,EAASC,CAAM,CACjD,EAKMI,GAAyB,CAC3BL,EACAC,IACU,CAGV,GAAID,EAAQ,QAAQ,0BAA0B,EAC1C,MAAO,GAIX,IAAMM,EAAoB,CACtB,GAAGR,GACH,GAAIG,GAAQ,gBAAkB,CAAC,CACnC,EAEIM,EAAiCP,EACjCQ,EAAQ,EACNC,EAAW,EAEjB,KAAOF,GAAkBC,GAASC,GAAU,CAExC,GACIH,EAAkB,KAAMI,GACpBH,EAAgB,UAAU,SAASG,CAAG,CAC1C,EAEA,MAAO,GAIX,GAAIH,EAAe,UAAU,SAAS,uBAAuB,EACzD,MAGJA,EAAiBA,EAAe,cAChCC,GACJ,CAEA,MAAO,EACX,ECxUO,IAAMG,GACTC,GACgB,CAChB,GAA2BA,GAAU,KAAM,OAAO,KAClD,GAAI,OAAOA,GAAU,SAAU,OAAO,OAAO,SAASA,CAAK,EAAIA,EAAQ,KAEvE,IAAMC,EAAUD,EAAM,KAAK,EAC3B,GAAIC,IAAY,GAAI,OAAO,KAG3B,IAAMC,EAAQ,uBAAuB,KAAKD,CAAO,EACjD,GAAI,CAACC,EAAO,OAAO,KAEnB,IAAMC,EAAS,OAAO,WAAWD,EAAM,CAAC,CAAC,EACzC,OAAO,OAAO,SAASC,CAAM,EAAIA,EAAS,IAC9C,EAGMC,GACFC,GAC8B,CAC9B,GAA6BA,GAAY,KAAM,OAE/C,GAAI,OAAOA,GAAY,SAAU,OAAOA,EAExC,IAAMC,EAAQD,EAAQ,KAAK,EAAE,MAAM,KAAK,EACxC,GAAI,EAAAC,EAAM,SAAW,GAAKA,EAAM,CAAC,IAAM,IAGvC,OADIA,EAAM,SAAW,GACjBA,EAAM,SAAW,EAAUA,EAAM,CAAC,EAC/BA,EAAM,CAAC,CAClB,EAGMC,GACFF,GACS,CACT,IAAMC,EACF,OAAOD,GAAY,SACb,CAAC,GAAGA,CAAO,IAAI,EACfA,EAAQ,KAAK,EAAE,MAAM,KAAK,EAG9B,CAACG,EAAKC,EAAQD,EAAKE,EAASF,EAAKG,EAAOF,CAAK,EAAIH,EAEvD,MAAO,GAAGE,CAAG,IAAIC,CAAK,MAAME,CAAI,EACpC,EAYaC,GACTC,GACyB,CACzB,GAAI,CAACA,EAAO,MAAO,CAAE,MAAAA,EAAO,aAAc,CAAE,EAE5C,IAAMC,EAAcD,EAAM,gBAAkB,OACtCE,EAAeF,EAAM,UAAY,OACvC,GAAI,CAACC,GAAe,CAACC,EAAc,MAAO,CAAE,MAAAF,EAAO,aAAc,CAAE,EAGnE,IAAMG,EAAYF,EACZD,EAAM,cACNT,GAA2BS,EAAM,OAAO,EAExCI,EAASlB,GAAciB,CAAwC,EAErE,GAAIC,IAAW,MAAQA,GAAU,EAAG,MAAO,CAAE,MAAAJ,EAAO,aAAc,CAAE,EAEpE,IAAMK,EAAiC,CAAE,GAAGL,CAAM,EAClD,OAAIE,GAAgBF,EAAM,UAAY,SAClCK,EAAU,QAAUX,GAChBM,EAAM,OACV,GAEAC,IACAI,EAAU,cAAgB,GAGvB,CAAE,MAAOA,EAAW,aAAcD,CAAO,CACpD,ECzFA,OAAS,aAAAE,GAAW,UAAAC,GAAQ,YAAAC,OAA2B,QAgBhD,IAAMC,GAAmB,CAAC,CAC7B,aAAAC,EACA,OAAAC,CACJ,IAAqD,CAEjD,IAAMC,EAAUD,EAAO,SAAW,GAC5BE,EAAYF,EAAO,WAAa,GAChCG,EAAcH,EAAO,aAAeE,EAAY,EAChDE,EAAYJ,EAAO,UAEnB,CAACK,EAAcC,CAAe,EAAIT,GAAS,CAAC,EAC5C,CAACU,EAAcC,CAAe,EAAIX,GAAS,EAAK,EAGhDY,EAAYb,GAAO,CAAC,EACpBc,GAAYd,GAAO,CAAC,EACpBe,EAAcf,GAAO,EAAK,EAC1BgB,EAAahB,GAAO,EAAK,EACzBiB,GAAkBjB,GAAO,EAAK,EAC9BkB,EAAkBlB,GAAO,CAAC,EAC1BmB,GAAgBnB,GAAO,EAAK,EAElC,OAAAD,GAAU,IAAM,CACZoB,GAAc,QAAUR,CAC5B,EAAG,CAACA,CAAY,CAAC,EAEjBZ,GAAU,IAAM,CACZ,IAAMqB,EAAYjB,EAAa,QAE/B,GAAI,CAACiB,GAAa,EADHf,GAAW,OAAOG,GAAc,YACpB,OAI3B,IAAMa,GAAoBC,GAAwC,CAC9D,IAAIC,EAAKD,aAAkB,QAAUA,EAAS,KAC9C,KAAOC,GAAMA,IAAOH,GAAW,CAC3B,GACIG,aAAc,aACdA,EAAG,aAAeA,EAAG,aAAe,GACpCA,EAAG,UAAY,EAEf,MAAO,GAEXA,EAAKA,EAAG,aACZ,CACA,MAAO,EACX,EAGMC,GAAoBC,GAAkB,CAOxCV,EAAY,QAAU,GACtBC,EAAW,QAAU,GACrBC,GAAgB,QAAU,GAEtB,CAAAE,GAAc,SACdM,EAAE,QAAQ,SAAW,GAKrBL,EAAU,YAAc,GACvBC,GAAiBI,EAAE,MAAM,IAC9BV,EAAY,QAAU,GACtBF,EAAU,QAAUY,EAAE,QAAQ,CAAC,EAAE,QACjCX,GAAU,QAAUW,EAAE,QAAQ,CAAC,EAAE,QACrC,EAGMC,GAAmBD,GAAkB,CAEvC,GADI,CAACV,EAAY,SAAWI,GAAc,SACtCM,EAAE,QAAQ,SAAW,EAAG,OAE5B,IAAME,EAAKF,EAAE,QAAQ,CAAC,EAAE,QAAUZ,EAAU,QACtCe,GAAKH,EAAE,QAAQ,CAAC,EAAE,QAAUX,GAAU,QAE5C,GAAI,CAACE,EAAW,QAAS,CAErB,GAAIW,GAAM,GAAKP,EAAU,UAAY,EAAG,CACpCL,EAAY,QAAU,GACtB,MACJ,CAEA,GAAIY,EAAK,GAAKA,EAAK,KAAK,IAAIC,EAAE,EAC1BZ,EAAW,QAAU,GAMrBC,GAAgB,QAAUG,EAAU,UAAY,MAEhD,OAER,CAII,CAACH,GAAgB,SAAWQ,EAAE,YAAYA,EAAE,eAAe,EAC/D,IAAMI,GAAW,KAAK,IAAI,KAAK,IAAIF,EAAI,CAAC,EAAI,GAAKpB,CAAW,EAC5DW,EAAgB,QAAUW,GAC1BnB,EAAgBmB,EAAQ,CAC5B,EAGMC,EAAiB,IAAM,CAIzB,GAHI,CAACf,EAAY,UACjBA,EAAY,QAAU,GAElB,CAACC,EAAW,SAAS,OACzBA,EAAW,QAAU,GACrBC,GAAgB,QAAU,GAE1B,IAAMY,EAAWX,EAAgB,QACjCA,EAAgB,QAAU,EAEtBW,GAAYvB,GAAa,CAACa,GAAc,SAExCP,EAAgB,EAAI,EACpBF,EAAgBJ,CAAS,EACzB,QAAQ,QAAQ,EACX,KAAK,IAAME,EAAU,CAAC,EACtB,MAAM,IAAG,EAAY,EACrB,QAAQ,IAAM,CACXI,EAAgB,EAAK,EACrBF,EAAgB,CAAC,CACrB,CAAC,GAELA,EAAgB,CAAC,CAEzB,EAOIqB,EAAe,GAEbC,GAAa,IAAM,CACjBD,IACJX,EAAU,iBAAiB,YAAaM,GAAiB,CACrD,QAAS,EACb,CAAC,EACDK,EAAe,GACnB,EAEME,GAAa,IAAM,CAChBF,IACLX,EAAU,oBAAoB,YAAaM,EAAe,EAC1DK,EAAe,GACnB,EAEMG,GAAYT,GAAa,CAC3B,IAAMU,EAAIV,EAAE,OACNU,aAAa,cACfA,EAAE,cAAgBA,EAAE,aAAe,IACnCA,EAAE,WAAa,EAAGH,GAAW,EACvBjB,EAAY,SAASkB,GAAW,GAC9C,EACA,OAAAD,GAAW,EACXZ,EAAU,iBAAiB,SAAUc,GAAU,CAC3C,QAAS,GACT,QAAS,EACb,CAAC,EACDd,EAAU,iBAAiB,aAAcI,GAAkB,CACvD,QAAS,EACb,CAAC,EACDJ,EAAU,iBAAiB,WAAYU,EAAgB,CACnD,QAAS,EACb,CAAC,EACDV,EAAU,iBAAiB,cAAeU,EAAgB,CACtD,QAAS,EACb,CAAC,EAEM,IAAM,CACTV,EAAU,oBAAoB,aAAcI,EAAgB,EAC5DS,GAAW,EACXb,EAAU,oBAAoB,SAAUc,GAAU,CAAE,QAAS,EAAK,CAAC,EACnEd,EAAU,oBAAoB,WAAYU,CAAc,EACxDV,EAAU,oBAAoB,cAAeU,CAAc,CAC/D,CACJ,EAAG,CAAC3B,EAAcE,EAASG,EAAWF,EAAWC,CAAW,CAAC,EAEtD,CACH,aAAAE,EACA,aAAAE,EACA,aAAc,KAAK,IAAIF,EAAeH,EAAW,CAAC,EAClD,cAAeA,CACnB,CACJ,EClNA,OAAS,aAAA8B,GAAW,YAAAC,OAAgB,QAG7B,SAASC,IAA4B,CACxC,GAAM,CAACC,EAAQC,CAAS,EAAIH,GAAkB,IACtC,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WACvD,GAEJ,OAAO,WAAW,mBAAmB,EAAE,OACjD,EAED,OAAAD,GAAU,IAAM,CACZ,GAAI,OAAO,OAAW,KAAe,OAAO,OAAO,YAAe,WAC9D,OAEJ,IAAMK,EAAM,OAAO,WAAW,mBAAmB,EAE3CC,EAAgBC,GAA+BH,EAAUG,EAAM,OAAO,EAI5E,OAFAH,EAAUC,EAAI,OAAO,EAEjB,OAAOA,EAAI,kBAAqB,YAChCA,EAAI,iBAAiB,SAAUC,CAAY,EACpC,IAAMD,EAAI,oBAAoB,SAAUC,CAAY,IAE/DD,EAAI,YAAYC,CAAY,EACrB,IAAMD,EAAI,eAAeC,CAAY,EAChD,EAAG,CAAC,CAAC,EAEEH,CACX,CJ4qDgC,cAAAK,EAwDZ,QAAAC,OAxDY,oBA5kDhC,IAAMC,GAAoC,CAAC,EACrCC,GAAoC,CAAC,EACrCC,GAAsC,CAAC,EACvCC,GAA+C,CAAC,EAChDC,GAA2C,CAAC,EAC5CC,GAAsD,CAAC,EAEvDC,GAAmBC,GACrB,CACI,CACI,UAAAC,EAAY,GACZ,MAAAC,EAAQ,CAAC,EACT,eAAAC,EAAiB,CAAC,EAClB,aAAAC,EAAe,CAAC,EAChB,SAAAC,EACA,SAAAC,EAGA,MAAAC,EAAQd,GACR,MAAAe,EAAQd,GACR,OAAAe,EAASd,GACT,WAAAe,EAAad,GACb,SAAAe,EAAWd,GACX,cAAAe,GAAgBd,GAGhB,cAAee,EAAoB,GACnC,wBAAyBC,EAA8B,GACvD,kBAAAC,GAAoB,GACpB,aAAAC,EAAe,EACnB,EACAC,KACC,CAKD,IAAMC,EAAkBC,GAAiB,EAEnCC,GAAgBP,EAChBQ,GAA0BH,EAAkB,GAAQJ,EAMpDQ,GAAgB,MADAC,GAAM,EACc,QAAQ,kBAAmB,EAAE,CAAC,GAGlEC,EAAaR,IAAiB,IAASA,GAAgB,KACvDS,EAAY,OAAOT,GAAiB,SAAWA,EAAe,CAAC,EAC/DU,GAAeD,EAAU,WAAa,IACtCE,GAAiBF,EAAU,aAAeA,EAAU,kBAAoB,IACxEG,GAAeH,EAAU,WAAa,KACtCI,EAAcJ,EAAU,UAAY,QACpCK,EAAYL,EAAU,QAAU,GAEhCM,GAAkBN,EAAU,cAAgBK,EAC5CE,GAAgBP,EAAU,YAAc,UACxCQ,GAAWR,EAAU,OAAS,UAC9BS,GAAUT,EAAU,MAAQ,GAE5B,CAACU,GAAkBC,EAAmB,EAAIC,EAAS,EAAK,EAExD,CAACC,GAAcC,EAAe,EAAIF,EAAS,EAAK,EAEhDG,GAAkBC,EAA6C,IAAI,EAGnEC,GAAeD,EAUlB,CAAC,CAAC,EAGLE,EAAU,IAAM,CAEZD,GAAa,QAAU,CACnB,SAAArC,EACA,SAAAC,EACA,cAAAc,GACA,wBAAAC,GACA,MAAAd,EACA,MAAAC,EACA,OAAAC,EACA,WAAAC,EACA,SAAAC,CACJ,CACJ,CAAC,EAED,IAAMiC,GAAaH,EAAuB,IAAI,EACxCI,EAAeJ,EAAuB,IAAI,EAC1CK,GAAaL,EAAuB,IAAI,EACxCM,GAAeN,EAAuB,IAAI,EAC1CO,GAAWP,EAAuB,IAAI,EAGtCQ,EAA2BR,EAA2B,IAAI,EAG1D,CACF,aAAAS,GACA,aAAAC,GACA,aAAAC,EACJ,EAAIC,GAAiB,CAAE,aAAAR,EAAc,OAAQjC,EAAc,CAAC,EACtD0C,GAAqB1C,GAAc,gBAAkB,UACrD2C,GAAuBL,GAAe,GAAKC,GAG3C,CAACK,GAAkBC,CAAmB,EAAIpB,EAAS,EAAK,EACxD,CAACqB,EAAYC,EAAa,EAAItB,EAAS,EAAK,EAC5C,CAACuB,GAAgBC,EAAiB,EAAIxB,EAAS,EAAK,EACpD,CAACyB,GAAWC,EAAY,EAAI1B,EAAS,CAAE,EAAG,EAAG,UAAW,CAAE,CAAC,EAC3D,CAAC2B,GAAaC,EAAc,EAAI5B,EAAS,CAAC,EAC1C,CAAC6B,GAAUC,EAAW,EAAI9B,EAAS,CAAC,EACpC,CAAC+B,GAAsBC,EAAuB,EAAIhC,EAAS,EAAK,EAGhE,CAACiC,GAA0BC,EAA2B,EACxDlC,EAAS,EAAK,EACZ,CAACmC,EAAsBC,EAAuB,EAAIpC,EAAS,EAAK,EAChE,CAACqC,GAAqBC,EAAsB,EAAItC,EAAS,CAC3D,EAAG,EACH,WAAY,CAChB,CAAC,EACK,CAACuC,GAAYC,EAAa,EAAIxC,EAAS,CAAC,EACxC,CAACyC,GAAWC,EAAY,EAAI1C,EAAS,CAAC,EACtC,CACF2C,GACAC,EACJ,EAAI5C,EAAS,EAAK,EAGZ6C,GAAyBzC,EAAO,EAAK,EACrC,CAAC0C,GAAiBC,EAAkB,EAAI/C,EAAS,EAAK,EAGtDgD,GAAqB5C,EAAO,EAAK,EACjC,CAAC6C,GAAiBC,EAAkB,EAAIlD,EAAS,CACnD,EAAG,EACH,EAAG,EACH,UAAW,EACX,WAAY,CAChB,CAAC,EACK,CAACmD,GAAaC,EAAc,EAAIpD,EAClC,IACJ,EACM,CAACqD,GAAcC,EAAe,EAAItD,EACpC,IACJ,EAGM,CAACuD,GAAeC,EAAgB,EAAIxD,EAAS,EAAK,EAGlD,CAACyD,GAAsBC,EAAuB,EAAI1D,EAAS,CAAC,EAG5D2D,GAAkBvD,EACpB,IACJ,EAGMwD,GAAsBxD,EAAO,EAAK,EAGlCyD,GAAiBzD,EACnB,IACJ,EAGM0D,GAAuB1D,EAEnB,IAAI,EAGR2D,GAAsB3D,EAElB,IAAI,EAGR4D,EAAmBC,GAAQ,IAAM,CACnC,IAAMC,EAAYhG,EAAM,OAAS,UACjC,MAAO,CACH,MAAOA,EAAM,OAAS,EACtB,UAAWA,EAAM,WAAa,GAC9B,OAAQA,EAAM,SAAWA,EAAM,OAAS,GAAK,EAC7C,MAAOgG,EACP,QAAShG,EAAM,SAAW,GAC1B,WAAYA,EAAM,YAAcgG,EAChC,aAAchG,EAAM,cAAgB,CACxC,CACJ,EAAG,CAACA,CAAK,CAAC,EAEJiG,EAAmBF,GACrB,KAAO,CACH,MAAO9F,EAAM,OAAS,GACtB,MAAOA,EAAM,OAAS,2BACtB,QAASA,EAAM,SAAW,GAC1B,UAAWA,EAAM,WAAa,UAC9B,OAAQA,EAAM,QAAU6F,EAAiB,QAAU,EACnD,OAAQ7F,EAAM,QAAU,EACxB,UAAWA,EAAM,WAAa,GAC9B,UAAWA,EAAM,WAAa,EAClC,GACA,CAACA,EAAO6F,EAAiB,MAAM,CACnC,EAEMI,EAAoBH,GAAQ,IAAM,CACpC,IAAMC,EAAY9F,EAAO,OAAS,UAClC,MAAO,CACH,QAASA,EAAO,SAAW,GAC3B,KAAMA,EAAO,MAAQ,GACrB,MAAO8F,EACP,QAAS9F,EAAO,SAAW,GAC3B,WAAYA,EAAO,YAAc8F,EACjC,aAAc9F,EAAO,cAAgB,CACzC,CACJ,EAAG,CAACA,CAAM,CAAC,EAELiG,GAAwBJ,GAC1B,KAAO,CAEH,QAASpF,EAAkB,GAAQR,EAAW,SAAW,GACzD,eAAgBA,EAAW,gBAAkB,CAAC,EAC9C,iBAAkBA,EAAW,kBAAoB,CAAC,CACtD,GACA,CAACA,EAAYQ,CAAe,CAChC,EAEMyF,EAAsBL,GACxB,KAAO,CACH,QAAS3F,EAAS,SAAW,GAC7B,MAAOA,EAAS,OAAS,KACzB,aAAcA,EAAS,cAAgB,IACvC,aAAcA,EAAS,cAAgB,GAC3C,GACA,CAACA,CAAQ,CACb,EAGM,CAACiG,GAAsBC,EAAuB,EAAIxE,EACpD,KAAO1B,EAAS,cAAgB,KAAO,CAC3C,EAGMmG,EAAkBT,EAAiB,MACnCU,GAAkBP,EAAiB,MACnCQ,GAAiBX,EAAiB,UAClCY,EAAaR,EAAkB,QAC/BS,GAAYT,EAAkB,KAG9BU,EAAgBC,EAAY,IAAM,CACpC,GAAI,CAACvE,EAAa,QAAS,OAG3B,IAAMwE,EAAgB,SAAS,cAI3BA,GACAxE,EAAa,QAAQ,SAASwE,CAAa,GAC3CA,IAAkBxE,EAAa,SAMnCA,EAAa,QAAQ,MAAM,CAC/B,EAAG,CAAC,CAAC,EAGLyE,GACIrG,GACA,KAAO,CACH,mBAAoB,IAAM4B,EAAa,QACvC,SAAW0E,GAA6B,CAChC1E,EAAa,SACbA,EAAa,QAAQ,SAAS0E,CAAO,CAE7C,EACA,IAAI,WAAY,CACZ,OAAO1E,EAAa,SAAS,WAAa,CAC9C,EACA,IAAI,cAAe,CACf,OAAOA,EAAa,SAAS,cAAgB,CACjD,EACA,IAAI,cAAe,CACf,OAAOA,EAAa,SAAS,cAAgB,CACjD,CACJ,GACA,CAAC,CACL,EAGA,IAAM2E,EAAwBJ,EAAY,IAA0B,CAEhE,GAAInE,EAAyB,QAAS,CAClC,IAAMwE,EAASxE,EAAyB,QAExC,GACI,SAAS,SAASwE,CAAM,IACvBA,EAAO,aAAeA,EAAO,aAAe,GACzCA,EAAO,YAAcA,EAAO,YAAc,GAE9C,OAAOA,EAGXxE,EAAyB,QAAU,IACvC,CAEA,GAAI,CAACJ,EAAa,QACd,OAAO,KAIX,GACIA,EAAa,QAAQ,aACjBA,EAAa,QAAQ,aAAe,GACxCA,EAAa,QAAQ,YACjBA,EAAa,QAAQ,YAAc,EAEvC,OAAAI,EAAyB,QAAUJ,EAAa,QACzCA,EAAa,QAKxB,GAAI,CAAC9B,GACD,OAAO,KAKX,IAAM2G,EACF7E,EAAa,QAAQ,iBACjB,wHACJ,EAEJ,QAAW8E,KAASD,EAAyB,CACzC,IAAME,EAAUD,EAIhB,GACIC,IAAY/E,EAAa,SACzB+E,EAAQ,UAAU,SAAS,6BAA6B,EAGxD,SAIJ,IAAIC,EAA6BD,EAAQ,cACrCE,EAA6B,GAEjC,KAAOD,GAAUA,IAAWhF,EAAa,SAAS,CAC9C,GACIgF,EAAO,UAAU,SACb,6BACJ,GACAA,IAAWhF,EAAa,QAC1B,CAEEiF,EAA6B,GAC7B,KACJ,CACAD,EAASA,EAAO,aACpB,CAEA,GAAI,CAAAC,IAMAF,EAAQ,aAAeA,EAAQ,aAAe,GAC9CA,EAAQ,YAAcA,EAAQ,YAAc,GAE5C,OAAA3E,EAAyB,QAAU2E,EAC5BA,CAEf,CAEA,OAAO,IACX,EAAG,CAAC,CAAC,EAGCG,EAAeX,EAAY,IACtBI,EAAsB,IAAM,KACpC,CAACA,CAAqB,CAAC,EAGpBQ,EAAiBZ,EAAY,IAAM,CACjClB,GAAe,UACf,aAAaA,GAAe,OAAO,EACnCA,GAAe,QAAU,KAEjC,EAAG,CAAC,CAAC,EAEC+B,GAAuBb,EAAY,IAAM,CACvCjB,GAAqB,UACrB,aAAaA,GAAqB,OAAO,EACzCA,GAAqB,QAAU,KAEvC,EAAG,CAAC,CAAC,EAEC+B,GAAsBd,EAAY,IAAM,CACtChB,GAAoB,UACpB,aAAaA,GAAoB,OAAO,EACxCA,GAAoB,QAAU,KAEtC,EAAG,CAAC,CAAC,EAGC+B,GAA0Bf,EAAY,IAAM,CAC9Ca,GAAqB,EAGjB,EAAAvE,GACAc,GACAW,IACAD,GAAuB,WAK3B8C,EAAe,EACfvE,EAAoB,EAAK,EAC7B,EAAG,CACCuE,EACAC,GACAvE,EACAc,EACAW,EACJ,CAAC,EAGKiD,EAAehB,EAChBiB,GAAkB,CAEV1B,EAAoB,UAGzBqB,EAAe,EACf9B,GAAe,QAAU,WAAW,IAAM,CACtCzC,EAAoB,EAAK,EACzByC,GAAe,QAAU,IAC7B,EAAGmC,CAAK,EACZ,EACA,CAACL,EAAgBrB,EAAoB,OAAO,CAChD,EAGM2B,EAAkBlB,EAAY,IAAM,CAGtC,IAAMmB,EACFf,EAAsB,GAAK3E,EAAa,QAC5C,GAAI,CAAC0F,EAAmB,CAEpB9E,EAAoB,EAAK,EACzBY,GAAwB,EAAK,EAC7BY,GAAkC,EAAK,EACvC+C,EAAe,EACf,MACJ,CAGI,CAACrB,EAAoB,SAAW,CAACC,KACjCnD,EAAoB,EAAI,EACxBuE,EAAe,GAGnB,IAAMQ,EAAkBD,EAAkB,aACpCE,EAAgBF,EAAkB,aAClCG,EAAYH,EAAkB,UAE9BI,EAAiBJ,EAAkB,YACnCK,EAAeL,EAAkB,YACjCM,EAAaN,EAAkB,WAC/BO,EAAsBL,EAAgBD,EAAkB,EACxDO,EAAwBH,EAAeD,EAAiB,EACxDK,GAAiBF,GAAuBC,EAG9C1E,GAAwByE,CAAmB,EAC3C7D,GAAkC8D,CAAqB,EAGlDC,KACDvF,EAAoB,EAAK,EACzBuE,EAAe,GAInB,IAAIiB,GAA0B,EAC1BC,EAAgB,EACpB,GAAItG,GAAW,QAAS,CACpB,IAAMuG,EAAgB,OAAO,iBACzBvG,GAAW,OACf,EACMwG,EAAa,WAAWD,EAAc,UAAU,GAAK,EAC3DD,EAAgB,WAAWC,EAAc,aAAa,GAAK,EAC3DF,GAA0BG,EAAaF,EACvCnD,GAAwBmD,CAAa,CACzC,CAEA,GAAIJ,EAAqB,CAErB,IAAMO,EAAapC,EACbH,EAAkB,EAAIN,EAAiB,OAAS,EAChDA,EAAiB,OAAS,EAG1B8C,EACFd,EAAkBa,EAAaJ,GAC7BM,GAAcf,EAAkBC,EAChCe,GAAwB,KAAK,IAC/BF,EAAkBC,GAClBvC,EACJ,EAGMyC,GAAmBhB,EAAgBD,EACnCkB,GACFJ,EAAkBE,GAChBG,GACFF,GAAmB,EACZf,EAAYe,GAAoBC,GACjC,EAEVzF,GAAeuF,EAAqB,EACpCrF,GAAYwF,EAAkB,CAClC,MACI1F,GAAe,CAAC,EAChBE,GAAY,CAAC,EAIjB,IAAMyF,GAA4BhB,EAAeD,EACjD,GAAIiB,GAA4B,EAAG,CAC/B,IAAMC,EAAwBlB,EAAiBC,EACzCkB,EAAuB,KAAK,IAC9BnB,EAAiBkB,EACjB,EACJ,EAEME,GACFpB,EAAiBmB,EACfE,GACFJ,GAA4B,EACrBf,EAAae,GACdG,GACA,EAEVlF,GAAciF,CAAoB,EAClC/E,GAAaiF,EAAmB,CACpC,MACInF,GAAc,CAAC,EACfE,GAAa,CAAC,CAEtB,EAAG,CACCyC,EACAQ,EACAf,EACAH,EACAE,GACAL,EAAoB,QACpBC,EACJ,CAAC,EAGKqD,GAAuB7C,EACxB8C,GAA4B,CACzBA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAEtB,IAAMC,EAAwB3C,EAAsB,EAC/C2C,IAILxG,GAAc,EAAI,EAClBI,GAAa,CACT,EAAGmG,EAAM,QACT,UAAWC,EAAsB,SACrC,CAAC,EAEDnC,EAAe,EACfvE,EAAoB,EAAI,EAGxB0D,EAAc,EAClB,EACA,CAACK,EAAuBQ,EAAgBb,CAAa,CACzD,EAGMiD,GAAkBhD,EACnB8C,GAAsB,CACnB,GAAI,CAACxG,EAAY,OAEjB,IAAMyG,EAAwB3C,EAAsB,EACpD,GAAI,CAAC2C,EACD,OAGJ,IAAM3B,EAAkB2B,EAAsB,aAExCV,EADgBU,EAAsB,aACH3B,EAEnC6B,EAASH,EAAM,QAAUpG,GAAU,EACnC4F,EAAwBlB,EAAkBxE,GAC1CsG,EACDD,EAASX,EAAyBD,EAEjCc,EAAe,KAAK,IACtB,EACA,KAAK,IACDd,EACA3F,GAAU,UAAYwG,CAC1B,CACJ,EAEAH,EAAsB,UAAYI,EAClCjC,EAAgB,CACpB,EACA,CACI5E,EACAI,GACAE,GACAsE,EACAd,CACJ,CACJ,EAGMgD,GAAgBpD,EAAY,IAAM,CACpCzD,GAAc,EAAK,EACfoE,EAAa,GACbK,EAAazB,EAAoB,KAAK,CAE9C,EAAG,CAACoB,EAAcK,EAAczB,EAAoB,KAAK,CAAC,EAGpD8D,GAAmBrD,EACpB8C,GAA4B,CACzB,GAAI,CAACnH,GAAa,QACd,OAIJ,IAAM2H,EADY3H,GAAa,QACR,sBAAsB,EACvC4H,EAAST,EAAM,QAAUQ,EAAK,IAE9BP,EAAwB3C,EAAsB,EACpD,GAAI,CAAC2C,EACD,OAGJ,IAAM3B,EAAkB2B,EAAsB,aACxC1B,EAAgB0B,EAAsB,aAGtCI,EADcI,EAASnC,GAEVC,EAAgBD,GAEnC2B,EAAsB,UAAY,KAAK,IACnC,EACA,KAAK,IAAI1B,EAAgBD,EAAiB+B,CAAY,CAC1D,EACAjC,EAAgB,EAEhB7E,EAAoB,EAAI,EACxB2E,EAAazB,EAAoB,KAAK,EAGtCQ,EAAc,CAClB,EACA,CACImB,EACAF,EACAzB,EAAoB,MACpBa,EACAL,CACJ,CACJ,EAGMyD,GAAqBxD,EACtB8C,GAA4B,CAIzB,GAHAA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAElB,CAACrH,EAAa,QAAS,OAE3B,IAAM0H,EAAe,KAAK,IACtB,EACA1H,EAAa,QAAQ,UAAYqE,EACrC,EAEArE,EAAa,QAAQ,UAAY0H,EACjCjC,EAAgB,EAEhB7E,EAAoB,EAAI,EACxB2E,EAAazB,EAAoB,KAAK,EAGtCQ,EAAc,CAClB,EACA,CACImB,EACAF,EACAlB,GACAP,EAAoB,MACpBQ,CACJ,CACJ,EAGM0D,GAAuBzD,EACxB8C,GAA4B,CAIzB,GAHAA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAElB,CAACrH,EAAa,SAAW,CAACC,GAAW,QAAS,OAElD,IAAMgI,EAAYjI,EAAa,QAEzBkI,EADUjI,GAAW,QAEf,aAAegI,EAAU,aAC/BP,EAAe,KAAK,IACtBQ,EACAD,EAAU,UAAY5D,EAC1B,EAEA4D,EAAU,UAAYP,EACtBjC,EAAgB,EAEhB7E,EAAoB,EAAI,EACxB2E,EAAazB,EAAoB,KAAK,EAGtCQ,EAAc,CAClB,EACA,CACImB,EACAF,EACAlB,GACAP,EAAoB,MACpBQ,CACJ,CACJ,EAGM6D,GAAiC5D,EAClC8C,GAA4B,CACzBA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAEtB,IAAMC,EAAwB3C,EAAsB,EAC/C2C,IAIL1F,GAAwB,EAAI,EAC5BE,GAAuB,CACnB,EAAGuF,EAAM,QACT,WAAYC,EAAsB,UACtC,CAAC,EAEDnC,EAAe,EACfvE,EAAoB,EAAI,EAGxB0D,EAAc,EAClB,EACA,CAACK,EAAuBQ,EAAgBb,CAAa,CACzD,EAGM8D,GAA4B7D,EAC7B8C,GAAsB,CACnB,GAAI,CAAC1F,EAAsB,OAE3B,IAAM2F,EAAwB3C,EAAsB,EACpD,GAAI,CAAC2C,EACD,OAGJ,IAAMxB,EAAiBwB,EAAsB,YAEvCe,EADef,EAAsB,YACJxB,EAEjCwC,EAASjB,EAAM,QAAUxF,GAAoB,EAC7CqF,EAAuBpB,EAAiB/D,GACxC0F,EACDa,EAASpB,EAAwBmB,EAEhCE,EAAgB,KAAK,IACvB,EACA,KAAK,IACDF,EACAxG,GAAoB,WAAa4F,CACrC,CACJ,EAEAH,EAAsB,WAAaiB,EACnC9C,EAAgB,CACpB,EACA,CACI9D,EACAE,GACAE,GACA0D,EACAd,CACJ,CACJ,EAGM6D,GAA0BjE,EAAY,IAAM,CAC9C3C,GAAwB,EAAK,EACzBsD,EAAa,GACbK,EAAazB,EAAoB,KAAK,CAE9C,EAAG,CAACoB,EAAcK,EAAczB,EAAoB,KAAK,CAAC,EAGpD2E,GAA6BlE,EAC9B8C,GAA4B,CACzB,GAAI,CAACnH,GAAa,QACd,OAIJ,IAAM2H,EADY3H,GAAa,QACR,sBAAsB,EACvCwI,EAASrB,EAAM,QAAUQ,EAAK,KAE9BP,EAAwB3C,EAAsB,EACpD,GAAI,CAAC2C,EACD,OAGJ,IAAMxB,EAAiBwB,EAAsB,YACvCvB,EAAeuB,EAAsB,YAGrCiB,EADcG,EAAS5C,GAEVC,EAAeD,GAElCwB,EAAsB,WAAa,KAAK,IACpC,EACA,KAAK,IAAIvB,EAAeD,EAAgByC,CAAa,CACzD,EACA9C,EAAgB,EAEhB7E,EAAoB,EAAI,EACxB2E,EAAazB,EAAoB,KAAK,EAGtCQ,EAAc,CAClB,EACA,CACImB,EACAF,EACAzB,EAAoB,MACpBa,EACAL,CACJ,CACJ,EAGMqE,GAAwBpE,EACzB8C,GAA4B,CAQzB,GANI,CAACxD,GAAsB,SAMvBwD,EAAM,iBAAkB,OAG5B,IAAMuB,EAASvB,EAAM,OAMfY,EAAYjI,EAAa,QAQ/B,GAPI,CAACiI,GAAa,CAACA,EAAU,SAASW,CAAM,GAExCC,GAAmBD,EAAQ/E,EAAqB,GAKhDwD,EAAM,SAAW,EAAG,OAExB,IAAM3B,EAAoBf,EAAsB,EAIhD,GAHI,CAACe,GAIDA,EAAkB,cACdA,EAAkB,cACtBA,EAAkB,aACdA,EAAkB,YAEtB,OAEJ2B,EAAM,eAAe,EAGrB,IAAMyB,EAAS,SAAS,cAEpBA,aAAkB,aAClBA,IAAW,SAAS,MACpB,CAACA,EAAO,SAASF,CAAM,GAEvBE,EAAO,KAAK,EAEhBzG,GAAuB,QAAU,GACjCK,GAAmB,CACf,EAAG2E,EAAM,QACT,EAAGA,EAAM,QACT,UAAW3B,EAAkB,UAC7B,WAAYA,EAAkB,YAAc,CAChD,CAAC,CAKL,EACA,CAAC7B,GAAuBgF,GAAoBlE,CAAqB,CACrE,EAGMoE,GAAuBxE,EACxB8C,GAAsB,CACnB,GAAI,CAAChF,GAAuB,SAAW,CAACC,GAAiB,OAEzD,IAAMoD,EAAoBf,EAAsB,EAChD,GAAI,CAACe,EAAmB,OAExB,IAAM4C,EAAS7F,GAAgB,EAAI4E,EAAM,QACnCG,EAAS/E,GAAgB,EAAI4E,EAAM,QAGzC,GAAI,KAAK,IAAIG,CAAM,EAAI,GAAK,KAAK,IAAIc,CAAM,EAAI,EAC3C,OAGAjG,GAAuB,UACvBA,GAAuB,QAAU,GACjCG,GAAmB,QAAU,GAC7BD,GAAmB,EAAI,GAI3B3B,EAAoB,EAAI,EAGxB,IAAM8G,EAAe,KAAK,IACtB,EACA,KAAK,IACDhC,EAAkB,aACdA,EAAkB,aACtBjD,GAAgB,UAAY+E,CAChC,CACJ,EAGMe,EAAgB,KAAK,IACvB,EACA,KAAK,IACD7C,EAAkB,YACdA,EAAkB,YACtBjD,GAAgB,WAAa6F,CACjC,CACJ,EAEA5C,EAAkB,UAAYgC,EAC9BhC,EAAkB,WAAa6C,EAC/B9C,EAAgB,CACpB,EACA,CACInD,GACAG,GACAkC,EACAc,CACJ,CACJ,EAGMuD,GAAsBzE,EAAY,IAAM,CAC1C,IAAM0E,EAAmBzG,GAAmB,QAI5C,GAAIyG,EAAkB,CAClB,IAAMC,EAAW7B,GAAsB,CACnCA,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EACtB,OAAO,oBAAoB,QAAS6B,EAAS,EAAI,CACrD,EACA,OAAO,iBAAiB,QAASA,EAAS,EAAI,EAC9C,OAAO,WAAW,IAAM,OAAO,oBAAoB,QAASA,EAAS,EAAI,EAAG,CAAC,CACjF,CACA7G,GAAuB,QAAU,GACjCG,GAAmB,QAAU,GAC7BD,GAAmB,EAAK,EAEpB0G,GAAoB/D,EAAa,GACjCK,EAAazB,EAAoB,KAAK,CAE9C,EAAG,CAACoB,EAAcK,EAAczB,EAAoB,KAAK,CAAC,EAG1DhE,EAAU,IAAM,CACZ,IAAMqJ,EAAgB9B,GAAiB,CAOnC,GANA5B,EAAgB,EAMZ9G,EAAY,CAEZ,IAAMyK,GADO/B,EAAM,QAA+B,WAAa,GACtCxI,GACzBU,GAAoB6J,CAAU,EAC1BzJ,GAAgB,UAChB,aAAaA,GAAgB,OAAO,EACpCA,GAAgB,QAAU,MAG1ByJ,GAAcrK,GAAe,IAC7BY,GAAgB,QAAU,WAAW,IAAM,CACvCA,GAAgB,QAAU,KAC1BJ,GAAoB,EAAK,CAC7B,EAAGR,EAAY,EAEvB,CAGA,GAAIgF,GAAsB,CAClBtG,GACAA,EAAS4J,CAAK,EAElB,MACJ,CAGAlC,EAAe,EACfvE,EAAoB,EAAI,EAGxByE,GAAoB,EAGpB,IAAMG,EAAQpC,GAAoB,QAC5BU,EAAoB,aACpBA,EAAoB,MAC1ByB,EAAaC,CAAK,EAEd/H,GACAA,EAAS4J,CAAK,CAEtB,EAEMgC,EAAc,IAAM,CAEtBjG,GAAoB,QAAU,GAG1BD,GAAgB,SAChB,aAAaA,GAAgB,OAAO,EAIxCA,GAAgB,QAAU,WAAW,IAAM,CACvCC,GAAoB,QAAU,EAClC,EAAG,GAAG,EAGNiC,GAAoB,EACpB9B,GAAoB,QAAU,WAAW,IAAM,CAC3C3C,EAAoB,EAAI,EACxB2C,GAAoB,QAAU,KAE9BgC,EAAazB,EAAoB,YAAY,CACjD,EAAG,EAAE,CACT,EAEMwF,EAAiC,CAAC,EAGlC5D,EAAoBf,EAAsB,EAC5Ce,GACA4D,EAAgB,KAAK5D,CAAiB,EAI1C,IAAMuC,EAAYjI,EAAa,QAC/B,OAAIiI,GAAa,CAACvC,IACd4D,EAAgB,KAAKrB,CAAS,EAGEA,EAAU,iBACtC,wHACJ,EACwB,QAASnD,GAAU,CACvC,IAAMC,EAAUD,EAGhB,GACIC,IAAYkD,GACZlD,EAAQ,UAAU,SACd,6BACJ,EAEA,OAIJ,IAAIC,EAA6BD,EAAQ,cACzC,KAAOC,GAAUA,IAAWiD,GAAW,CACnC,GACIjD,EAAO,UAAU,SACb,6BACJ,GACAA,IAAWiD,EAEX,OAEJjD,EAASA,EAAO,aACpB,CAEAsE,EAAgB,KAAKvE,CAAO,CAChC,CAAC,GAILuE,EAAgB,QAASvE,GAAY,CACjCA,EAAQ,iBAAiB,SAAUoE,EAAc,CAC7C,QAAS,EACb,CAAC,EACDpE,EAAQ,iBAAiB,QAASsE,EAAa,CAC3C,QAAS,EACb,CAAC,CACL,CAAC,EAEM,IAAM,CAETC,EAAgB,QAASvE,GAAY,CACjCA,EAAQ,oBAAoB,SAAUoE,CAAY,EAClDpE,EAAQ,oBAAoB,QAASsE,CAAW,CACpD,CAAC,CACL,CACJ,EAAG,CACC1E,EACAc,EACAhI,EACA0H,EACAI,EACAzB,EACAC,GACApF,EACAE,GACAE,EACJ,CAAC,EAKDe,EACI,IAAM,IAAM,CACJH,GAAgB,SAChB,aAAaA,GAAgB,OAAO,EAEpCwD,GAAgB,SAChB,aAAaA,GAAgB,OAAO,EAEpCI,GAAoB,SACpB,aAAaA,GAAoB,OAAO,CAEhD,EACA,CAAC,CACL,EAGAzD,EAAU,IAAM,CACZ,IAAMyJ,EAAiBlC,GAAyB,CAC5C,IAAMuB,EAASvB,EAAM,OAKrB,GAJIA,EAAM,kBAKNuB,aAAkB,SAClBC,GAAmBD,EAAQ/E,EAAqB,EAEhD,OAGJ,IAAM6B,EAAoBf,EAAsB,EAChD,GAAI,CAACe,EAAmB,OAExB,GAAM,CAAE,IAAA8D,CAAI,EAAInC,EACV,CAAE,UAAAxB,EAAW,aAAA4D,EAAc,aAAAC,CAAa,EAC1ChE,EACEwC,GAAeuB,EAAeC,EAG9BC,GAAiB,GAEnBjC,EAA8B,KAElC,OAAQ8B,EAAK,CACT,IAAK,UACDnC,EAAM,eAAe,EACrBK,EAAe,KAAK,IAAI,EAAG7B,EAAY8D,EAAc,EACrD,MACJ,IAAK,YACDtC,EAAM,eAAe,EACrBK,EAAe,KAAK,IAChBQ,GACArC,EAAY8D,EAChB,EACA,MACJ,IAAK,SACDtC,EAAM,eAAe,EACrBK,EAAe,KAAK,IAAI,EAAG7B,EAAY6D,CAAY,EACnD,MACJ,IAAK,WACDrC,EAAM,eAAe,EACrBK,EAAe,KAAK,IAChBQ,GACArC,EAAY6D,CAChB,EACA,MACJ,IAAK,OACDrC,EAAM,eAAe,EACrBK,EAAe,EACf,MACJ,IAAK,MACDL,EAAM,eAAe,EACrBK,EAAeQ,GACf,MACJ,QACI,MACR,CAEA,GAAIR,IAAiB,KAAM,CAEvB,IAAMhB,GAAcgB,EAAeQ,GAC7B1B,EAAapC,EACbH,EAAkB,EAAIN,EAAiB,OAAS,EAChDA,EAAiB,OAAS,EAG1BiG,IAFkBF,EAAelD,EACSrF,IACJuF,GAE5CpF,GAAYsI,EAAW,EAGvBlE,EAAkB,UAAYgC,EAG9BvC,EAAe,EACfvE,EAAoB,EAAI,EACxB2E,EAAazB,EAAoB,KAAK,CAC1C,CACJ,EAEMmE,EAAYjI,EAAa,QAC/B,GAAIiI,EACA,OAAAA,EAAU,iBAAiB,UAAWsB,CAAa,EAC5C,IAAM,CACTtB,EAAU,oBAAoB,UAAWsB,CAAa,CAC1D,CAER,EAAG,CACC5E,EACAP,EACAH,EACAN,EAAiB,OACjBxC,GACAgE,EACAI,EACAzB,EAAoB,MACpBD,EACJ,CAAC,EAGD/D,EAAU,KACN,SAAS,iBAAiB,YAAaiJ,EAAoB,EAC3D,SAAS,iBAAiB,UAAWC,EAAmB,EACjD,IAAM,CACT,SAAS,oBAAoB,YAAaD,EAAoB,EAC9D,SAAS,oBAAoB,UAAWC,EAAmB,CAC/D,GACD,CAAC1G,GAAiByG,GAAsBC,EAAmB,CAAC,EAG/DlJ,EAAU,IAAM,CACZ,GAAIe,EACA,gBAAS,iBAAiB,YAAa0G,EAAe,EACtD,SAAS,iBAAiB,UAAWI,EAAa,EAC3C,IAAM,CACT,SAAS,oBAAoB,YAAaJ,EAAe,EACzD,SAAS,oBAAoB,UAAWI,EAAa,CACzD,CAER,EAAG,CAAC9G,EAAY0G,GAAiBI,EAAa,CAAC,EAG/C7H,EAAU,IAAM,CACZ,GAAI6B,EACA,gBAAS,iBACL,YACAyG,EACJ,EACA,SAAS,iBAAiB,UAAWI,EAAuB,EACrD,IAAM,CACT,SAAS,oBACL,YACAJ,EACJ,EACA,SAAS,oBACL,UACAI,EACJ,CACJ,CAER,EAAG,CACC7G,EACAyG,GACAI,EACJ,CAAC,EAGD1I,EAAU,IAAM,CAEZ2F,EAAgB,EAEhB,IAAMoE,EAAQ,WAAW,IAAM,CAC3BpE,EAAgB,CACpB,EAAG,GAAG,EACN,MAAO,IAAM,aAAaoE,CAAK,CACnC,EAAG,CAACpE,CAAe,CAAC,EAGpBqE,GAAgB,IAAM,CAClB9G,GAAiB,EAAI,EAErByC,EAAgB,EAGZ,CAAC3B,EAAoB,SACrB,CAACC,IACDmB,EAAa,GAEbtE,EAAoB,EAAI,CAEhC,EAAG,CACCsE,EACAO,EACA3B,EAAoB,QACpBC,EACJ,CAAC,EAGDjE,EAAU,IAAM,CACZ,GAAIgE,EAAoB,aAAe,EAAG,CACtC,IAAM+F,EAAQ,WAAW,IAAM,CAC3B7F,GAAwB,EAAK,CACjC,EAAGF,EAAoB,YAAY,EACnC,MAAO,IAAM,aAAa+F,CAAK,CACnC,CACJ,EAAG,CAAC/F,EAAoB,YAAY,CAAC,EAIrC,IAAMiG,GAAqBnK,EAAO6F,CAAe,EACjDsE,GAAmB,QAAUtE,EAC7B,IAAMuE,GAAiBpK,EAAsB,IAAI,EAC3CqK,GAAuB1F,EAAY,IAAM,CACvCyF,GAAe,UAAY,OAC/BA,GAAe,QAAU,sBAAsB,IAAM,CACjDA,GAAe,QAAU,KACzBD,GAAmB,QAAQ,CAC/B,CAAC,EACL,EAAG,CAAC,CAAC,EACLjK,EACI,IAAM,IAAM,CACJkK,GAAe,UAAY,OAC3B,qBAAqBA,GAAe,OAAO,EAC3CA,GAAe,QAAU,KAEjC,EACA,CAAC,CACL,EAGAlK,EAAU,IAAM,CACZ,IAAMoK,EAAiB,IAAI,eAAe,IAAM,CAC5CD,GAAqB,CACzB,CAAC,EAEKE,EAAmC,CAAC,EAG1C,OAAInK,EAAa,SACbmK,EAAkB,KAAKnK,EAAa,OAAO,EAE3CC,GAAW,SACXkK,EAAkB,KAAKlK,GAAW,OAAO,EAKzCG,EAAyB,SACzB,SAAS,SAASA,EAAyB,OAAO,GAElD+J,EAAkB,KAAK/J,EAAyB,OAAO,EAI3D+J,EAAkB,QAASpF,GAAY,CACnCmF,EAAe,QAAQnF,CAAO,CAClC,CAAC,EAEM,IAAMmF,EAAe,WAAW,CAC3C,EAAG,CAACzE,EAAiBwE,EAAoB,CAAC,EAG1CnK,EAAU,IAAM,CACZ,GAAI,CAACE,EAAa,QACd,OAGJ,IAAMoK,EAAW,IAAI,iBAAiB,IAAM,CAExChK,EAAyB,QAAU,KACnCqF,EAAgB,CACpB,CAAC,EAED,OAAA2E,EAAS,QAAQpK,EAAa,QAAS,CACnC,UAAW,GACX,QAAS,GACT,WAAY,GACZ,gBAAiB,CAAC,OAAO,CAC7B,CAAC,EAEM,IAAMoK,EAAS,WAAW,CACrC,EAAG,CAAC3E,CAAe,CAAC,EAGpB,IAAM4E,EAAqB,KAAK,IAAInG,GAAiBD,CAAe,EAGpEnE,EAAU,IAAM,CACZ,IAAMwK,EAAU,2BAChB,GAAI,SAAS,eAAeA,CAAO,EAC/B,OAEJ,IAAMjN,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,GAAKiN,EACXjN,EAAM,YAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cA8BpB,SAAS,KAAK,YAAYA,CAAK,CAEnC,EAAG,CAAC,CAAC,EAILyC,EAAU,IAAM,CACZ,IAAMwK,EAAU,8BAA8B7L,EAAa,GACrD8L,EAAW,SAAS,eAAeD,CAAO,EAC5CC,GAAUA,EAAS,OAAO,EAE9B,IAAMlN,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,GAAKiN,EACX,IAAME,EAAM,IAAI/L,EAAa,GAG7B,OAAApB,EAAM,YAAc;AAAA,kBACdmN,CAAG;AAAA;AAAA;AAAA;AAAA,kBAIHA,CAAG;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKHA,CAAG;AAAA,kBACHA,CAAG,0DACT,SAAS,KAAK,YAAYnN,CAAK,EAExB,IAAM,CACT,SAAS,eAAeiN,CAAO,GAAG,OAAO,CAC7C,CACJ,EAAG,CAAC7L,EAAa,CAAC,EAIlB,GAAM,CAAE,yBAAAgM,GAA0B,uBAAAC,GAAwB,mBAAAC,EAAmB,EACzElH,GAAQ,IAAM,CACV,IAAMwE,EAAY2C,GAAqBtN,CAAc,EAC/CuN,EAAUD,GAAqBrN,CAAY,EACjD,MAAO,CACH,yBAA0B0K,EAAU,MACpC,uBAAwB4C,EAAQ,MAChC,mBACI5C,EAAU,aAAe4C,EAAQ,YACzC,CACJ,EAAG,CAACvN,EAAgBC,CAAY,CAAC,EAErC,OACIZ,GAAC,OACG,IAAKoD,GACL,UAAW,6BAA6B3C,CAAS,GACjD,aAAckI,GACd,MAAO,CACH,QAAS,OACT,cAAe,SACf,SAAU,WACV,UAAW,EACX,OAAQ,OACR,KAAM,SACN,SAAU,UACV,GAAGjI,CACP,EAGC,UAAAqD,IACGhE,EAAC,OACG,UAAU,mCACV,MAAO,CACH,SAAU,WACV,IAAK,EACL,KAAM,MACN,UAAW,mBAAmB2D,GAAe,EAAE,MAC/C,WAAYC,GACN,2BACAD,KAAiB,EACf,0BACA,OACR,MAAO,GACP,OAAQ,GACR,aAAc,MACd,WAAY,UACZ,UAAW,gCACX,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,OAAQ,KACR,cAAe,OACf,QAASC,GAAe,EAAI,KAAK,IAAIC,GAAe,IAAK,CAAC,CAC9D,EAEC,SAAAD,GAEG5D,EAAC,OACG,MAAM,KACN,OAAO,KACP,QAAQ,YACR,MAAO,CACH,UACI,iDACR,EAEA,SAAAA,EAAC,UACG,GAAG,KACH,GAAG,KACH,EAAE,IACF,KAAK,OACL,OAAQ+D,GACR,YAAY,MACZ,cAAc,QACd,gBAAgB,KAChB,iBAAiB,KACrB,EACJ,EAGA/D,EAAC,OACG,MAAM,KACN,OAAO,KACP,QAAQ,YACR,MAAO,CACH,UAAW,UAAU6D,GAAe,GAAG,OACvC,QAASA,IAAgB,EAAI,EAAI,EACrC,EAEA,SAAA7D,EAAC,QACG,EAAE,6MACF,KAAM+D,GACV,EACJ,EAER,EAIJ/D,EAAC,OACG,IAAKsD,EAEL,UAAW,+BAA+BvB,EAAa,GACvD,SAAU,GACV,YAAakK,GACb,MAAO,CACH,QAAS,OAKT,MAAO,OACP,KAAM,WACN,UAAW,EACX,SAAU,OAGV,QAAS,OACT,WAAYrG,GAAkB,OAAS,OACvC,GAAGmI,EACP,EAEA,SAAA9N,GAAC,OACG,IAAKsD,GACL,UAAU,4BACV,MAAO,CAGH,KAAM,WACN,QAAS,OACT,cAAe,SACf,UAAW,aACX,GAAGyK,EACP,EAEC,UAAAlN,EACAmN,GAAqB,GAIlBjO,EAAC,OACG,UAAU,kCACV,cAAY,OACZ,MAAO,CAAE,KAAM,WAAY,OAAQiO,EAAmB,EAC1D,GAER,EACJ,EAGCpM,IACGgD,KACCoC,EAAiB,WAAa,KAC3BhH,GAAC,OACG,IAAKuD,GACL,UAAU,0BACV,aAAc,IAAM,CAGZ7B,IAGJ8G,EAAe,EAGf7B,GAAqB,QAAU,WAC3B,IAAM,CACF1C,EAAoB,EAAI,EACxB0C,GAAqB,QAAU,IACnC,EACA,GACJ,EACJ,EACA,aAAc,IAAM,CAEhB8B,GAAqB,EAEhBvE,GACD0E,EAAazB,EAAoB,KAAK,CAE9C,EACA,MAAO,CACH,SAAU,WACV,IAAK,EACL,MAAO,EACP,MAAO,GAAGuG,CAAkB,KAC5B,OAAQ,OACR,QAAS1J,GAAmB,EAAI,EAChC,WAAY,2BACZ,OAAQ,UACR,OAAQ,IACR,cAAe,MACnB,EAGC,UAAAgD,EAAiB,SACdjH,EAAC,OACG,UAAU,qCACV,QAAU,GAAM,CACZ,EAAE,eAAe,EACjB,EAAE,gBAAgB,EAClBkL,GAAiB,CAAC,CACtB,EACA,MAAO,CACH,SAAU,WACV,IAAKxD,EACC,GACIZ,EAAiB,MACjBG,EAAiB,OAAS,CAC9B,KACA,GAAGA,EAAiB,MAAM,KAChC,MACIA,EAAiB,YACjB,UACM,MACA,IACK0G,EACG7G,EAAiB,OACrB,CACJ,KACV,MAAO,GAAGA,EAAiB,KAAK,KAChC,OAAQY,EACF,eACIZ,EAAiB,MAAQ,EACzBG,EAAiB,OAAS,CAC9B,MACA,eACIA,EAAiB,OAAS,CAC9B,MACN,gBAAiBA,EAAiB,MAClC,aAAc,GAAGA,EAAiB,MAAM,KACxC,OAAQ,SACZ,EACJ,EAIJjH,EAAC,OACG,IAAKyD,GACL,UAAU,0BACV,YAAaiH,GACb,aAAc,IAAMpG,GAAkB,EAAI,EAC1C,aAAc,IAAMA,GAAkB,EAAK,EAC3C,MAAO,CACH,SAAU,WACV,IAAK,IACAoD,EACKH,EACAN,EAAiB,OAAS,EAC1BA,EAAiB,QACvBtC,EACJ,KACA,MACIsC,EAAiB,YAAc,UACzB,MACA,IACK0G,EACGpG,GACJ,CACJ,KACV,MAAO,GAAGA,CAAe,KACzB,OAAQ,GAAG,KAAK,IACZ9C,GACAgD,EACJ,CAAC,KACD,gBACIpD,IAAkBF,EACZ2C,EAAiB,WACjBA,EAAiB,MAC3B,QACIzC,IAAkBF,EACZ2C,EAAiB,aACjBA,EAAiB,QAC3B,aAAc,GAAGA,EAAiB,MAAM,KACxC,OAAQ,UACR,WACI,6DACR,EACJ,GACJ,EAIPjF,IAAiBgD,IAAwB6C,GACtC1H,EAAC,OACG,UAAU,6BACV,QAASqL,GACT,aAAc,IAAMjF,GAAgB,IAAI,EACxC,aAAc,IAAMA,GAAgB,IAAI,EACxC,MAAO,CACH,SAAU,WACV,IAAK,GAAGa,EAAiB,MAAM,KAC/B,MACIA,EAAiB,YAAc,UACzB,MACA,IACK0G,EACGpG,GACJ,CACJ,KACV,MAAO,GAAGA,CAAe,KACzB,OAAQ,GAAGA,CAAe,KAC1B,OAAQ,UACR,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,SAAU,GAAG,KAAK,IACdA,EAAkB,IAClB,CACJ,CAAC,KACD,MACIpB,KAAiB,KACXe,EAAkB,WAClBA,EAAkB,MAC5B,WAAY,OACZ,OAAQ,KACR,QAASjD,GACHkC,KAAiB,KACbe,EAAkB,aAClBA,EAAkB,QACtB,EACN,WACI,mDACR,EACH,kBAED,EAIHrF,IAAiBgD,IAAwB6C,GACtC1H,EAAC,OACG,UAAU,+BACV,QAASsL,GACT,aAAc,IAAMlF,GAAgB,MAAM,EAC1C,aAAc,IAAMA,GAAgB,IAAI,EACxC,MAAO,CACH,SAAU,WACV,OAAQ,GAAGa,EAAiB,MAAM,KAClC,MACIA,EAAiB,YAAc,UACzB,MACA,IACK0G,EACGpG,GACJ,CACJ,KACV,MAAO,GAAGA,CAAe,KACzB,OAAQ,GAAGA,CAAe,KAC1B,OAAQ,UACR,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,SAAU,GAAG,KAAK,IACdA,EAAkB,IAClB,CACJ,CAAC,KACD,MACIpB,KAAiB,OACXe,EAAkB,WAClBA,EAAkB,MAC5B,WAAY,OACZ,OAAQ,KACR,QAASjD,GACHkC,KAAiB,OACbe,EAAkB,aAClBA,EAAkB,QACtB,EACN,WACI,mDACR,EACH,kBAED,EAIHrF,IACGC,IACA2D,KACCwB,EAAiB,WAAa,KAC3BhH,GAAC,OACG,UAAU,qCACV,aAAc,IAAM,CAGZ0B,IAGJ8G,EAAe,EAGf7B,GAAqB,QAAU,WAC3B,IAAM,CACF1C,EAAoB,EAAI,EACxB0C,GAAqB,QAAU,IACnC,EACA,GACJ,EACJ,EACA,aAAc,IAAM,CAEhB8B,GAAqB,EAEhBzD,GACD4D,EAAazB,EAAoB,KAAK,CAE9C,EACA,MAAO,CACH,SAAU,WACV,OAAQ,GAAGb,EAAoB,KAC/B,KAAM,EACN,MAAO,OACP,OAAQ,GAAGoH,CAAkB,KAC7B,QAAS1J,GAAmB,EAAI,EAChC,WAAY,2BACZ,OAAQ,UACR,OAAQ,IACR,cAAe,MACnB,EAGC,UAAAgD,EAAiB,SACdjH,EAAC,OACG,UAAU,gDACV,QAAU,GAAM,CACZ,EAAE,eAAe,EACjB,EAAE,gBAAgB,EAClB+L,GAA2B,CAAC,CAChC,EACA,MAAO,CACH,SAAU,WACV,OACI9E,EAAiB,YACjB,UACM,MACA,IACK0G,EACGpG,GACJ,CACJ,KACV,KAAM,GAAGN,EAAiB,MAAM,KAChC,MAAOpC,GACDoC,EAAiB,YACjB,UACI,GACI0G,EACA1G,EAAiB,MACrB,KACA,GAAG0G,CAAkB,KACzB,GAAG1G,EAAiB,MAAM,KAChC,OAAQ,GAAGM,CAAe,KAC1B,gBAAiBN,EAAiB,MAClC,aAAc,GAAGA,EAAiB,MAAM,KACxC,OAAQ,SACZ,EACJ,EAIJjH,EAAC,OACG,UAAU,qCACV,YAAayL,GACb,aAAc,IACVzG,GAA4B,EAAI,EAEpC,aAAc,IACVA,GAA4B,EAAK,EAErC,MAAO,CACH,SAAU,WACV,OACIiC,EAAiB,YAAc,UACzB,MACA,IACK0G,EACGpG,GACJ,CACJ,KACV,KAAM,GACFN,EAAiB,OAAS1B,EAC9B,KACA,MAAOV,GACDoC,EAAiB,YACjB,UACI,GACI0G,EACA1G,EAAiB,MACrB,KACA,GAAG0G,CAAkB,KACzB,OACN,SAAU9I,GACJoC,EAAiB,YACjB,UACI,eACIA,EAAiB,OACjB1B,GACAoI,EACA1G,EAAiB,MACrB,MACA,eACIA,EAAiB,OACjB1B,GACAoI,CACJ,MACJ,OACN,MAAO,GAAG,KAAK,IAAItI,GAAY,EAAE,CAAC,KAClC,OAAQ,GAAGkC,CAAe,KAC1B,gBACIxC,IACAE,EACM6B,EAAiB,WACjBA,EAAiB,MAC3B,QACI/B,IACAE,EACM6B,EAAiB,aACjBA,EAAiB,QAC3B,aAAc,GAAGA,EAAiB,MAAM,KACxC,OAAQ,UACR,WACI,6DACR,EACJ,GACJ,EAKP7E,GACGjC,EAAC,UACG,KAAK,SACL,aAAW,sBACX,QAAS,IAAM,CACXgD,GAAgB,EAAK,GAGpBiF,EAAsB,GAAK3E,EAAa,UAAU,SAAS,CACxD,IAAK,EACL,SAAU,QACd,CAAC,CACL,EAEA,cAAe,IAAMN,GAAgB,EAAI,EACzC,YAAa,IAAMA,GAAgB,EAAK,EACxC,eAAgB,IAAMA,GAAgB,EAAK,EAC3C,gBAAiB,IAAMA,GAAgB,EAAK,EAC5C,MAAO,CACH,SAAU,WACV,CAACV,CAAW,EAAGC,EACf,OAAQC,GACR,OAAQ,KACR,MAAOG,GACP,OAAQA,GACR,QAAS,EACT,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,OAAQ,OACR,aAAc,MACd,gBAAiBF,GACjB,MAAOC,GACP,UAAW,mCACX,OAAQ,UAER,QAASE,GAAoBG,GAAe,EAAIX,GAAkB,EAClE,cAAeQ,GAAmB,OAAS,OAG3C,WAAYA,GAAmB,qBAAuB,oBAC1D,EAEA,SAAA3C,GAAC,OACG,MAAO,KAAK,MAAM0C,GAAU,EAAG,EAC/B,OAAQ,KAAK,MAAMA,GAAU,EAAG,EAChC,QAAQ,YACR,KAAK,OACL,OAAO,eACP,YAAY,MACZ,cAAc,QACd,eAAe,QACf,cAAY,OAGZ,UAAA3C,EAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,EACrCA,EAAC,YAAS,OAAO,kBAAkB,GACvC,EACJ,GAER,CAER,CACJ,EAEOoO,GAAQ5N",
6
+ "names": ["useRef", "useEffect", "useState", "useCallback", "useMemo", "forwardRef", "useImperativeHandle", "useLayoutEffect", "useId", "DEFAULT_EXCLUDE_CLASSES", "isTextInputElement", "element", "config", "tagName", "type", "selector", "checkElementAndParents", "allExcludeClasses", "currentElement", "depth", "maxDepth", "cls", "toPixelLength", "value", "trimmed", "match", "parsed", "readShorthandPaddingBottom", "padding", "parts", "withShorthandPaddingBottomRemoved", "top", "right", "bottom", "left", "extractBottomPadding", "style", "hasLonghand", "hasShorthand", "rawBottom", "pixels", "nextStyle", "useEffect", "useRef", "useState", "usePullToRefresh", "containerRef", "config", "enabled", "threshold", "maxDistance", "onRefresh", "pullDistance", "setPullDistance", "isRefreshing", "setIsRefreshing", "startYRef", "startXRef", "trackingRef", "pullingRef", "nativeBounceRef", "pullDistanceRef", "refreshingRef", "container", "isTouchPathAtTop", "target", "el", "handleTouchStart", "e", "handleTouchMove", "dy", "dx", "distance", "handleTouchEnd", "moveAttached", "attachMove", "detachMove", "syncMove", "t", "useEffect", "useState", "useCoarsePointer", "coarse", "setCoarse", "mql", "handleChange", "event", "jsx", "jsxs", "DEFAULT_THUMB_CONFIG", "DEFAULT_TRACK_CONFIG", "DEFAULT_ARROWS_CONFIG", "DEFAULT_DRAG_SCROLL_CONFIG", "DEFAULT_AUTO_HIDE_CONFIG", "DEFAULT_PULL_TO_REFRESH_CONFIG", "OverlayScrollbar", "forwardRef", "className", "style", "containerStyle", "contentStyle", "children", "onScroll", "thumb", "track", "arrows", "dragScroll", "autoHide", "pullToRefresh", "showScrollbarProp", "showHorizontalScrollbarProp", "detectInnerScroll", "scrollTopFab", "ref", "isCoarsePointer", "useCoarsePointer", "showScrollbar", "showHorizontalScrollbar", "instanceClass", "useId", "fabEnabled", "fabConfig", "fabThreshold", "fabIdleOpacity", "fabHideDelay", "fabPosition", "fabOffset", "fabOffsetBottom", "fabBackground", "fabColor", "fabSize", "showScrollTopFab", "setShowScrollTopFab", "useState", "isFabPressed", "setIsFabPressed", "fabHideTimerRef", "useRef", "prevPropsRef", "useEffect", "wrapperRef", "containerRef", "contentRef", "scrollbarRef", "thumbRef", "cachedScrollContainerRef", "pullDistance", "isRefreshing", "pullProgress", "usePullToRefresh", "pullIndicatorColor", "pullIndicatorVisible", "scrollbarVisible", "setScrollbarVisible", "isDragging", "setIsDragging", "isThumbHovered", "setIsThumbHovered", "dragStart", "setDragStart", "thumbHeight", "setThumbHeight", "thumbTop", "setThumbTop", "hasScrollableContent", "setHasScrollableContent", "isHorizontalThumbHovered", "setIsHorizontalThumbHovered", "isDraggingHorizontal", "setIsDraggingHorizontal", "dragStartHorizontal", "setDragStartHorizontal", "thumbWidth", "setThumbWidth", "thumbLeft", "setThumbLeft", "hasHorizontalScrollableContent", "setHasHorizontalScrollableContent", "isDragScrollPendingRef", "isDragScrolling", "setIsDragScrolling", "isDragScrollingRef", "dragScrollStart", "setDragScrollStart", "activeArrow", "setActiveArrow", "hoveredArrow", "setHoveredArrow", "isInitialized", "setIsInitialized", "wrapperPaddingBottom", "setWrapperPaddingBottom", "wheelTimeoutRef", "isWheelScrollingRef", "hideTimeoutRef", "hoverEnterTimeoutRef", "wheelShowTimeoutRef", "finalThumbConfig", "useMemo", "baseColor", "finalTrackConfig", "finalArrowsConfig", "finalDragScrollConfig", "finalAutoHideConfig", "isInitialDelayActive", "setIsInitialDelayActive", "finalThumbWidth", "finalTrackWidth", "thumbMinHeight", "showArrows", "arrowStep", "maintainFocus", "useCallback", "activeElement", "useImperativeHandle", "options", "findScrollableElement", "cached", "childScrollableElements", "child", "element", "parent", "isNestedInAnotherScrollbar", "isScrollable", "clearHideTimer", "clearHoverEnterTimer", "clearWheelShowTimer", "handleWrapperMouseLeave", "setHideTimer", "delay", "updateScrollbar", "scrollableElement", "containerHeight", "contentHeight", "scrollTop", "containerWidth", "contentWidth", "scrollLeft", "hasVerticalOverflow", "hasHorizontalOverflow", "hasAnyOverflow", "wrapperPaddingTopBottom", "paddingBottom", "computedStyle", "paddingTop", "arrowSpace", "availableHeight", "scrollRatio", "calculatedThumbHeight", "scrollableHeight", "thumbScrollableHeight", "calculatedThumbTop", "horizontalScrollableWidth", "scrollRatioHorizontal", "calculatedThumbWidth", "thumbScrollableWidth", "calculatedThumbLeft", "handleThumbMouseDown", "event", "actualScrollContainer", "handleMouseMove", "deltaY", "scrollDelta", "newScrollTop", "handleMouseUp", "handleTrackClick", "rect", "clickY", "handleUpArrowClick", "handleDownArrowClick", "container", "maxScrollTop", "handleHorizontalThumbMouseDown", "handleHorizontalMouseMove", "scrollableWidth", "deltaX", "newScrollLeft", "handleHorizontalMouseUp", "handleHorizontalTrackClick", "clickX", "handleDragScrollStart", "target", "isTextInputElement", "active", "handleDragScrollMove", "handleDragScrollEnd", "wasDragScrolling", "swallow", "handleScroll", "shouldShow", "handleWheel", "elementsToWatch", "handleKeyDown", "key", "scrollHeight", "clientHeight", "lineScrollStep", "newThumbTop", "timer", "useLayoutEffect", "updateScrollbarRef", "resizeFrameRef", "scheduleResizeUpdate", "resizeObserver", "elementsToObserve", "observer", "adjustedTrackWidth", "styleId", "existing", "sel", "normalizedContainerStyle", "normalizedContentStyle", "bottomSpacerHeight", "extractBottomPadding", "content", "OverlayScrollbar_default"]
7
7
  }